libsnooze event reservation #5

Closed
opened 2023-04-06 11:06:05 +01:00 by deavmi · 1 comment
Owner

What is this?

When we call dequeue() that would be the first time we call wait() actually and only then is a pipe pair made.

Therefore the exists a potential race to the first dequeue() call of a freshly made Queue and the enqueue() call that the Watcher would make (which it could do before). In the case whereby the Watcher makes the first notifyAll() call, well there is no pipes to write too and hence our dequeue() would block forever.

Solution

The easiest way to ensure that the the thread which created the Queue and then registered it later has a pipe-thread pair would be for the Queue constructor to call wait(0) such that it blocks the calling thread whilst creating/constructing the Queue but also immediately returns as no bytes are available to be read from the pipe (according to libsnooze's use of select()), this will however create a pipe pair for us via the ensurePipePair() call of libsnooze.

The calling thread

This ensures that atleast in our unit test it will work. The unit test in Watcher shows us the normal usage one would expect from tristanable. The Queue-constructing Thread will be usable in this use case at least.

If you passed the Queue to another thread then it would only register on the first call to dequeue(). This usage if a little weird, you should construct it on the thread doing the dequeuing. Doesn't make sense to have them split, also what doesn't necessarily make sense is multiple threads dequeuing (from the same Queue) - one can do it but its odd, it would be a race to whoever can unlock the mutex first.


Here is the solution in code form:

    /** 
     * Constructs a new Queue and immediately sets up the notification
     * sub-system for the calling thread (the thread constructing this
     * object) which ensures that a call to dequeue will immediately
     * unblock on the first message received under this tag
     *
     * Params:
     *   queueID = the id to use for this queue
     */
    this(ulong queueID)
    {
        /* Initialize the queue lock */
        this.queueLock = new Mutex();

        /* Initialize the event */
        this.event = new Event();

        /* Set the queue id */
        this.queueID = queueID;

        /* Ensure pipe existence (see https://deavmi.assigned.network/git/deavmi/tristanable/issues/5) */
        event.wait(dur!("seconds")(0));
    }

As of commit 2fa77e639ff16ef07db55a5cc70433af3f03ef64.

## What is this? When we call `dequeue()` that would be the first time we call `wait()` actually and only then is a pipe pair made. Therefore the exists a potential race to the first `dequeue()` call of a freshly made `Queue` and the `enqueue()` call that the `Watcher` would make (which it could do before). In the case whereby the `Watcher` makes the first `notifyAll()` call, well there is no pipes to write too and hence our `dequeue()` would block forever. ## Solution ✅ The easiest way to ensure that the the **thread** which created the `Queue` and then registered it later has a pipe-thread pair would be for the `Queue` constructor to call `wait(0)` such that it blocks the calling thread whilst creating/constructing the `Queue` but also immediately returns as no bytes are available to be read from the pipe (according to libsnooze's use of `select()`), this will **however** create a pipe pair for us via the `ensurePipePair()` call of libsnooze. ### The calling thread This ensures that atleast in our unit test it will work. The unit test in `Watcher` shows us the normal usage one would expect from tristanable. The `Queue`-constructing `Thread` will be usable in this use case at least. If you passed the `Queue` to another thread then it would only register on the first call to `dequeue()`. This usage if a little weird, you should construct it on the thread doing the dequeuing. Doesn't make sense to have them split, also what doesn't necessarily make sense is multiple threads dequeuing (from the same `Queue`) - one can do it but its odd, it would be a race to whoever can unlock the mutex first. --- Here is the solution in code form: ```d /** * Constructs a new Queue and immediately sets up the notification * sub-system for the calling thread (the thread constructing this * object) which ensures that a call to dequeue will immediately * unblock on the first message received under this tag * * Params: * queueID = the id to use for this queue */ this(ulong queueID) { /* Initialize the queue lock */ this.queueLock = new Mutex(); /* Initialize the event */ this.event = new Event(); /* Set the queue id */ this.queueID = queueID; /* Ensure pipe existence (see https://deavmi.assigned.network/git/deavmi/tristanable/issues/5) */ event.wait(dur!("seconds")(0)); } ``` As of commit `2fa77e639ff16ef07db55a5cc70433af3f03ef64`.
deavmi added reference nextgen 2023-04-06 11:09:15 +01:00
deavmi added the
bug
todo
labels 2023-04-06 11:09:21 +01:00
deavmi self-assigned this 2023-04-06 11:09:24 +01:00
deavmi added this to the Next-gen milestone 2023-04-06 11:09:26 +01:00
deavmi added the due date 2023-04-09 2023-04-06 11:09:33 +01:00
deavmi added a new dependency 2023-04-06 11:09:44 +01:00
Author
Owner

Fixed in commit 2fa77e639ff16ef07db55a5cc70433af3f03ef64

Fixed in commit `2fa77e639ff16ef07db55a5cc70433af3f03ef64`
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

2023-04-09

Blocks
#3 Next gen
deavmi/tristanable
Reference: deavmi/tristanable#5
No description provided.