Home > Back-end >  Simulate Multithreading with semaphores in a single thread
Simulate Multithreading with semaphores in a single thread

Time:11-12

I want to simulate the firmware (C ) of an embedded device on a Windows machine (C ). The firmware runs on a microcontroller (nRF5340) and runs Zephyr as an operating systes. Within the real firmware there are multiple tasks.

The challenge is now: I want to be able to create multiple instances of one virtual device, but each device should only run in one single thread.

Semaphores are used for multi-threading (in the firmware), which are blocked or released. Is there a way I can use semaphores in a single thread and implement a kind of context switch?

So that, for example, a function is called that runs through to a semaphore. When the semaphore is reached, a new function can be called which releases the previous semaphore so that the original function can continue (but all in one single thread).

CodePudding user response:

This can be done with coroutines. A coroutine is like a function, but at some point in the body the programmer calls yield. This saves the state of the coroutine. It can be resumed later at the point where it yielded.

Coroutines were added to C 20. However, they didn't yet add specification for a task-management library. It would be a significant amount of work to implement yourself. You can find 3rd party ones. Boost also has 3 coroutine libraries that predate C 20 (1 old, 1 current, and 1 specifically for Asio). Using a 3rd party task-management library is probably the superior approach, but you will have to use C 20 and learn a new library that might go obsolete pretty quickly.

Alternatively you could make a work-around if you allowed each virtual device to spawn an actual thread for each of its simulated threads, then programmed some kind of synchronization so only 1 thread per virtual device runs at any given time. You're essentially taking advantage of the fact that a real thread has context switching built in. I would use this approach, as it avoids a lot of development on things tangential to your project, and I already know how to use threading tools.

Lastly, you could create a "stack" for each simulated thread and do the context switch yourself. You'd lose portability. Basically you're writing your own coroutine library, but it's tailored to your use case so it would be easier to use. However, the dev time would probably balance out with just learning how to use an existing library.

Going back to the middle approach (a real thread for each simulated thread)... your yielding function could look something like:

Each simulated thread has its own semaphore (so you can specify which to call):

void switch_to(T& other)
{
    other.up()
    me.down();
}

Or all simulated threads on a virtual device share a semaphore:

void switch()
{
    sem.up();
    sem.down();
}

(This approach only works if the semaphore implements fairness.)

Both ways, you init the semaphore with 0 passes and have each thread call down() on the semaphore as its first action (this inits each thread in a blocking state).

C doesn't have a standard semaphore, but you can implement one with condition variables.

  • Related