user149408
user149408

Reputation: 5901

Portable way to make a thread sleep for a certain time or until woken up

In my project I spawn a worker thread which deals with an information source. It needs to poll a source (to which it is subscribed) and, whenever needed, change the subscription (i.e. telling the source that the filter criteria for the information it needs have changed).

Polling the source happens at regular intervals, in the minutes range. Changes to the subscription need to be triggered immediately when the main thread identifies the condition for doing so. However, since the operation can be lengthy, it needs to run on the worker thread.

So the worker thread looks like this (in pseudo code—real code is in C):

while(true) {
    acquireWriteLock();
    if (subscriptionChangeNeeded) {
        changeSubscription();
        subscriptionChangeNeeded = false;
    }
    releaseWriteLock();
    pollSource();
    sleep(pollInterval);
}

Now, if the main thread sets subscriptionChangeNeeded right after the worker thread has completed one run of the loop and gone to sleep, the subscription change is going to be delayed by almost the duration of pollInterval.

Therefore, I need a way to wake the thread prematurely from its sleep—or, rather than tell it “sleep for X”, “sleep until I wake you up, but no longer than X”.

I do not need to explicitly tell the thread why it has come out of sleep—it can infer that from subscriptionChangeNeeded. Polling prematurely after changing the subscription is a desirable side effect.

Setting subscriptionChangeNeeded happens at a single location, hence I can easily incorporate a “wake up the worker thread” operation there.

Challenge: the code needs to run on Unix-like OSes as well as Windows. It solves that by implementing a thread abstraction wrapper around the respective APIs.

I am aware both platforms (pthread and Windows) support condition variables, but implementations vary somewhat. Among others, Windows protects the critical section with a critical section object, whereas pthread uses a mutex. Also, older Windows platforms (up to XP/2003) do not support condition variables at all. Finally, condition variables are more powerful than what I need—all I really need is a way to send a thread to sleep and allow it to be woken up by another thread.

What other constructs do the two platforms offer to acomplish that?

Upvotes: 1

Views: 1490

Answers (2)

user149408
user149408

Reputation: 5901

After some research, it seems events on WinAPI are closest to what I am trying to accomplish, and they have been around since at least Windows XP (I believe even earlier versions had it). On POSIX, the closest match really are conditional variables, though the mutex is not really necessary.

Since the project implements its own thread abstraction wrapper, this gives us some extra freedom.

We’ll introduce a data type event. This is an opaque data structure. On Windows, this corresponds to an event (created as an auto-reset event); on POSIX, this is a struct holding a condition and a mutex.

When a thread goes to sleep, the POSIX implementation locks the mutex, calls pthread_cond_timedwait and unlocks the mutex as soon as it continues. The Windows implementation just calls WaitForSingleObject.

To wake up the sleeping thread, the POSIX implementation locks the mutex, calls pthread_cond_signal and unlocks the mutex again. The Windows implementation calls SetEvent.

On Windows, events can be auto-reset or not. An auto-reset event will be reset as soon as the first waiting thread is woken up (so it will never wake up more than one thread), but will remain set if that thread happens to be running at the time the event is signaled. Other events will remain in signaled state until explicitly reset, thus they can wake up any number of threads or even prevent them from going to sleep in the first place.

On POSIX, a condition can be signaled (waking up one waiting thread) or broadcast (waking up all waiting threads). As I understand it, it is reset after it fires, even if no thread got woken up.

Thus, as long as we never have more than one thread waiting on the same event, auto-reset events on Windows behave much like pthread_cond_signal on POSIX. The only difference is that we need to reset the event after handling it. Our thread abstraction wrapper needs to provide a function for that, which calls ResetEvent on Windows and is a no-op on POSIX.

Unlike conditions, this construct does not provide a mutex to provide synchronized access. Therefore, if the waiting and the signaling thread access a shared data structure, they must implement their own mechanism to synchronize access, e.g. using a mutex or lock.

Upvotes: 1

jordanvrtanoski
jordanvrtanoski

Reputation: 5557

You can use pthread_cond_wait() to wait for a signal from the main thread that the condition was met and the work can resume (ex. wait for the subscriptionChangeNeeded to change). This will remove the need to use sleep, however once the thread is suspended on this call, the only way to resume it is to call the pthread_cond_signal().

If your worker thread's loop has other tasks to do and you depend on periodical wake-ups, you can use the pthread_cond_timedwait() to wait for the subscriptionChangeNeeded to change; or for a timeout to be reached; whatever happens first.

The main thread will have a task to identify the change in the condition and once it has identified that there is need for the update, main thread will call pthread_cond_signal() to inform the writer thread that he needs to wakeup. If the signal is not received in the time specified as a timeout, the thread will resume (wake up), even if the condition was met, so this is similar to sleep

Regardless on which one occurs first (the time-out or the condition change), the thread will be resumed and he can perform the changeSubscription();.

You can check more details and examples here

This are POSIX functions defined in pthread.h

Upvotes: 0

Related Questions