[fpc-pascal] Semaphore problems

Vinzent Höfler JeLlyFish.software at gmx.net
Mon Jul 24 20:33:23 CEST 2006

Burkhard Carstens wrote:
> Am Montag, 24. Juli 2006 19:22 schrieb Vinzent Höfler:
>> Burkhard Carstens wrote:
>>> Am Montag, 24. Juli 2006 17:27 schrieb Vinzent Hoefler:
>>>>> - the rtlEvent works even with timeout (with my patch posted on
>>>>> mantis), but this one clears the signaled state, when wait is
>>>>> called.
>>>> *oh* Before the wait? That's not good. This cries for race
>>>> conditions and subtle deadlock situations.
>>> thread synchronization uses this and takes care of it by calling
>>> RTLeventstartwait which locks the associated mutex.
>> As far as I just read code and comments, it does it for Windows to
>> get Unix behaviour, although I don't see the point here, because this
>> just forces the race condition the POSIX-Threads implementation
>> actually tries to circumvent with this associated mutex.
> I don't think so.

Well, in that case either I am totally stupid (won't be news, anyway) or 
I must have different source code:

 From "rtl/unix/cthreads.inc":

|procedure intRTLEventStartWait(AEvent: PRTLEvent);
|var p:pintrtlevent;
|  p:=pintrtlevent(aevent);
|  pthread_mutex_lock(@p^.mutex);
|procedure intRTLEventWaitFor(AEvent: PRTLEvent);
|var p:pintrtlevent;
|  p:=pintrtlevent(aevent);
|  pthread_cond_wait(@p^.condvar, @p^.mutex);
|  pthread_mutex_unlock(@p^.mutex);

Which is pretty much what you described and if pthread_cond_wait() works 
as described, there's no race condition here.

But now "rtl/win/systhrd.inc":

|procedure intRTLEventStartWait(AEvent: PRTLEvent);
|  { this is to get at least some common behaviour on unix and win32:
|    events before startwait are lost on unix, so reset the event on
|    win32 as well }
|  ResetEvent(THANDLE(AEvent));
|procedure intRTLEventWaitFor(AEvent: PRTLEvent);
|  INFINITE=dword(-1);
|  WaitForSingleObject(THANDLE(AEvent), INFINITE);

I don't see any locking in intRTLEventStartWait(), but I see the signal 
being cleared in an unprotected way. So the signal can still get set 
between the calls to intRTLStartWait() and intRTLEventWaitFor() which 
makes the clearing quite useless.

> In unix, the mutex is locked, which ensures, there is 
> not setevent between startwait and actual call to wait, because the 
> call to wait seems to reset the cond, then releases the mutex and 
> starts waiting.

Yes, and now I seem to understand why. Unix only implements transient 
signals. So if there's noone waiting on the signal, it gets lost. 
Windows OTOH seems to implement persistent signals. That's all the 

 > In windows, this doesn't matter (resetting the event on
> startwait), because it preserves the signaled state of the event.

It preserves the signalled state? By resetting it? I don't think so.

AFAICS, it resets the signal before actually starting to wait for it. So 
it still can sneak in if the timing is right. That's what I'd call a 
race condition.

 > The wait call just returns immediately, if the event is allready 
> To me, this is fine. As long as rtlevent is used with this in mind, it 
> should give a consistent behaviour on windows and linux. (see 
> classes.inc thread synchronization)

You mean code like:

|if timeout>0 then
|  begin
|    RtlEventStartWait(SynchronizeTimeoutEvent);
|    RtlEventWaitFor(SynchronizeTimeoutEvent,timeout);
|  end
|  else
|    RtlEventResetEvent(SynchronizeTimeoutEvent);

So where's the code that makes sure that the thread signalling the event 
is (only) resumed between the two calls?

>> I'm not even sure, if Unix actually clears the event. The man-pages
>> don't seem to indicate so.
> It does, at least with the timedwait. I tested that extensively.

Yes, and now I think we both know why. I seem to repeat myself, but it 
might be important: The difference is all between implementing transient 
and persistent signals.


More information about the fpc-pascal mailing list