[fpc-pascal] Semaphore problems

Burkhard Carstens fpc at bcsoft.de
Mon Jul 24 22:02:12 CEST 2006


Am Montag, 24. Juli 2006 20:33 schrieb Vinzent Höfler:
> 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:

I am sure, you're not ;-)


>  From "rtl/unix/cthreads.inc":
> |procedure intRTLEventStartWait(AEvent: PRTLEvent);
> |var p:pintrtlevent;
> |
> |begin
> |  p:=pintrtlevent(aevent);
> |  pthread_mutex_lock(@p^.mutex);
> |end;
> |
> |procedure intRTLEventWaitFor(AEvent: PRTLEvent);
> |var p:pintrtlevent;
> |
> |begin
> |  p:=pintrtlevent(aevent);
> |  pthread_cond_wait(@p^.condvar, @p^.mutex);
here, the mutex is unlocked, and wait state is entered, If timeout 
occours or cond gets signalled, mutex is locked again and wait returns

> |  pthread_mutex_unlock(@p^.mutex);
> |end;
>
> 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);
> |begin
> |  { 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));
> |end;
> |
> |procedure intRTLEventWaitFor(AEvent: PRTLEvent);
> |const
> |  INFINITE=dword(-1);
> |begin
> |  WaitForSingleObject(THANDLE(AEvent), INFINITE);
> |end;
>
> 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.

Yea, but as it's windows, that doesn't matter. setting the signal 
between startwait and waitfor doesn't hurt, because windows doesn't 
reset it before waiting. Resetting it in startwait just ensures, that 
we have roughly the same behaviour like in unix, i.e. clear any 
previously set signal and catch anything that gets signalled between 
the call to startwait and return of waitfor...

> > 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
> difference.

right.

>  > 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.

I mean: it doesn't reset it on the waitforsingleobject call, at least 
not *before* it starts waiting ..

> 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
>
> signaled.
>
> > 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?

You are right! Looks like this is a bug! However, in unix, this would't 
hurt anyway, as RtlEventWaitFor with timeout allways returns 
immediately (mantis #7196) ;-)
Guess, no one tried to use checksynchonize with timeout <> 0 yet, 
otherwise, we'd have a bugreport :-)

> >> 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.

Agreed!

Burkhard




More information about the fpc-pascal mailing list