|
|
|
@ -26,7 +26,7 @@ Hard to capture faults.
|
|
|
|
|
|
|
|
|
|
### Traditional error handeling
|
|
|
|
|
|
|
|
|
|
{% highlight java %}
|
|
|
|
|
{% highlight c %}
|
|
|
|
|
FILE *
|
|
|
|
|
openConfigFile(){
|
|
|
|
|
FILE * f = fopen("/path/to/config.conf");
|
|
|
|
@ -248,7 +248,7 @@ Controlling a pump filling a tank.
|
|
|
|
|
|
|
|
|
|
#### A trivial solution: "Cyclic Exectutive"
|
|
|
|
|
|
|
|
|
|
{% highlight java %}
|
|
|
|
|
{% highlight c %}
|
|
|
|
|
oldTime = now();
|
|
|
|
|
i = 0;
|
|
|
|
|
while(true) {
|
|
|
|
@ -282,7 +282,7 @@ while(true) {
|
|
|
|
|
* Scheduler often inculuded in OSes
|
|
|
|
|
* Introducing priorities
|
|
|
|
|
|
|
|
|
|
{% highlight java %}
|
|
|
|
|
{% highlight c %}
|
|
|
|
|
/**
|
|
|
|
|
* scheduler_registerThread(function, time, priority)
|
|
|
|
|
* Higher priority numer means higher priority in scheduler
|
|
|
|
@ -331,11 +331,11 @@ main() {
|
|
|
|
|
* Make a handler for a timer interrupt
|
|
|
|
|
* Store all registers (including IP & SP) in a "thread object"
|
|
|
|
|
* Organize queue of processes (Round Robin e.g. - a collection of thread objects?)
|
|
|
|
|
* Can synchronize by: `while(!ready);` (busy wating, "spin locks")
|
|
|
|
|
* Can synchronize by: while(!ready); (busy wating, "spin locks")
|
|
|
|
|
|
|
|
|
|
**Bad solution**
|
|
|
|
|
|
|
|
|
|
{% highlight java%}
|
|
|
|
|
{% highlight c%}
|
|
|
|
|
while(lock==1) {}
|
|
|
|
|
lock = 1;
|
|
|
|
|
// We may run
|
|
|
|
@ -344,7 +344,7 @@ lock = 0;
|
|
|
|
|
|
|
|
|
|
**Better solution**
|
|
|
|
|
|
|
|
|
|
{% highlight java%}
|
|
|
|
|
{% highlight c%}
|
|
|
|
|
void t1() {
|
|
|
|
|
flag1 = 1; // Declare my intention
|
|
|
|
|
turn = 2; // But try to be polite
|
|
|
|
@ -371,170 +371,6 @@ void t1() {
|
|
|
|
|
**Let us introduce another queue; the collection of threads not running, waiting for something**
|
|
|
|
|
|
|
|
|
|
* Fixes the bad performance of spin locks. Is conceptually better.
|
|
|
|
|
* `suspend` moves a thread object from "run" queue to "blocked" queue
|
|
|
|
|
* `resume` moves it back.
|
|
|
|
|
|
|
|
|
|
##### Two bad solutions
|
|
|
|
|
|
|
|
|
|
{% highlight java%}
|
|
|
|
|
t1(){
|
|
|
|
|
while(busy == 1) suspend();
|
|
|
|
|
busy = 1; // It is free; tak it - No
|
|
|
|
|
// Run
|
|
|
|
|
busy = 0; // Release resource
|
|
|
|
|
|
|
|
|
|
resume t2 // No
|
|
|
|
|
}
|
|
|
|
|
{% endhighlight %}
|
|
|
|
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
|
|
{% highlight java%}
|
|
|
|
|
t1(){
|
|
|
|
|
while(TestNSet(busy, 1) == 1) suspend();
|
|
|
|
|
// We own resource
|
|
|
|
|
// Run
|
|
|
|
|
busy = 0;
|
|
|
|
|
|
|
|
|
|
resume t2 // No
|
|
|
|
|
}
|
|
|
|
|
{% endhighlight %}
|
|
|
|
|
|
|
|
|
|
##### The suspend/resume problem
|
|
|
|
|
|
|
|
|
|
{% highlight java%}
|
|
|
|
|
// Global variables
|
|
|
|
|
bool g_initDone = False;
|
|
|
|
|
|
|
|
|
|
// Threads
|
|
|
|
|
t1(){ t2(){
|
|
|
|
|
/* Do init */ if (g_initDone == False) {
|
|
|
|
|
g_initDone = True; Suspend();
|
|
|
|
|
resume(t2) }
|
|
|
|
|
// Continue executing // Continue exectuting
|
|
|
|
|
} }
|
|
|
|
|
{% endhighlight %}
|
|
|
|
|
|
|
|
|
|
#### Priorities
|
|
|
|
|
|
|
|
|
|
* Threads mey have different *priorities*. (A sortet run-queue, or more of them.)
|
|
|
|
|
* Only if there are no running threads on a higher priority, a thread will run.
|
|
|
|
|
* We are not aiming for some sens of fairness (!). But predictability.
|
|
|
|
|
* And priorities supports schedulability proofs.
|
|
|
|
|
* But we open ourselves up to *starvation*. A thread may not ever get to run, even if it is runnable.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Application-level syncronization
|
|
|
|
|
|
|
|
|
|
**SO, the application programmer needs some syncronozation primitives...**
|
|
|
|
|
|
|
|
|
|
* `sleep()`? - Ok
|
|
|
|
|
* Publish `suspend` and `resume` - No
|
|
|
|
|
* Events (`wait` and `signal`) - Just named versions of suspend & resume semantics.
|
|
|
|
|
* Fixes the need to know aboud "thread objects". But no
|
|
|
|
|
* ...or "Condition variables" - same
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Semaphores
|
|
|
|
|
|
|
|
|
|
**A counting semaphore**
|
|
|
|
|
|
|
|
|
|
* `signal(SEM)` increases the counter (possibly resuming a thread waiting for the semaphore)
|
|
|
|
|
* `wait(SEM)` decrements the counter - will block (be suspended) `if SEM == 0`
|
|
|
|
|
* The semaphores value can not be negative
|
|
|
|
|
* Of course; These calls are protected from interleaving by disabling the timer interrupt
|
|
|
|
|
|
|
|
|
|
**We solve beautifully:**
|
|
|
|
|
* Mutual Exclusion
|
|
|
|
|
* Conditional Synchronization (ref `suspend`/`resume`)
|
|
|
|
|
* Basic resource allocation
|
|
|
|
|
|
|
|
|
|
**Semaphore variations**
|
|
|
|
|
|
|
|
|
|
* `wait` and `signal` nay take parameter value to add or subtract
|
|
|
|
|
* `getValue(SEM)` returning the value of the semaphore. (Fishy)
|
|
|
|
|
* BInary semaphores (`signal` will fail `if SEM == 1`)
|
|
|
|
|
* Who is woken at `signal` (FIFO, Arbitrary, Highest priority)
|
|
|
|
|
* The mutex
|
|
|
|
|
* binary
|
|
|
|
|
* ownership
|
|
|
|
|
* allows mulitple waits by owner
|
|
|
|
|
* regions (may be released by Javas `wait` or POSIX condition variables)
|
|
|
|
|
* RTFM
|
|
|
|
|
|
|
|
|
|
**Semaphore challenges**
|
|
|
|
|
|
|
|
|
|
* Breaks modules (both ways)
|
|
|
|
|
* Does not scale!
|
|
|
|
|
* Deadlocks
|
|
|
|
|
* Global analysis --> Does not scale
|
|
|
|
|
* Can not release "temporarily
|
|
|
|
|
* "Limited expressive power". Some reasonalbe problems are hard to solve
|
|
|
|
|
* Ref ["The Little Book of Semaphores"](https://greenteapress.com/semaphores/LittleBookOfSemaphores.pdf)
|
|
|
|
|
|
|
|
|
|
### Why shared-variable synchronization
|
|
|
|
|
|
|
|
|
|
**Why not?**
|
|
|
|
|
|
|
|
|
|
* "Shared variables" is bad code quality
|
|
|
|
|
* Ref global variables, and data members in module interfaces
|
|
|
|
|
* An obvious bottleneck? Scales terribly
|
|
|
|
|
* "Variables" are passive objects
|
|
|
|
|
* They can not protect themselves
|
|
|
|
|
* Why use synchronization when it is communication we need?
|
|
|
|
|
* Technology transfers badly to distibuted systems
|
|
|
|
|
* ... and this is before we start discussing how hard it is
|
|
|
|
|
|
|
|
|
|
**Why?**
|
|
|
|
|
|
|
|
|
|
* Part of the "real-time" design pattern
|
|
|
|
|
* "One thread per timing demand"
|
|
|
|
|
* We do have scheduling proofs and best practises
|
|
|
|
|
* Timing analysis is global anyway
|
|
|
|
|
* Scalability and deadlock analysis may not be the limiting constraint
|
|
|
|
|
* HW is shared memory architecture
|
|
|
|
|
* Infrastucture is avalible
|
|
|
|
|
* Communication systems requires infrastucture that we may not have
|
|
|
|
|
|
|
|
|
|
#### *All* resources are shared!
|
|
|
|
|
|
|
|
|
|
* Memory, certainly
|
|
|
|
|
* "Hidden" memory used by libraries (.. your own modules and the kernel)
|
|
|
|
|
* If the library takes care of this itself, it is called *"reentrant"*
|
|
|
|
|
* Sensors and actuators
|
|
|
|
|
* "CPU" - Computing capacity
|
|
|
|
|
* *This* is real-time programming; We solve it by *Scheduling*
|
|
|
|
|
* ... any other interface
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Some standard problems/pit-falls
|
|
|
|
|
|
|
|
|
|
* **Race condition**: A bug that surfaces by unfortunate timing or order of events
|
|
|
|
|
* **Deadlock:** system in circular wait
|
|
|
|
|
* Special case of livelock
|
|
|
|
|
* Does not use CPU
|
|
|
|
|
* **Livelock:** system locked in a subset of states
|
|
|
|
|
* like deadlock, but we use CPU
|
|
|
|
|
* Busy-Waiting is a livelock
|
|
|
|
|
* **Starvation:** A thread does "by accident" not get the necessary resources
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Features in syncronization
|
|
|
|
|
|
|
|
|
|
* Critical Section - Code that must not be interupted
|
|
|
|
|
* Mutual Exclusion - More piecesof code that must not interrupt each other
|
|
|
|
|
* Bounded buffer - Buffer with full/empty synchronization
|
|
|
|
|
* Read/Write Locks
|
|
|
|
|
* Readers can interleave eachother
|
|
|
|
|
* Writers have mutual exclusion
|
|
|
|
|
* Condition Syncronization - Blocking on event or status
|
|
|
|
|
* Guards etc.
|
|
|
|
|
* Resource allocation
|
|
|
|
|
* More than mutual exclution!
|
|
|
|
|
* Ref: The lock manager
|
|
|
|
|
* Rendezvouz/barriere - Synchronization point
|
|
|
|
|
* Ref: AA "end boundary"
|
|
|
|
|
* Communication
|
|
|
|
|
* Broadcast
|
|
|
|
|
* ...
|
|
|
|
|
* "Suspend" moves a thread object from "run" queue to "blocked" queue
|
|
|
|
|
* "Resume" moves it back.
|
|
|
|
|
|
|
|
|
|