Porting windows manual-reset event to Linux?
Asked Answered
P

7

14

Is there any easier solution in porting a windows manual-reset event to pthread, than a pthread conditional-variable + pthread mutex + a flag if event is set or unset?

Psychology answered 7/10, 2008 at 11:55 Comment(0)
M
18

Pthreads are low level constructs. No, there isn't a simpler mechanism; pthread_cond__* is conceptually similar to an auto-reset event. Be careful, pthread_cond_wait may have spurious wakeups, so it should never be used without some sort of external flag regardless of the situation.

Building your own wouldn't be too hard, though.

#include <pthread.h>
#include <stdbool.h>

struct mrevent {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    bool triggered;
};

void mrevent_init(struct mrevent *ev) {
    pthread_mutex_init(&ev->mutex, 0);
    pthread_cond_init(&ev->cond, 0);
    ev->triggered = false;
}

void mrevent_trigger(struct mrevent *ev) {
    pthread_mutex_lock(&ev->mutex);
    ev->triggered = true;
    pthread_cond_signal(&ev->cond);
    pthread_mutex_unlock(&ev->mutex);
}

void mrevent_reset(struct mrevent *ev) {
    pthread_mutex_lock(&ev->mutex);
    ev->triggered = false;
    pthread_mutex_unlock(&ev->mutex);
}

void mrevent_wait(struct mrevent *ev) {
     pthread_mutex_lock(&ev->mutex);
     while (!ev->triggered)
         pthread_cond_wait(&ev->cond, &ev->mutex);
     pthread_mutex_unlock(&ev->mutex);
}

This may not fit your usage, as you will often have a different lock that you'd want to use in place of ev->mutex, but this is the gist of how it's typically used.

Menell answered 7/10, 2008 at 15:15 Comment(4)
Don't forget that the windows auto reset event will "remember" that it's been signalled and inform the next thread that waits and then reset itself. the pthread_cond_signal may actually do nothing if there are no threads waiting so the "event" in this case would appear to not have happened.Mikemikel
@ephemient: "The pthread_cond_wait() and pthread_cond_timedwait() functions are used to block on a condition variable. They are called with mutex locked by the calling thread or undefined behaviour will result." Shouldn't you capture the mutex before every call to pthread_cond_wait? These functions atomically release mutex and cause the calling thread to block on the condition variable cond;Revareval
@user877329: The pthread_cond_* functions drop the mutex while waiting and reacquire it before returning.Menell
@Menell great answer man. Worked like a charm for me. definite +1. I would have accepted the answer if I had asked it. I dont know what is wrong with sfhdhsf as this answers his question perfectly :)Stagnate
L
8

You can easy implement manual-reset events with pipes:

event is in triggered state -> there is something to read from the pipe

SetEvent -> write()

ResetEvent -> read()

WaitForMultipleObjects -> poll() (or select()) for reading

the "SetEvent" operation should write something (e.g. 1 byte of any value) just to put the pipe in non-empty state, so subsequent "Wait" operation, that is, poll() for data available for read will not block.

The "ResetEvent" operation will read up the data written to make sure pipe is empty again. The read-end of the pipe should be made non-blocking so that trying to reset (read from) already reset event (empty pipe) wont block - fcntl(pipe_out, F_SETFL, O_NONBLOCK) Since there may be more than 1 SetEvents before the ResetEvent, you should code it so that it reads as many bytes as there are in the pipe:

char buf[256]; // 256 is arbitrary
while( read(pipe_out, buf, sizeof(buf)) == sizeof(buf));

Note that waiting for the event does not read from the pipe and hence the "event" will remain in triggered state until the reset operation.

Linehan answered 12/4, 2012 at 1:41 Comment(0)
S
5

I prefer the pipe approach, because often one doesn't need just an event to wait for, but multiple objects e.g. WaitForMultipleObjects(...). And with using pipes one could easily replace the windows WaitForMultipleObjects call with poll(...), select, pselect, and epoll.

There was a light-weight method for process synchronization called Futex (Fast Userspace Locking system call). There was a function futex_fd to get one or more file descriptors for futexes. This file descriptor, together with possibly many others representing real files, devices, sockets or the like could get passed to select, poll, or epoll. Unfortunately it was removed from the kernel. So the pipes tricks remain the only facility to do this:

int pipefd[2];
char buf[256]; // 256 is arbitrary
int r = pipe2(pipefd, O_NONBLOCK);

void setEvent()
{
  write(pipefd[1], &buf, 1); 
}

void resetEvent() {  while( read(pipefd[0], &buf, sizeof(buf)) > 0 ) {;} }

void waitForEvent(int timeoutMS)
{ 
   struct pollfd fds[1];
   fds[0].fd = pipefd[0];
   fds[0].events = POLLRDNORM;
   poll(fds, 1, timeoutMS);
}

// finalize:
close(pipefd[0]);
close(pipefd[1]);
Stickweed answered 29/6, 2012 at 11:22 Comment(0)
E
3

No there isn't any easier solution but the following code will do the trick:

void LinuxEvent::wait()
{
    pthread_mutex_lock(&mutex);

    int signalValue = signalCounter;

    while (!signaled && signalValue == signalCounter)
    {
        pthread_cond_wait(&condition, &mutex);
    }

    pthread_mutex_unlock(&mutex);
}

void LinuxEvent::signal()
{
    pthread_mutex_lock(&mutex);

    signaled = true;
    signalCounter++;
    pthread_cond_broadcast(&condition);

    pthread_mutex_unlock(&mutex);
}

void LinuxEvent::reset()
{
    pthread_mutex_lock(&mutex);
    signaled = false;
    pthread_mutex_unlock(&mutex);
}

When calling signal(), the event goes in signaled state and all waiting thread will run. Then the event will stay in signaled state and all the thread calling wait() won't wait. A call to reset() will put the event back to non-signaled state.

The signalCounter is there in case you do a quick signal/reset to wake up waiting threads.

Expunge answered 28/2, 2012 at 21:40 Comment(0)
T
2

I think Windows Events are more akin to a semaphore. I.e. for auto-reset, you'd use a binary semaphore and the sem_timedwait() function.

Tenebrous answered 6/10, 2009 at 7:30 Comment(0)
R
2

We were looking for a similar solution to port some heavily-multithreaded C++ code from Windows to Linux, and ended up writing an open source, MIT-licensed Win32 Events for Linux library. It should be the solution you are looking for, and has been heavily vetted for performance and resource consumption.

It implements manual and auto-reset events, and both the WaitForSingleObject and WaitForMultipleObject functionalities.

Receiver answered 22/1, 2013 at 14:29 Comment(1)
This looks very nice. Its too bad *nix descriptors, semaphores, events, threads, sockets, etc are not waitable in a unified manner like on Windows, where one call rules them all.Cephalic
R
0

We (full disclosure: I work at NeoSmart Technologies) wrote an open source (MIT licensed) library called pevents which implements WIN32 manual and auto-reset events on POSIX, and includes both WaitForSingleObject and WaitForMultipleObjects clones. It's seen some adoption since then (it's used in Steam on Linux/Mac) and works fairly well.

Although I'd personally advise you to use POSIX multithreading and signaling paradigms when coding on POSIX machines, pevents gives you another choice if you need it.

Receiver answered 11/7, 2015 at 18:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.