CSCI 1600 Embedded and Real Time Software Lecture
CSCI 1600: Embedded and Real Time Software Lecture 10: Concurrency Steven Reiss, Fall 2019
Arduino Programming is Simple Single processor, no scheduling Only concurrency is with interrupts Not all embedded programming is that simple More sophisticated systems use a RTOS Tasks that are interruptible (run concurrently) Available on the Arduino as well (Free. RTOS) Including Multiple threads (on multiple cores) Multiple processes Scheduling (time sliced, interrupted) 2 Lecture 19: Concurrent Programming 2/27/2021
Interrupt Programming Interrupts introduce multiprocessing Need to worry about race conditions, synchronization, deadlock They need to be coded very cautiously Shared data can be problematic, esp. data structures Note that even 16/32 bit integers might be multiple values Treat as multithreaded programming Non-blocking synchronization techniques Critical regions Locks = masking interrupts Interaction with real synchronization can be weird (later) 3 Lecture 10: Input Output Concepts 2/27/2021
Arduino Interrupts User provide a routine to be called when an interrupt occurs For either DIO or AIO line (small set are usable) Specific line and condition is settable Interrupts can be masked Also have timer interrupts Convoluted programmings 4 Lecture 10: Input Output Concepts 2/27/2021
Interrupt Programming Do the minimum amount of work possible Small and fast interrupt routine Set shared flags for processing later Read input and save for future processing Protect access to these flags 5 Lecture 10: Input Output Concepts 2/27/2021
Concurrency is an Issue Many things can go wrong Deadlocks & race conditions Memory or cache consistency Unpredictability … Consider increment() { ++counter; } multiple threads call increment What can counter do? Is it accurate? Is it monotonic? 6 Lecture 19: Concurrent Programming 2/27/2021
Synchronization Synchronizing interrupts Disable and enable interrupts Interrupt priorities Is this enough? Declaring variables to be volatile This isn’t enough for synchronizing threads Or interruptible or time-sliced tasks 7 Lecture 19: Concurrent Programming 2/27/2021
Memory Synchronization Main Program disable() x=0 enable() while (x == 0) wait(); Interrupt Handler: x=1 return; … 8 Lecture 19: Concurrent Programming 2/27/2021
Mutex Synchronization Primitive MUTEX Holder has exclusive action until released Defines an atomic region C/C++: Process or system-based mutexes Provided by the operating system Different operating systems provide different primitives 9 Lecture 19: Concurrent Programming 2/27/2021
Condition Variables & Monitors Code region controlled by a mutex Can contain arbitrary code Again process or system wide Wait/notify functionality Wait – releases the lock Notify – wakes up waiters Waiter regains the lock Very general Difficult to reason about, predict 10 Lecture 19: Concurrent Programming 2/27/2021
Semaphores Synchronized counter Can’t go below zero Two operations: P (lock) and V (unlock) Process or system based P operation (lock) If counter > 0, atomically decrement and continue Otherwise wait V operation (unlock) Increment counter If it was 0, wake up any waiters (one waiter fairly) 11 Lecture 19: Concurrent Programming 2/27/2021
Synchronization Primitives Other primitives Read/write locks Hardware: test and set, compare and swap, … All these can be used to implement the others Semaphores seem to be the most common Different (older) implementation on Linux 12 Lecture 19: Concurrent Programming 2/27/2021
Problem Allow one (or several) tasks to send messages to another task Messages might be fixed sized data structures Messages might be variable sized data structures Messages might be arbitrary text What are the issues or problems Representing messages Fix the size of the buffer or allow it to expand (fixed) Who needs to wait (reader/writer/both) Minimizing synchronization (and overhead) Efficient implementation 13 Lecture 19: Concurrent Programming 2/27/2021
Problem 14 Lecture 19: Concurrent Programming 2/27/2021
Messages Fixed size: easy Variable size Include the length Either directly or indirectly Fixed part + variable part Fixed part includes length Alternatives Message as a sequence of chunks Each containing its length and end/continue flag 15 Lecture 19: Concurrent Programming 2/27/2021
General Approach Use a fixed sized circular buffer Start data pointer: first used space in the buffer End data pointer: next free space in the buffer Assume the buffer is in common (shared) memory Is this sufficient? Extend circular buffer read/write routines To handle variable length messages To handle waiting 16 Lecture 19: Concurrent Programming 2/27/2021
Write operation int write(void * data, int len) { if (write_ptr == top_ptr) write_ptr = 0; if (bln > 0) { if (len == 0) return 0; memcpy(&buffer[write_ptr], wait. For. Write(len); data+aln, bln); int aln = top_ptr – write_ptr; write_ptr += bln; if (aln > ln) aln = ln; } memcpy(&buffer[write_ptr], data, aln); write_ptr += aln; return ln; } int bln = ln – aln; 17 Lecture 19: Concurrent Programming 2/27/2021
Wait for Write Operation void wait. For. Write(int ln) { for ( ; ; ) { int aln = read_ptr – write_ptr; if (aln < 0) aln += top_ptr; if (aln > ln) return; // wait here } } 18 Lecture 19: Concurrent Programming 2/27/2021
Read Operation int read(void * vd, int ln) { read_ptr += aln; wait. For. Read(ln); int bln = ln – aln; int aln = write_ptr – read_ptr; if (read_ptr == top_ptr) read_ptr = 0; if (aln > ln) aln = ln; if (bln > 0) { memcpy(vd+aln, else { &buffer[read_ptr], bln); aln = top_ptr – read_ptr; read_ptr += bln; if (aln >= ln) aln = ln; } } memcpy(vd &buffer[read_ptr], aln); return ln; } 19 Lecture 19: Concurrent Programming 2/27/2021
Wait For Read Operation void wait. For. Read(int ln) { for ( ; ; ) { int aln = write_ptr – read_ptr; if (aln < 0) aln += top_ptr; if (aln > ln) return; // wait here } } 20 Lecture 19: Concurrent Programming 2/27/2021
What Synchronization is Needed How many readers and writers? If more than one, then the whole read/write operation has to be synchronized (atomic) If multiple writers, might want to enforce a queue hierarchy To ensure ordered processing If only one of each, then read/write pointers are safe Only need to worry about the wait routines 21 Lecture 19: Concurrent Programming 2/27/2021
Implementing the Wait How would you do this in Java? What are the alternatives? 22 Lecture 19: Concurrent Programming 2/27/2021
Using Semaphores Assume a P/V operation with a count What happens if you just do a P operation k times? Most semaphores have such an operation Write semaphore count starts with size of buffer Then you don’t have to check space in the buffer Read semaphore count starts with 0 Then you don’t have to data available to read 23 Lecture 19: Concurrent Programming 2/27/2021
Busy Wait Assume the reader is always running and generally faster than the writer What happens if this is not the case? Then the writer can busy wait In terms of the code, this means doing nothing for wait The reader can return 0 if nothing is there to process Can you tell how often waits should occur Given probabilistic models of the reader/writer 24 Lecture 19: Concurrent Programming 2/27/2021
Using a Condition Variable This is the Java approach If there is not enough space, wait When freeing space, notify This is wasteful – only want to notify if someone is waiting Notify only if the queue is getting full What does full mean? Coding is a bit tricky, but it should be faster 25 Lecture 19: Concurrent Programming 2/27/2021
Synchronization Problems Race Conditions Deadlock Priority Inversion Performance 26 Lecture 19: Concurrent Programming 2/27/2021
Dining Philosopher’s Problem (Dijkstra ’ 71) 27 Lecture 19: Concurrent Programming 2/27/2021
Priority Inversion Thread τ1 L L L Rx L Thread τ2 L L. . . L Thread τ3 L L L Rx L. . . L T 1: High, T 2: Medium, T 3: Low L: local CPU burst R: resource required (Mutual Exclusion) 28 Lecture 19: Concurrent Programming 2/27/2021
Example Suppose that threads τ1 and τ3 share some data. Access to the data is restricted using semaphore x: Each task executes the following code: do local work (L) sem_wait(s) (P(x)) access shared resource (R) sem_signal(s) (V(x)) do more local work (L) 29 Lecture 19: Concurrent Programming 2/27/2021
Blocking Blocked! τ1 L L L R L τ2 τ3 LLLR 0 30 LLL R t Lecture 19: Concurrent Programming t+3 t+4 t+6 2/27/2021
The Middle Thread Blocked! τ1 L L L τ2 τ3 LLLR 0 31 t Lecture 19: Concurrent Programming t+2 t+3 2/27/2021
Unbounded Priority Inversion Blocked! τ1 L L L τ2 τ3 L L. . . L LLLR 0 32 R R t Lecture 19: Concurrent Programming t+2 t+3 t+254 2/27/2021
Unbounded Priority Inversion Blocked! τ1 τ2 -2 L L L R L τ2 -n τ3 L L L R 0 33 L L L R t Lecture 19: Concurrent Programming t+2 t+3 t+2530 t+2540 2/27/2021
Fixing Priority Inversion Priority inheritance Assume priority of highest waiter on a lock Priority ceiling protocol Priority when locked = highest priority of any thread that can use that lock 34 Lecture 19: Concurrent Programming 2/27/2021
Homework Do exercise 6. 7 using Petri nets Read 2. 4 35 Lecture 13: Modeling Concurrency 2/27/2021
- Slides: 35