Multicore and...

Updated: April 19, 2023
Synchronization primitives
Standard synchronization primitives (barriers, mutexes, condvars, semaphores, and all of their derivatives, e.g., sleepon locks) are safe to use on a multicore box. You don't have to do anything special here.
FIFO scheduling
A common single-processor “trick” for coordinated access to a shared memory region is to use FIFO scheduling between two threads running at the same priority. The idea is that one thread will access the region and then call SchedYield() to give up its use of the processor. Then, the second thread runs and accesses the region. When it's done, the second thread too calls SchedYield(), and the first thread runs again. Since there's only one processor, both threads would cooperatively share that processor.

This FIFO trick won't work on an SMP system, because both threads may run simultaneously on different processors. You'll have to use the more “proper” thread synchronization primitives (e.g., a mutex), or use BMP to tie the threads to specific CPUs.

Interrupts
The following method is closely related to the FIFO scheduling trick. On a single-processor system, a thread and an interrupt service routine are mutually exclusive, because the ISR runs at a higher priority than any thread. Therefore, the ISR can preempt the thread, but the thread can never preempt the ISR. So the only “protection” required is for the thread to indicate that during a particular section of code (the critical section) interrupts should be disabled.

Obviously, this scheme breaks down in a multicore system, because again the thread and the ISR could be running on different processors.

The solution in this case is to use the InterruptLock() and InterruptUnlock() calls to ensure that the ISR won't preempt the thread at an unexpected point. But what if the thread preempts the ISR? The solution is the same: use InterruptLock() and InterruptUnlock() in the ISR as well.

Note: We recommend that you always use InterruptLock() and InterruptUnlock(), both in the thread and in the ISR.
Atomic operations
Note that if you wish to perform simple atomic operations, such as adding a value to a memory location, it isn't necessary to turn off interrupts to ensure that the operation won't be preempted. Instead, use the functions provided in the C include file <atomic.h>, which let you perform the following operations with memory locations in an atomic manner:
Function Operation
atomic_add() Add a number
atomic_add_value() Add a number and return the original value of *loc
atomic_clr() Clear bits
atomic_clr_value() Clear bits and return the original value of *loc
atomic_set() Set bits
atomic_set_value() Set bits and return the original value of *loc
atomic_sub() Subtract a number
atomic_sub_value() Subtract a number and return the original value of *loc
atomic_toggle() Toggle (complement) bits
atomic_toggle_value() Toggle (complement) bits and return the original value of *loc
Note: The *_value() functions may be slower on some systems, so don't use them unless you really want the return value.
Adaptive partitionining
You can use adaptive partitioning on a multicore system, but there are some interactions to watch out for. For more information, see Using adaptive partitioning and multicore together in the Adaptive Partitioning Scheduling Details chapter of the Adaptive Partitioning User's Guide.