Synchronization 2IN60: Real-time Architectures (for automotive systems) Mike Holenderski, m.holenderski@tue.nl Goals for this slide set • Describe different dependencies which may exist between tasks • Describe different methods for synchronizing dependent tasks, and explain how to apply them • Describe the priority inversion and deadlock problems, and how to address them • Explain how to implement periodic tasks using semaphores Mike Holenderski, m.holenderski@tue.nl 2 Outline • Synchronization requirements – Mutual exclusion – Precedence constraints • Mutexes – Problems: priority inversion & deadlock – Solutions: Priority Calling Protocol & Stack Resource Policy • Semaphores – Example: implementing periodic tasks Mike Holenderski, m.holenderski@tue.nl 3 Synchronization problem • Mutual exclusion: – A resource can be used by at most one task or ISR at a time • Precedence constraints: – Tasks or ISRs must execute in a particular order • Problem: Co-ordinate execution of a given concurrent program such that no erroneous interleavings are possible Mike Holenderski, m.holenderski@tue.nl 4 Example: mutual exclusion (disabling interrupts) • Implement atomicity by disabling interrupts • Problem: busy waiting inside of conversion with non-preemptive execution w.r.t. tasks and ISRs int ATDReadChannel(...) { ... OS_ENTER_CRITICAL(); “do the conversion”; OS_EXIT_CRITICAL(); ... } void Task1(void) { ... ATDReadChannel(PAD13); ... } void Task2(void) { ... ATDReadChannel(PAD14); ... } – Higher priority tasks not sharing resources are penalized – May lead to missed interrupts Mike Holenderski, m.holenderski@tue.nl 5 Example: mutual exclusion (disabling scheduler) • Implement atomicity by disabling scheduler • Problem: busy waiting inside of conversion with non-preemptive execution w.r.t. tasks int ATDReadChannel(...) { ... OSSchedLock(); “do the conversion”; OSSchedUnlock(); ... } void Task1(void) { ... ATDReadChannel(PAD13); ... } void Task2(void) { ... ATDReadChannel(PAD14); ... } – Higher priority tasks not sharing resources are penalized Mike Holenderski, m.holenderski@tue.nl 6 Mutual exclusion: spot the bug • Implement atomicity by checking a global flag • Problem: if Task1 has higher priority than Task2 and preempts Task2 after Task2 has set avail to false, then Task1 will busy-wait forever, preventing Task2 to set avail to true • Solution: suspend Task1 when avail is false, allowing Task2 to set it to true Mike Holenderski, m.holenderski@tue.nl int avail = true; int ATDReadChannel(...) { ... while (!avail) {} avail = false; “do the conversion”; avail = true; ... } void Task1(void) { ... ATDReadChannel(PAD13); ... } void Task2(void) { ... ATDReadChannel(PAD14); ... } 7 Precedence constraints: spot the bug • Task2 senses the light. Every time that the value is larger than some threshold, Task1 toggles a led • Problem: if Task1 has a higher priority than Task2, Task1 will busy-wait and Task2 will never run • Solution: have Task1 suspend until x is true, or swap the task priorities Mike Holenderski, m.holenderski@tue.nl 8 int x = false; int light; void Task1(void) while (!x) {} x = false; if (light > Threshold) { ToggleLed(LED_D24); } } void Task2(void) light = ATDReadChannel(PAD14); x = true; } Synchronization primitives • Primitives for synchronizing tasks and ISRs: check for a condition and wait until the condition is satisfied – Examples of conditions: • a shared resource is available • a signal from ISR has arrived • another task has arrived Mike Holenderski, m.holenderski@tue.nl 9 Synchronization primitives • Two options for waiting: suspend or busy-wait – Suspend: execute the check and suspension atomically – Busy waiting is only reasonable if • preemption is allowed and we are waiting for a higher priority task or ISR, or • it involves another piece of hardware (e.g. multi-core, sensors). Mike Holenderski, m.holenderski@tue.nl 10 Synchronization primitives • Blocking due to mutual exclusion: Disable interrupts Disable scheduler – Mutexes – Semaphores • Blocking due to precedence constraints: – Semaphores Mike Holenderski, m.holenderski@tue.nl 11 Outline • Synchronization requirements – Mutual exclusion – Precedence constraints • Mutexes – Problems: priority inversion & deadlock – Solutions: Priority Calling Protocol & Stack Resource Policy • Semaphores – Example: implementing periodic tasks Mike Holenderski, m.holenderski@tue.nl 12 Mutex • Mutex guards a shared resource, allowing only one task to access it at a time • Tasks can acquire and release a mutex void Task(void) { . . . (* acquire mutex *); (* execute critical section *); (* release mutex *); . . . } • While a task is holding a mutex, no other task is able to acquire it Mike Holenderski, m.holenderski@tue.nl 13 Dining philosophers problem Mike Holenderski, m.holenderski@tue.nl 14 Deadlock problem • Deadlock can occur when resources are acquired in nested fashion and there is a cyclic dependency Mike Holenderski, m.holenderski@tue.nl OS_EVENT* mutex1; OS_EVENT* mutex2; void Task1(void* pArg) { ... OSMutexPend(mutex1, 0, &err); OSMutexPend(mutex2, 0, &err); (* a critical section *) OSMutexPost(mutex2); OSMutexPost(mutex1); ... } void Task2(void* pArg) { ... OSMutexPend(mutex2, 0, &err); OSMutexPend(mutex1, 0, &err); (* a critical section *) OSMutexPost(mutex1); OSMutexPost(mutex2); ... } 15 Example: deadlock • Consider a system comprised of tasks 1 and 2 – 1 and 2 both use resources r1 and r2 – 1 first locks r1 and subsequently r2 – 2 first locks r2 and subsequently r1 Task1 Task2 • Problem: deadlock may occur Task1 Task2 Mike Holenderski, m.holenderski@tue.nl 16 Avoiding deadlock • Avoid nested critical sections – in principle, no Pend operations between Pend(m)…Post(m) • Acquire all required resources at once (atomically) – Let Pend({a, b, c, …}) be an indivisible operation when there is a danger of deadlock, acquiring resources a, b, c, … atomically • Use a fixed order of Pend() operations – Pend(m);Pend(n); .... in one process may deadlock with Pend(n);Pend(m);... in another process – Solution: acquire resources in a fixed order Mike Holenderski, m.holenderski@tue.nl 17 Dining philosophers problem Forks should be picked up in the order: 1, 2, 3 1 2 3 Mike Holenderski, m.holenderski@tue.nl 18 Avoiding deadlock • Avoid nested critical sections – in principle, no Pend operations between Pend(m)…Post(m) • Acquire all required resources at once (atomically) – Let Pend({a, b, c, …}) be an indivisible operation when there is a danger of deadlock, acquiring resources a, b, c, … atomically • Use a fixed order of Pend() operations – Pend(m);Pend(n); .... in one process may deadlock with Pend(n);Pend(m);... in another process – Solution: acquire resources in a fixed order • Use an appropriate Resource Access Protocol – E.g. Stack Resource Policy (SRP) • In general: avoid cyclic dependencies! Mike Holenderski, m.holenderski@tue.nl 19 Priority inversion • A low priority job obtains a resource; a high priority job waits on its release • A middle priority job pre-empts the low priority job during resource access – the high priority job now waits on the middle priority job ... and effectively has the low priority while waiting L(r) U(r) task h task h priority inversion task l L(r) U(r) L(r) U(r) Mike Holenderski, m.holenderski@tue.nl task m task l “unbounded” priority inversion L(r) U(r) Priority inversion • A pair of alternating middle priority jobs can block the high priority job “indefinitely” – “unbounded” priority inversion: • example: Mars rover; see article. • Resource access protocol: – Guarantee mutual exclusion (e.g. mutexes) – Resolve other problems, such as priority inversion – At least bound the inversion time – Adhere to the task priorities as closely as possible Mike Holenderski, m.holenderski@tue.nl 21 Resource access protocols: Priority Calling Protocol • Protocol – Each mutex has a priority, which is used when accessing the mutex: • When the mutex is already acquired and a higher priority task attempts to acquire the mutex, then the priority of the task owning the mutex is raised to the mutex priority • Mutex priority must be higher than any of the tasks competing for the mutex Mike Holenderski, m.holenderski@tue.nl 22 Resource access protocols: Priority Calling Protocol • Properties – Avoids “unbounded” priority inversion – Suffers from deadlock! • Used in μC/OS-II for implementing mutexes Mike Holenderski, m.holenderski@tue.nl 23 Priority Calling Protocol Task1 Task2 Task1 Task2 Mutex priority Task1 priority Task2 priority Mike Holenderski, m.holenderski@tue.nl 24 Priority Calling Protocol Task1 Task2 Task1 Task2 Mutex priority Task1 priority Task2 priority Mike Holenderski, m.holenderski@tue.nl 25 Resource access protocols: Stack Resource Policy (SRP) • Protocol – Assign to each mutex a ceiling, equal (or higher) to the maximum priority of any task that can acquire it – Scheduler keeps track of the system ceiling, equal to the maximum ceiling of any mutex currently acquired by any task – A task can only start executing if • it has the highest priority among ready tasks, and • its priority is higher than the current system ceiling Mike Holenderski, m.holenderski@tue.nl 26 Resource access protocols: Stack Resource Policy (SRP) • Properties – Avoids deadlock – Priority inversion limited to at most one critical section of a lower priority task – Simple implementation: • Can reuse the ready queue (used by the scheduler) for storing the tasks blocked on a mutex Mike Holenderski, m.holenderski@tue.nl 27 Stack Resource Policy Task1 Task2 Task1 Task2 1 System ceiling 2 ⊥ Mike Holenderski, m.holenderski@tue.nl 28 Stack Resource Policy Task1 Task2 Task3 Task1 Task2 is not blocked while Task3 accesses resource :-) Task2 Task3 1 System ceiling 2 3 ⊥ Mike Holenderski, m.holenderski@tue.nl 29 Mutexes in μC/OS-II • Mutexes in μC/OS-II implement the Priority Calling Protocol • Mutexes cannot be used by ISRs • Mutex is created using OS_EVENT* m = OSMutexCreate(prio, &err); – Mutex is assigned a priority prio • prio must be set to a priority higher than any task accessing mutex m Mike Holenderski, m.holenderski@tue.nl 30 Mutexes in μC/OS-II • Tasks can acquire mutex m using OSMutexPend(m, timeout, &err); – If the m is available it is immediately acquired – If the m is already acquired, the task is suspended until another task releases m • Tasks with a higher priority than prio will not be blocked by the lower priority tasks while they are using m – Since prio is higher than the priority of any task using m, at most one task can be suspended on m – If timeout > 0 and m does not become available within that time, an error is returned indicating that m could not be granted. If timeout is 0, then OSMutexPend() will wait indefinitely until m is available. – &err is a pointer to an integer where the error code is returned Mike Holenderski, m.holenderski@tue.nl 31 Mutexes in μC/OS-II • Tasks can release mutex m using OSMutexPost(m); – If there is any other task waiting for m, that task is made ready and the scheduler is invoked Mike Holenderski, m.holenderski@tue.nl 32 Disabling the scheduler vs. mutexes in μC/OS-II Using mutexes Disabling the scheduler OS_EVENT* mutex; void Task(void) { ... OSSchedLock(); (* a critical section *) OsSchedUnlock(); ... } void Task(void) { INT8U err; ... OSMutexPend(mutex, 0, &err); (* a critical section *) OSMutexPost(mutex); ... } void main(void) { ... } void main(void) { INT8U err; ... mutex = OSMutexCreate(prio, &err); ... } Mike Holenderski, m.holenderski@tue.nl 33 Evaluation: disabling interrupts • Pros: – Avoids deadlock and “unbounded” priority inversion – Simple implementation (low memory processor overhead) – Simple timing analysis • Cons: – May lead to unnecessarily long blocking times • In particular, for higher priority tasks that do not need the shared resource. – May lead to missed interrupts Mike Holenderski, m.holenderski@tue.nl 34 Evaluation: disabling the scheduler • Pros: – Simple implementation (low memory processor overhead) – Simple timing analysis • Cons: – May lead to unnecessarily long blocking times • In particular, for higher priority tasks that do not need the shared resource. – If ISRs use shared resources, then atomicity is not guaranteed Mike Holenderski, m.holenderski@tue.nl 35 Evaluation: mutexes • Pros: – Allow handling of interrupts • Reduce processor blocking to just the administration of the mutual exclusion primitives – Higher priority tasks not sharing the resource guarded by the critical section are not affected • Cons: – Not allowed in ISRs – Elaborate implementation • Need to maintain the task suspended on a mutex (μC/OS-II) – Introduces possibility for deadlock • Can be prevented with the right resource access protocol Mike Holenderski, m.holenderski@tue.nl 36 Outline • Synchronization requirements – Mutual exclusion – Precedence constraints • Mutexes – Problems: priority inversion & deadlock – Solutions: Priority Calling Protocol & Stack Resource Policy • Semaphores – Example: implementing periodic tasks Mike Holenderski, m.holenderski@tue.nl 37 Semaphores • Conceived by Edsger W. Dijkstra (TU/e, 1965) • Semaphore s is an integer with initial value s 0 and atomic operations P(s) and V(s). – Task executing P(s) is said to “try to acquire semaphore s” – P(s) stands for “prolaag”, i.e. portmanteau for “probeer te verlagen” – Task executing V(s) is said to “release semaphore s” – V(s) stands for “verhoog” Mike Holenderski, m.holenderski@tue.nl 38 Semaphores • The effect of these operations is defined as follows: – P(s): < await(s>0); s = s-1 > – V(s): < s = s+1 > • (here we used “< >” to denote atomicity) • await: a statement to indicate suspension until a condition is met • The s>0 check and s = s-1 must be executed atomically • A process that executes P(s) is suspended until s>0 • A process executing V(s) possibly releases suspended processes Mike Holenderski, m.holenderski@tue.nl 39 Semaphores • General assumptions: – atomic execution of P and V • If several processes simultaneously invoke a P or V operation on the same semaphore, the operations will occur sequentially in an arbitrary order. – arbitrary selection of process to proceed: • If more than one process is waiting inside a P operation on the same semaphore and the semaphore becomes positive (because of the execution of a V), one of the waiting processes is selected arbitrarily to complete the P operation. Mike Holenderski, m.holenderski@tue.nl 40 Semaphores • Specific assumptions for Real-Time Systems: – specific selection of process to proceed: • … the process with the highest priority among the waiting processes is selected to complete the P operation. Mike Holenderski, m.holenderski@tue.nl 41 Example: synchronizing tasks using semaphores • Mutual exclusion: – Assume a resource guarded by semaphore s – Each task sharing resource guarded by s contains: P(s) /* use resource */ V(s) – Initialization: s = 1 • Precedence (enforced limitations on order, where the triggered task waits for a triggering task): – – – – Assume synchronization based on s Triggering task: V(s) Triggered task: P(s) Initialization: s = 0 Mike Holenderski, m.holenderski@tue.nl 42 Semaphores in μC/OS-II • Semaphores can be used by tasks and ISRs – With the exception of OSSemPend()(task only) • Semaphore is created using OS_EVENT* s = OSSemCreate(value); – Semaphore is not assigned a priority – value is the initial value of s Mike Holenderski, m.holenderski@tue.nl 43 Semaphores in μC/OS-II • Tasks can acquire one unit of semaphore s using OSSemPend(s, timeout, &err); – If the value of s > 0, the semaphore is decremented and task continues – If the value of s = 0, the task is suspended – Several tasks may be blocked on the same semaphore • They will be released according to their priority (highest first) – If timeout > 0 and s was not granted before that, an error is returned – &err is a pointer to an integer where the error code is returned – Note: this is the P(s) operation Mike Holenderski, m.holenderski@tue.nl 44 Semaphores in μC/OS-II • Tasks can release semaphore s using OSSemPost(s); – If there is any other task waiting for s, that task is made ready and the scheduler is invoked – Note: this is the V(s) operation Mike Holenderski, m.holenderski@tue.nl 45 Example: implementing periodic tasks • Periodic task τi is specified by: – priority i, phasing φi , period Ti • Standard μC/OS-II does not support periodic tasks – These are added by the RELTEQ extension Mike Holenderski, m.holenderski@tue.nl 46 Periodic task implementation • Two options: – Create a new task context upon each job arrival ✓ – Create a task context once, and let jobs be the iterations of a while loop. Mike Holenderski, m.holenderski@tue.nl 47 void Task(void (*f)(void)) { ... while (true) { OSTaskWaitPeriod(); f(); } } void OSTimeTick(void) { ... if (* Task_i period expired *) { (* signal Task_i *) } ... } void main(void) { ... OSTaskCreatePeriodic(fi, Ti, φi, s, i); ... } Periodic task implementation • Synchronize loop iterations using OSTimeDly() • Suffers from large jitter – Due to interference between OSTimeGet() and OSTimeDly() Mike Holenderski, m.holenderski@tue.nl 48 void Task(void (*f)(void)) { int k = 0; ... while (true) { now OSTaskWaitPeriod(); = OSTimeGet(); f(); OSTimeDly(φi + k∗Ti - now); } f(); } k = k + 1; void } OSTimeTick(void) { } ... void if (* OSTimeTick(void) Task_i period expired { *) { ... (* signal Task_i *) if (* Task_i period expired *) { } ... (* signal Task_i *) } } void ...main(void) { } ... void OSTaskCreatePeriodic(f main(void) { i, Ti, φ... i, s, i); OSTaskCreatePeriodic(fi, Ti, ... } φi, s, i); ... } Periodic task implementation OS_EVENT* Task1Sem; void Task(void (*f)(void)) { ... INT8U err; while (true) { OSTaskWaitPeriod(); OSSemPend(Task1Sem, 0, &err); f(); } } void OSTimeTick(void) { ... if (* Task_i periodexpired expired*) *){{ Task1 period (* signal Task_i *) OSSemPost(Task1Sem); } ... } void main(void) { ... OSTaskCreatePeriodic(f Task1Sem = OSSemCreate(0); i, Ti, φ ... i, s, i); } ... } • Synchronize loop iterations using semaphores Mike Holenderski, m.holenderski@tue.nl 49 Summary of mutual exclusion primitives Primitive Pros Cons Disable interrupts • Avoid deadlock • Simple implementation • Prevent interference from interrupts • Higher priority tasks not sharing resources are penalized • Interrupts can be missed Disable scheduler • Avoid deadlock • Simple implementation • Allow interrupts • Higher priority tasks not sharing resources are penalized • Cannot guard resources shared with ISRs Mutex • Allow interrupts • Higher priority tasks not sharing resources are not penalized • Avoid “unbounded” priority inversion • Can lead to deadlock (depends on implementation) • Cannot guard resources shared with ISRs • Suspension not allowed in ISRs Semaphore • Allow interrupts • Higher priority tasks not sharing resources are not penalized Mike Holenderski, m.holenderski@tue.nl 50 • • • • Can lead to deadlock Cannot guard resources shared with ISRs Suspension not allowed in ISRs “Unbounded” priority inversion References • Recommended reading: – [Burns] Ch. 5.1-5.4, 11.8, 11.9 – Mike Jones, “What really happened on Mars?”, 1997 • http://research.microsoft.com/~mbj/mars_pathfinder/ mars_pathfinder.html – T. Baker, “Stack-based scheduling for realtime processes”, Journal of Real-Time Systems, vol 3, 1991 Mike Holenderski, m.holenderski@tue.nl 51