Hello Menno,
First a question about packet streams:
- Can we expect the packet streams to deliver packets in order to the
other component as a FIFO? Thus: can we expect the packet submitted first, to be delivered first at the other component? Also when bursts of packets are send through the packet stream?
each packet stream has two ring buffers located within the dataspace shared between the packet source and packet sink.
* One ring buffer contains the packet descriptors of the submitted packets (submit queue). It is populated by the packet source and consumed by the packet sink.
* The other ring buffer contains the packet descriptors that were processed by the sink (acknowledgement queue). It is populated by the sink and consumed by the source.
Each of both queues are fifo queues. Hence the order of packets submitted is exactly the same as the order of packets observed at the receiving side.
However, both queues are independent from each other. E.g., a sink may take a whole batch of packets out of the submit queue and process the requests out of order. In this case, the order of packets descriptors the source receives in the acknowledgement queue will differ from the order of submissions.
Other questions are with regards to synchronization between multiple threads within the same component:
- Say we have 2 threads in the same component, where Thread A needs to
sleep until an event occurs in Thread B. How to address this? If A and B were components, signals would be the answer. Would signals also work between threads within the same component or is there some other approach?
Signals can be used between threads of the same component. In some situations, this is the simplest approach.
Alternatively, you can use a 'Genode::Lock' for the synchronization. E.g., one thread may wait for another one releasing a blockade using a 'Lock' that is initialized to be in locked state:
Lock lock(Lock::LOCKED);
The to-be sleeping thread A would block on the lock by calling:
lock.lock();
Because the lock was already locked, the attempt to lock it twice will result in the thread A to block.
The thread B can wake up thread A by releasing the lock:
lock.unlock();
Internally within Genode, we use this approach to synchronize the startup of threads and in other situations. Please grep for "Lock::LOCKED" in the source tree to get many examples for this pattern.
- What if those same 2 threads need to exchange data? When data is
shared between components, shared memory would be the answer. How to approach this with 2 threads within the same component. Setup a shared data space? Do those 2 threads share the same heap which we can use for this exchange?
Within a component, all memory is shared between threads. So there is no need to use a dataspace. You can simply pass a pointer or a reference for a component-local object to multiple threads. All threads can directly access it. However, great care must be taken for the synchronization of such shared state, usually by enforcing mutually exclusive access to the shared state via a lock.
Cheers Norman