Continuation call across threads

Y

ysantoso-rubytalk

Hi,

I was wondering why a Continuation is not callable across threads.

From the definition at wikipedia:

"a continuation saves the execution state of a program at a given
point so that it's possible to resume execution from that state at a
later point in time."

If one takes a thread for what it actually is, which is a
stream-of-execution, then I fail to see a reason, beside the one described
below, why one thread cannot call a Continuation object generated from
another thread.

Why can't the thread use the execution state information saved within
a continuation to recreate the execution state, thus resuming the
suspended execution.

One argument against this capability is that the following code will
may not be correct:

$ = nil
t =Thread.new {
until $todo != nil; end
$todo.call
$todo = nil
}

def foo
Thread.current[:counter] = 0
callcc{|cc|
$todo = cc
return
}
puts "Counter is 0: #{Thread.current[:counter]}" # --> but counter may not be 0
# code depending on thread-specific storage does not operate
# correctly if resumed from another thread.
end

foo
t.join



However, I feel that this is more of a discipline issue on the part of
the programmer. If your code depends on thread-specific storage, don't
resume from another thread.

So, what is the reason for the restriction? Is there some conceptual
issue with continuations being callable from across threads, or is
this just implementation-specific issue?

Thanks,
YS.
 
R

Robert Klemme

Hi,

I was wondering why a Continuation is not callable across threads.

From the definition at wikipedia:

"a continuation saves the execution state of a program at a given
point so that it's possible to resume execution from that state at a
later point in time."

If one takes a thread for what it actually is, which is a
stream-of-execution, then I fail to see a reason, beside the one described
below, why one thread cannot call a Continuation object generated from
another thread.

Why can't the thread use the execution state information saved within
a continuation to recreate the execution state, thus resuming the
suspended execution.

One argument against this capability is that the following code will
may not be correct:

$ = nil
t =Thread.new {
until $todo != nil; end
$todo.call
$todo = nil
}

def foo
Thread.current[:counter] = 0
callcc{|cc|
$todo = cc
return
}
puts "Counter is 0: #{Thread.current[:counter]}" # --> but counter may not be 0
# code depending on thread-specific storage does not operate
# correctly if resumed from another thread.
end

foo
t.join



However, I feel that this is more of a discipline issue on the part of
the programmer. If your code depends on thread-specific storage, don't
resume from another thread.

Every continuation depends on thread specific data, namely the call stack.
So, what is the reason for the restriction? Is there some conceptual
issue with continuations being callable from across threads, or is
this just implementation-specific issue?

You'd have to options that seem to be equally bad:

1. One thread's complete state is cloned and resumed in another thread
context. That means effectively that on thread disappears. And suddenly
you would have to solve all kinds of synchronization problems you weren't
expecting: assume, you use an instance X in one thread that is thread local
and thus not seen by any other thread. You will not synchronize access to
this instance since it's confined to a single thread. Now, if that thread
is cloned than all of a sudden there are two threads sharing this
instance...

2. The invocation of the continuation makes the continuation's original
thread stop and resume the continuation. This will lead to unexpected
context switches and all kinds of strange behavior. And what happens to the
calling thread? Where does execution resume? Is the call suddenly
asynchronous? That would not fit well with the rest of Ruby since all
method invocations are synchronous. And you'd have to make sure that no
return value is used (which is normally done with continuations). If the
call is not asynchronous the calling thread must effectively be blocked
until the cont returns - but then it's not sure that this will happen at all
plus you don't need multipe threads if you have only one thread working and
the other blocked.

In short: I don't see how continuations and threads mix well.

Regards

robert
 
Y

Yohanes Santoso

Robert Klemme said:
1. One thread's complete state is cloned and resumed in another thread
context. That means effectively that on thread disappears. And suddenly
you would have to solve all kinds of synchronization problems you weren't
expecting: assume, you use an instance X in one thread that is thread local
[cut]


2. The invocation of the continuation makes the continuation's original
thread stop and resume the continuation. This will lead to unexpected
context switches and all kinds of strange behavior. And what happens to the
calling thread? Where does execution resume? Is the call suddenly
asynchronous? That would not fit well with the rest of Ruby since all
[cut]

In short: I don't see how continuations and threads mix well.

Regards

robert

Hi,

I find your reply interesting, but am not sure whether you are saying
that continuations and threads do not mix well conceptually or as they
are currently implemented in current official ruby implementation.

Conceptually, I do not find any issue in mixing continuations and
threads. They are distinctly separate concepts. Continuations saves
the execution state at that point. A possible implementation would
probably save the control and binding stacks as what Allergo CL does:

"The control stack maintains the function evaluation state including
lexical bindings. The binding stack records the dynamic bindings of
special variables." (http://tinyurl.com/2hgxw)

Meanwhile, threads are simply streams-of-executions. Resuming a
continuation, then, should be just-another-thing-to-execute. And one
can adopt Scheme's semantic in continuation resumption, that is to
"abandon whatever continuation is in effect ... and will instead use
the continuation that was in effect when the [continuation object was
created]" (http://tinyurl.com/2assv).

So, to address the concerns you raised above:

Concern #1. I certainly agree this is a valid concern. However, I am
also a proponent of responsible programmer. Thus, I see that the
responsibility to mix threads and continuations should lie on the
programmers. The programmers should know what he is doing. I think
it is reasonable to expect at least this much responsibility from
the programmer ("enabling" -- http://tinyurl.com/33x57). In any
case, this concern does not introduce any new responsibility to
programmers coding non-trivial multi-threaded program: be careful
when sharing common resources.

Concern #2. The thread that creates the continuation objects need not
stop, in fact there is no reason for it to stop. The calling thread
should just resume the continuation at the resumption point, which
in ruby is the next statement after the callcc{} statement.

So, running the following code (won't run with current official ruby
implementation):

Thread.new {
Thread.current[:id] = 1
puts "#{Thread.current[:id]} A"
callcc{|c|
puts "#{Thread.current[:id]} B"
Thread.new {
Thread.current[:id] = 2;
c.call
}
puts "#{Thread.current[:id]} C"
}
puts "#{Thread.current[:id]} D"
}

would result in (may vary depending on thread scheduling):

1 A
1 B
1 C
1 D
2 D

As Eric Hodel pointed out elsewhere in this posting thread, the
problem lies in the current implementation: continuations are
implemented in terms of threads. Thus, they are no longer distinct
entities and, apparently, one of the consequence is that you can't
call continuations from across threads. It will also brings out other
ugly things mentioned in your concern #2. So, apparently, what you
were saying are wrt current ruby implementation.

WRT my original problem that would have benefited from a continuation
that is callable across threads, I have found that Eric Hodel's
suggestion works acceptably.


Thank you,
YS.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top