Skip to content

Concurrency

Dietmar Planitzer edited this page Dec 19, 2025 · 1 revision

Serena OS offers a very rich set of concurrency mechanisms and tools that make it easy to write code that takes advantage of concurrency.

Threads of Execution

There are virtual processors, dispatch queues and processes. A virtual processor is the lowest level form of a thread if execution. It is very similar to threads in traditional operating systems, however a virtual processor has a much more fluid relationship to its process compared to a thread. A thread is something that a process creates and owns until it is terminated. A virtual processor on the other hand is a thread of execution that is dynamically acquired as needed by a process, used for some time and may be temporarily relinquished when not in active use. In a sense a virtual processor belongs to the kernel and is dynamically assigned and removed from a process as needed. In fact, a virtual processor may move between processes if the kernel deems this to be necessary.

Virtual processors may be organized in groups. Each group is identified by a virtual processor group id and groups can be expanded and reduced in size at any time as needed. All virtual processors in a group work in parallel. Additionally you can send a signal to a group.

Dispatch queues are an abstraction on top of a virtual processor group. Each dispatch queue is backed by its own private virtual processor group. A dispatch queue has a minimum and a maximum concurrency level. The concurrency level of a dispatch group determines how many virtual processor the dispatch group uses to achieve the desired level of concurrency. A dispatch queue with a concurrency level of 1 is known as a "serial dispatch queue" because at most one operation can be active at any given time. A dispatch queue with a concurrency level greater 1 is also known as a "concurrent dispatch queue" because more than one operation may be active at any given time.

Both virtual processors and dispatch queue are "process private" concurrency solutions because they are no visible outside the boundaries of a process. So no other process can directly access or address a virtual processor, virtual processor group or dispatch queue inside your process. There is an indirect mechanism available though: you can set up signal routes to ensure that a signal that is sent to your process, is automatically routed to a single virtual processor or a virtual processor group inside of your process. This routing also works with dispatch queues because a dispatch queue allows you to retrieve its virtual processor group id. You can then use this id to set up a route that routes a process-level signal to the dispatch queue for processing.

A process is a collection of virtual processor groups, an address space and a set of resources. Resources are things like ope files, pipes, terminal connections, etc. All these things are connected to the process by an I/O channel. The address space keeps a record of all memory that a process has allocated and it ensures that the memory will be automatically returned to the system if the process terminates.

Synchronization Primitives

The system provides a number of synchronization primitives that virtual processors can use to implement concurrency safe operations. There are spinlocks mutexes, condition variables and wait queues.

Spinlocks are the lowest level of synchronization primitive. They are so named because they do not use a wait queue to fully suspend the virtual processor if the lock is contended. Instead they simply yield for a short amount of time. This kind of lock is appropriate for situations where very little contention is expected.

Mutexes provide a full and fair locking implementation. The virtual processor is suspended if the lock is in use by some other virtual processor and it is only woken up once the lock becomes available.

Condition variables can be used to implement producer-consumer problems.

Finally wait queues serve as a fundamental building block for synchronization primitives and you can use them in your code to build your own kind of synchronization primitive if none of the provided ones are suitable for your problem.

Clone this wiki locally