Hi Paul,

> Your revised ConditionVariable#wait:
> 
>   def wait(mutex)
>     unlocked = false
>     begin
>       mutex.exclusive_unlock do
>         unlocked = true
>         @waiters.push(Thread.current)
>         Thread.stop
>       end
>     rescue # Timeout::Error
>       @waiters.delete Thread.current  # Q: is Array#delete an atomic operation?
>       raise
>     ensure
>       mutex.lock if unlocked
>     end
>   end
> 
> What happens if you get a timeout first inside the exclusive_unlock
> block, then inside the rescue (before the delete occurs)?
> 
> (I believe that this is a pretty rare race condition, but is still
> possible).

Wow, nice catch! Thanks - !

Hmm... Would that mean it's possible for 

     ensure
# -------> %exception here%
       mutex.lock if unlocked
     end

potentially an exception firing once we're in the
ensure block as well?  Or is ensure implemented to
have the properties of your no_raise style block,
I wonder?

Well, the reason I had proposed the timeout-safe
wait, or wanted to implement it using timeout,
is because I thought it would be easier than the
signal-based timed_wait I had also implemented.

I wrote what I believed to be a thread-safe (and 
hopefully exception safe) timed_wait using signals,
to sidestep the "timeout" exception throwing issue.
But it came out to a disconcerting 36 lines...

01: class ConditionVariable
02:
03:   def timed_wait(mutex, timeout_secs)
04:     waiter_th = Thread.current
05:     alarm_th = nil
06:     unlocked = false
07:     begin
08:       awake = false
09:
10:       alarm_th = Thread.start do
11:         sleep timeout_secs
12:         Thread.exclusive do
13:           unless awake
14:             awake = true
15:             @waiters.delete waiter_th
16:             waiter_th.wakeup
17:           end
18:         end
19:       end
20:
21:       Thread.exclusive do
22:         unless awake
23:           mutex.exclusive_unlock do
24:             unlocked = true
25:             @waiters.push(Thread.current)
26:             begin
27:               Thread.stop
28:             ensure
29:               awake = true
30:             end
31:           end
32:         end
33:       end
34:     ensure
35:       mutex.lock if unlocked
36:       alarm_th.kill if alarm_th and alarm_th.alive?
37:     end
38:   end
39:
40: end

I say disconcerting because the code in thread.rb is
so beautifully simple.  (Although, as we have seen,
it is not necessarily 100% well-protected, on the other
hand.)  But this routine above seemed more complicated
than I'd like.  [Note - I didn't care if it worked with
timeout, since it doesn't use timeout.]

. . .

Wow your observation has really got me curious now:
Can the exception raised by timeout occur anywhere
in:
  - a rescue block?
  - an ensure block?

That would seem to make it really hard to guard
against...


Thanks for your thoughts !


Regards,

Bill