Continuation call across threads

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.

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.

This paragraph halfway describes how Ruby implements Continuations.

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.

The other option is to implement a sandbox thread that handles callcc
and invoking the continuation.

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?

Its just an implementation-specific issue. Continuations in Ruby are
implemented as a “frozen” thread which is restored over the top of the
current thread.

···

ysantoso-rubytalk@jenny-gnome.dyndns.org (ysantoso-rubytalk@jenny-gnome.dyndns.org) wrote:


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

ysantoso-rubytalk@jenny-gnome.dyndns.org schrieb im Newsbeitrag
news:87n041mebw.fsf@dessyku.is-a-geek.org

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

“Robert Klemme” bob.news@gmx.net writes:

  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]

  1. 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]” (Project MAC Home Page).

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” – Software Development Attitude). 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.