[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: Thread#raise, Thread#kill, and timeout.rb are unsafe

Yukihiro Matsumoto

3/15/2008 2:30:00 AM

Hi,

In message "Re: Thread#raise, Thread#kill, and timeout.rb are unsafe"
on Sat, 15 Mar 2008 02:48:33 +0900, Tanaka Akira <akr@fsij.org> writes:

|But Charles's "safe" is for application. acquired lock
|should be released later, etc.

I know.

|Your "safe" is not enough for application.
|
|We need a mechanism to delay asynchronous exceptions until a
|safe point.

Hmm.

|I think the safe points declaration mechanism should able to
|define safe points for each exception and easy to handle
|blocking operations without race conditions.

What should safe point declaration mechanism look like?

matz.

42 Answers

Roger Pack

3/17/2008 3:07:00 PM

0


> What should safe point declaration mechanism look like?
>
> matz.

Tongue in cheek I would say something like

uninterruptible do

end

or

ensureUninterruptible

end

or

uninterruptible do
# some stuff
handle_exceptions_that_have_been_thrown
end
# where handle_exceptions is where they can all get handled.
Kind of scary still, though.

:)

Anyway my latest thought on the whole timeout difficulty is that there
are a few difficulties with it that might be overcomeable:

Unfortunately, as a previous poster noted, using raise seems itself
almost always inherently dangerous, so these would probably all be
band-aids to a bigger problem.

1) if you have two nested timeouts then "you don't know where the
timeout came from"
timeout 5 do
timeout 5 do
begin
sleep
rescue Exception
# which one is it? What did I just rescue?
end
end
end


You could overcome this by just ensuring that it throws distinct classes
per timeout (i.e. if different timeouts).
timeout 5 do # will by default throw class Timeout::ErrorDepth1 <
Timeout::Error
timeout 5 do # will by default throw class Timeout::ErrorDepth2 <
Timeout::Error

I suppose this would help people decipher what is happening when
timeouts are thrown, though not be helpful otherwise.

1.5) It creates a new thread once per call to 'timeout' which is less
than efficient.

While I typically don't care about efficiency when using timeout, it
might be possible to just have a "single timer thread" that handles all
the raise'ings on the others, to save on thread creation
(theoretically).


So something like this

(sorry if the syntax is wrong--this is just pseudo-code for now)

$timer_thread = nil # just so I remember the name--you wouldn't have to
use a global, of course

class TimerThread < Thread
def handle_any_requests
# if any thread requests a timeout, add it to some list
if some_thread_is_waiting
sleep_until_should_wake_next_thread
if waiting_thread_is_still_running_its_timeout_block
waiting_thread.raise Timeout::Error
end
else
sleep -1
end
end

def TimerThread.add_me_to_the_queue_to_interrupt_me_in seconds, &block
$timer_thread.add_it_to_queue Thread.current, seconds
$timer_thread.wakeup # might need to synchronize this call so that
we don't get confusion
block.call # if we get interrupted between this line and the next we
are DEAD. Exactly the same problem that we had before--an exception
could be raised "far in the future" to interrupt it, when it has already
left and it shouldn't be raised.
$timer_thread.take_me_off_the_queue_I_am_done Thread.current # might
need to synchronize this call, too
end

end



# begin program. Note that you could just fire this up the first time
the script calls Timeout::timeout
$timer_thread = Thread.new {
loop do
handle_any_requests
end
}


Now when you want timeouts you do something like
TimerThread.add_me_to_the_queue_to_interrupt_me_in 5 do # seconds

# do some stuff for a long time

end

That would help with efficiency, since you wouldn't have to spawn a new
thread for each call (though it would, sigh, leave one running always in
the background). It would alleviate some concurrency problems, and
still leave at least one in there. Just throwing it out there.

2) nested timeouts can interrupt each other and leave and 'errant
thread' running which raises a

timeout 5 do
timeout 50 do
begin
sleep
end
end

