views:

170

answers:

5

Hello I have some code that is cross-platform by unsing #ifdef OS,

I have a Queue protected by a CriticalSection on Windows, and by a pthread_mutex_t on Linux.

I would like to implement a Wait(timeout) call that would block a thread until something has been enqueued. I though about using WaitForSingleObject on windows but it don't seem to support CriticalSection. Which Win32 and which Linux functions should I use to Wait and Signal for a condition to happen.

Thank

+1  A: 

I think that boost's conditions might be what you need. It is crossplatform so you won't have to bother with different implementations depending on OS.

Another alternative is to use Windows Events with WaitForSingleObject() and the quite new linux eventfd() with select() or poll().

Anders Abel
how would this compare to linux's pthread_cond_timedwait()
JP
A: 

Using Boost will allow you to do threading and synchronization for both platforms without a bunch of ifdefs.

Steve Fallows
Boost is not an option for my code, which need to be small
JP
A: 

You can (sort of) simulate a try with a timeout using TryEnterCriticalSection, but for the most part, if you want a timeout you might be better off using a mutex instead (when you get down to it, a critical section is mostly a wrapper around a mutex).

Another possibility would be to use the Win32 pthreads library, which will probably let your Linux code compile under Win32 unchanged (and you'd simply eliminate your own Win32 code).

This is pretty similar to the threading support that's been added to the C++ 0x library, though it doesn't (even try to) follow the new standard precisely. If you want to follow the standard, you could use Anthony Williams' Just Thread library (warning: fairly reasonably priced, but not free in either sense).

Edit (in response to Billy O'neal's questions): Thinking about it a bit more, there actually is source code easily available that shows most of what's going on. The CRITICAL_SECTION data structure is defined in winbase.h as a typedef of an RTL_CRITICAL_SECTION. That, in turn, is defined in WinNT.h as:

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

    //
    //  The following three fields control entering and exiting the critical
    //  section for the resource
    //

    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;        // from the thread's ClientId->UniqueThread
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;        // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

If memory serves, the basic idea of how this is used runs something like:

  1. If this thread already owns the critical section, increment RecursionCount and return
  2. Otherwise, do SpinCount attempts to enter CS via fast path using atomic ops on LockCount
  3. Otherwise, wait on LockSemaphore
Jerry Coffin
*Note: A critical section is not a wrapper around a mutex but behaves like an unnamed single-process mutex. As such a critical section requires no support from the Kernel.
Billy ONeal
@Billy: The last time I looked through the source code, that clearly wasn't true -- EnterCriticalSection basically did a spin-lock for a few iterations, but if that failed, it waited on a real mutex. I'd almost bet that's still true -- it's what lets other threads get scheduled.
Jerry Coffin
@Jerry Coffin: Where did you get source code for Windows? According to MSDN ( http://msdn.microsoft.com/en-us/library/ms682530(v=VS.85).aspx ) "a critical section can be used only by the threads of a single process ... critical section objects provide a slightly faster, more efficient mechanism (a processor specific test and set instruction)". How would that be possible if they were implemented in terms of mutexes?
Billy ONeal
@Billy:well, not really source code, but stepped through it in the debugger. Basically, it does double-checked locking -- i.e. a "fast path" that does an `InterlockedIncrement` (or similar -- I don't remember all the details). If that fails several times, it waits on the kernel object.
Jerry Coffin
That kernel object is not a mutex though
Stewart
@Stewart: well, according to the name, it should certainly be a semaphore -- but keep in mind that mutex is really short for for "mutex semaphore", so the semaphore *could* still be a mutex (though I agree there's a good chance it's not). You'd just about have to look at `InitializeCriticalSection` to be sure.
Jerry Coffin
I think it gets initialized in EnterCriticalSection actually. I'm pretty sure it isn't a semaphore either. It probably was in 1993 or whenever when that header was written, but NT has moved on since then and changing variable names is difficult when those variables are in WinNT.h.
Stewart
@Stewart:I'm not sure why it would "move on" -- you're still left with the fact that if you can't enter the critical section, you want to wait on a kernel object so the scheduler knows the thread is blocked. Offhand, I can't think of much reason for the basics of how it works to have changed.
Jerry Coffin
Various online sources cover the gory details. The problem was that InitializeCriticalSection has no means of communicating failure, and in fact this is because the event used to block waiting threads isn't allocated until the first time there is contention. This meant EnterCriticalSection could throw an exception in low memory conditions. This surprised enough people that the implementation was changed significantly to fix it in the WinXP timeframe.
Stewart
A: 

Seems like conditions variable is what I was looking for.

On windows They work with critical Section and SleepConditionVariableCS

On linux pthread_cond_timedwait work with pthread.

Thanks all.

JP
I had to remplace the windows critical section condition variable by a CreateEvent() because critical section on windows are new as of Vista and up
JP
A: 

With pthread, a condition variable.

On Windows, it looks like you want a Semaphore. Win32 Semaphores have a counter that starts at zero - at which point the handle is not signaled. As you add items to the queue, you would increase the semaphore counter with ReleaseSemaphore - Each count added to the semaphore will satisfy one call to a WaitforXXXObject function, so, if you added 3 items to a queue, you would ReleaseSemaphore with a count of 3. WaitFor... would then return 3 times before the handle became non signalled again.

Chris Becke