0

This question does not pertain to any particular language, so answers of various solutions in operating systems/languages are welcome. It is relevant when a library does not natively integrate into a language's event loop or when the language doesn't have any default concurrency (e.g., C).

Assume we are using a library with the following interface (we have no control over that library's code):

req_launch(...)     // launches a request asynchronously
req_wait()          // blocks thread until any launched request finishes
req_test() -> bool  // tells us whether any request has finished (true) or all are still busy (false)
req_pop() -> resp_t // gets the response data of the request that has finished

We are using another library that, albeit being very similar API-wise to the first library, has an incompatible asynchronous interface to the first library.

msg_send(...)      // sends a message to another thread
msg_wait()         // blocks thread until a message is received
msg_test() -> bool // tells us whether a message is available
msg_pop() -> msg_t // gets the next message

Now, in user code, we want to launch requests, and do something when they finish individually. But in between the request and wait, the thread should be open to messages from other threads such that it, e.g., can launch more requests.

We can use the non-blocking API with (wasteful) busy waits to do this very easily:

THREAD_A:
  every 5s
    msg_send(THREAD_B, MSG_A)

THREAD_B:
loop
  if msg_test()
    switch msg_pop()
      MSG_A -> req_launch(/* some data for A*/)
      MSG_B -> req_launch(/* some data for B*/)

  if req_test()
    resp = req_pop()
    /* do something with `resp` */

If we had control of the code of the two libraries, we could add some sort of external signalling mechanism to have the request interrupt its waiting for another message to be handled:

chan_create() -> chan_t        // create a channel that can be signalled
chan_signal(chan_t)            // signal a channel
chan_signalled(chan_t) -> bool // check whether the channel was signalled
req_wait(chan_t)               // blocks thread until any launched request finishes OR the channel is signalled
THREAD_A:
  every 5s
    msg_send(THREAD_B, MSG_A)
    chan_signal(THREAD_B.chan)

THREAD_B:
chan = create_channel()
num_waiting = 0
loop
  if num_waiting = 0
    msg_wait()

  if msg_test()
    num_waiting = num_waiting + 1
    switch msg_pop()
      MSG_A -> req_launch(/* some data for A*/)
      MSG_B -> req_launch(/* some data for B*/)

  req_wait(chan)
  if not chan_signalled(chan)
    num_waiting = num_waiting - 1
    resp = req_pop()
    /* do something with `resp` */

But since we are not in control of the library code, what other (efficient) ways are there to wake a thread from a blocking sleep without the thread cooperating? Or, are there other alternatives to using the library-provided sleep functions?

2
  • 1
    You'll need something like select etc, to run select([msg_wait, req_wait]). If the library is written for the purpose of building your own event loop by supplying these primitives, they should be open to integrate that functionality as well - file an issue / feature request. Commented Dec 17, 2023 at 16:43
  • 1
    As a workaround, I suppose you could try to have one thread per library doing the sleeping (msg_wait(), req_wait()) and signal the thread that runs your actual event loop with events/channels. Unless those libraries use thread-local storage, of course. Commented Dec 17, 2023 at 16:44

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.