This causes two threads to be generated, each with a "time out bomb"
ticking, waiting to happen. The outer loops' thread could (as Charles
Nutter noted) interrupt the inner thread during its ensure block, so it
ends up not killing its ticking "time out bomb" thread. Which means
that 45 seconds later it will go off and randomly raise on the parent
thread.

To avoid this I "think" you could wrap the things with mutexes.
However, this also doesn't work because the mutex "synchronize" stuff
itself uses an ensure block, which could be interrupted by the same
confusion (and does, in my experience).

<<Sigh>>

Never mind. I give up. The only safe way to generate asynchronous
calls seems to be to use EventMachine or rev (which have asynchronous
timers) and have your own "exception handling" blocks (i.e. blocks where
it is designated to handle those exceptions).

Thanks.

-R
--
Posted via http://www.ruby-....

MenTaLguY

3/17/2008 4:30:00 PM

0

On Tue, 18 Mar 2008 00:06:47 +0900, Roger Pack <rogerpack2005@gmail.com> wrote:
> Tongue in cheek I would say something like
>
> uninterruptible do
>
> end
>
> or
>
> ensureUninterruptible
>
> end
>
> or
>
> uninterruptible do
> # some stuff
> handle_exceptions_that_have_been_thrown
> end
> # where handle_exceptions is where they can all get handled.
> Kind of scary still, though.

In a language with side-effects, I've become convinced that the only safe
way to handle asynchronous exceptions is for code to be uninterruptible by
default, and only explicitly allow interrupts at specific points (not broad
regions of code).

This gets back to the fundamental principle that, in order to write a
correct multithreaded program, any interaction between threads needs to be
by explicit mutual agreement at specific places. One thread needs to
explicitly initiate the interaction (in this case, by calling Thread#raise),
and the other thread needs to explicitly accept it (e.g. by calling a method
which is known to permit the delivery of any pending interrupts when it is
called). Otherwise, the resulting nondeterminism is impossible to control.

-mental


Tanaka Akira

3/18/2008 5:05:00 AM

0

In article <E1JaM9H-0000jj-AB@x61.netlab.jp>,
Yukihiro Matsumoto <matz@ruby-lang.org> writes:

> What should safe point declaration mechanism look like?

Basically, asynchronous events should be queued.

* make a queue for each thread.
* Thread#raise(exc) enqueue exc to the queue.
* Thread.check_interrupt(klass) checks an exception which is
a kind of klass in the queue of the current thread. If it
exists, the exception is raised.
* other queue examining mechanism, such as sigpending, etc,
may be useful.

Thread.check_interrupt is a safe point.

However safe points is not only Thread.check_interrupt.
Blocking operations, such as I/O, may or may not be safe
points. It is because Thread.check_interrupt with blocking
operations causes race conditions. So application must
choose that make a blocking operation interruption safe or
uninterruptible.

* Thread.blocking_interruptible(klass) { ... }
* Thread.blocking_uninterruptible(klass) { ... }

Blocking operations in Thread.blocking_interruptible are
safe points.

If a thread has no resources to release, it is safe to kill
asynchronously. Other than that, safe points should be
declared explicitly. When a resource is acquired,
application should declare it.

* Thread.delay_interrupt(klass) { ... }

It is safe points that out of outermost
Thread.delay_interrupt.

Another idea is about ensure clause. Since an ensure
clause is used to release a resource, it may delay
asynchronous events as in Thread.delay_interrupt.
--
Tanaka Akira

ara.t.howard

3/18/2008 5:20:00 AM

0


On Mar 17, 2008, at 10:30 AM, MenTaLguY wrote:

> In a language with side-effects, I've become convinced that the only
> safe
> way to handle asynchronous exceptions is for code to be
> uninterruptible by
> default, and only explicitly allow interrupts at specific points
> (not broad
> regions of code).

no doubt you've read it, but for posterity i'll post it here:

http://209.85.207.104/search?q=cache:2T61vSNQ4_EJ:home.pacbell.net/ouster/threads.pdf+%22why+threads+are+a+bad+idea%22&hl=en&ct=clnk&cd=3&gl=us&client...

a @ http://codeforp...
--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama



Gary Wright

3/18/2008 1:45:00 PM

0


On Mar 18, 2008, at 1:19 AM, ara howard wrote:

> On Mar 17, 2008, at 10:30 AM, MenTaLguY wrote:
>
>> In a language with side-effects, I've become convinced that the
>> only safe
>> way to handle asynchronous exceptions is for code to be
>> uninterruptible by
>> default, and only explicitly allow interrupts at specific points
>> (not broad
>> regions of code).
>>
>
> no doubt you've read it, but for posterity i'll post it here:
>
> http://209.85.207.104/sear...:
> 2T61vSNQ4_EJ:home.pacbell.net/ouster/threads.pdf+%22why+threads+are
> +a+bad+idea%22&hl=en&ct=clnk&cd=3&gl=us&client=firefox-a
>

Here is a related talk/paper that offers a slightly different
perspective than Ousterhout: <http://swtch.com/~rsc/talks/thre...

Gary Wright


James Gray

3/18/2008 1:55:00 PM

0

On Mar 18, 2008, at 12:04 AM, Tanaka Akira wrote:

> In article <E1JaM9H-0000jj-AB@x61.netlab.jp>,
> Yukihiro Matsumoto <matz@ruby-lang.org> writes:
>
>> What should safe point declaration mechanism look like?
>
> Basically, asynchronous events should be queued.
>
> * make a queue for each thread.
> * Thread#raise(exc) enqueue exc to the queue.
> * Thread.check_interrupt(klass) checks an exception which is
> a kind of klass in the queue of the current thread. If it
> exists, the exception is raised.
> * other queue examining mechanism, such as sigpending, etc,
> may be useful.

Perl went kind-of along these lines around version 5.8, if I recall
correctly. They added "safe signals" which were signals that only
interrupted at times when it was safe for them to do so.

This did have a few weird side-effects though. For example, running a
complex regex would shutoff signal handling for the duration. That
meant you could no longer timeout a regex.

Perhaps they've fixes these issues now, but I remember the change was
not as smooth as they had hoped it would be.

James Edward Gray II

Tanaka Akira

3/18/2008 2:02:00 PM

0

In article <41DCFB5E-7EF8-4A1E-81B7-95A6A7691F04@grayproductions.net>,
James Gray <james@grayproductions.net> writes:

> Perl went kind-of along these lines around version 5.8, if I recall
> correctly. They added "safe signals" which were signals that only
> interrupted at times when it was safe for them to do so.

It's from 5.7.3.
http://search.cpan.org/dist/perl/pod/perlipc.pod#Deferre...(Safe_Signals)

> This did have a few weird side-effects though. For example, running a
> complex regex would shutoff signal handling for the duration. That
> meant you could no longer timeout a regex.

Ruby already defer signal handling same as Perl.
--
Tanaka Akira

ara.t.howard

3/18/2008 3:21:00 PM

0


On Mar 18, 2008, at 7:44 AM, Gary Wright wrote:

>
> Here is a related talk/paper that offers a slightly different
> perspective than Ousterhout: <http://swtch.com/~rsc/talks/thre...

with 42 slides - it must be good!

;-)

a @ http://codeforp...
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




ara.t.howard

3/18/2008 3:21:00 PM

0


On Mar 18, 2008, at 7:44 AM, Gary Wright wrote:

>
> Here is a related talk/paper that offers a slightly different
> perspective than Ousterhout: <http://swtch.com/~rsc/talks/thre...

with 42 slides - it must be good!

;-)

a @ http://codeforp...
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




MenTaLguY

3/18/2008 5:58:00 PM

0

On Tue, 18 Mar 2008 22:44:42 +0900, Gary Wright <gwtmp01@mac.com> wrote:
> Here is a related talk/paper that offers a slightly different
> perspective than Ousterhout: <http://swtch.com/~rsc/talks/thre...

+1; those slides are quite good.

-mental