Quick question on thread-interrupt! Marc Nieper-Wißkirchen (07 Nov 2022 09:41 UTC)
Re: Quick question on thread-interrupt! Marc Feeley (07 Nov 2022 18:19 UTC)
Re: Quick question on thread-interrupt! Marc Nieper-Wißkirchen (07 Nov 2022 18:43 UTC)
Re: Quick question on thread-interrupt! Marc Feeley (07 Nov 2022 20:08 UTC)
Re: Quick question on thread-interrupt! Marc Feeley (07 Nov 2022 20:28 UTC)
Re: Quick question on thread-interrupt! Marc Nieper-Wißkirchen (08 Nov 2022 07:27 UTC)
Re: Quick question on thread-interrupt! Marc Feeley (08 Nov 2022 15:00 UTC)
Re: Quick question on thread-interrupt! Marc Nieper-Wißkirchen (08 Nov 2022 15:58 UTC)
Re: Quick question on thread-interrupt! Marc Nieper-Wißkirchen (09 Nov 2022 12:15 UTC)
Re: Quick question on thread-interrupt! Marc Nieper-Wißkirchen (11 Nov 2022 11:24 UTC)
Re: Quick question on thread-interrupt! Marc Feeley (07 Nov 2022 20:36 UTC)

Re: Quick question on thread-interrupt! Marc Nieper-Wißkirchen 08 Nov 2022 07:27 UTC

Am Mo., 7. Nov. 2022 um 21:08 Uhr schrieb Marc Feeley <xxxxxx@iro.umontreal.ca>:
>
>
> > On Nov 7, 2022, at 1:43 PM, Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
> >
> > Thank you!
> >
> > Am Mo., 7. Nov. 2022 um 19:19 Uhr schrieb Marc Feeley <xxxxxx@iro.umontreal.ca>:
> >>
> >>
> >>> On Nov 7, 2022, at 4:41 AM, Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
> >>>
> >>> Hey Marc,
> >>>
> >>> could you describe the exact semantics of Gambit's thread-interrupt!
> >>> or give me a link to where it is documented?
> >>>
> >>> Specifically, what happens when the thunk returns normally?
> >>>
> >>> We talked about capturing a continuation inside the thunk, which is
> >>> related to the question.
> >>>
> >>> Thanks,
> >>>
> >>> Marc
> >>>
> >>
> >> The API and semantics of thread-interrupt! has evolved over time since the introduction of threads in Gambit v4.0 (~2000).  The basic idea is to force a runnable or blocked thread to immediately execute a call to a thunk, regardless of what that thread is currently doing.  Conceptually each thread executes a series of atomic actions (“atomic” in the sense that they are operations at a certain level of abstraction of the virtual machine, such as a call to “cons”, “car”, “pair?”, etc).  But note that some Scheme predefined procedures, such as “append”, “map”, etc are not atomic and are a series of atomic actions.  The thread interrupt mechanism inserts the call to the thunk between such atomic actions thus ensuring that interrupts happen at “safe places” (for the Scheme virtual machine, which does not mean that it is safe for the logic of the program, which is the programmer’s concern).  Conceptually, if the thread was about to evaluate <expr>, it replaces this by the evaluation of (begin (thunk) <expr>) so that the thunk’s result is ignored and the thunk is called with the thread’s current continuation as a parent, including the dynamic environment.

The problem I still see is that thread-interrupt! and, to a lesser
extent, thread-exit! will expose implementation details of standard
procedures (those that are implemented as non-atomic ones).

Assume that we have a procedure "foo" in the standard, that, by
definition, increases the global variable "x" by one and then the
global variable "y" before it returns.

With the usual "as if" rule, it wouldn't matter if foo were implemented as

(define foo
  (lambda ()
    (set! x (+ x 1))
    (set! y (+ y 1))))

or as

(define bar
  (lambda ()
    (set! y (+ y 1))
    (set! x (+ x 1))))

With thread-interrupt!, however, a continuation in the middle of the
execution of foo could be caught, making the order of assignments
observable.  This would suddenly rule out the second implementation
because the specification of "foo" would have become an
over-specification with the introduction of thread-interrupt!.

Even more complicated is the matter with the non-atomic increase of x and y.

Another problem is that the application, including the standard
libraries (but not the VM) may be in an unsafe state at the time of
the interrupt.  So any call to a standard procedure may crash the
system.  But the standard should allow an implementation to offer a
safe mode.

Hypothetical procedures "thread-raise" and "thread-raise-continuable",
would make it a bit easier.  We would then have:

(define thread-interrupt!
  (lambda (thread thunk)
    (thread-raise-continuable thread (make-interrupt-condition (lambda
(exc) (thunk)))))

(define-condition-type &interrupt-condition &condition
  make-interrupt-condition interrupt-condition?
  (handler interrupt-condition-handler))

The initial exception handler would then be:

(lambda (exc)
  (cond
    [(interrupt-condition? exc)
     ((interrupt-condition-handler) exc)]
    [else ...]))

Procedures like foo could then install a custom exception handler
("interrupt handler").

This would make thread-interrupt! less powerful, though.

What is a good way out?

[...]

> >
> > Does Gambit handle signals through thread-int!, e.g. as in the following?
> >
> > (##thread-int! <thread> (lambda () (raise-continuable <signal-condition>)))
> >
> > or
> >
> > (##thread-int! <thread> (lambda () (signal-interrupt <info>)))
> >
> > where
> >
> > (define (signal-interrupt info)
> >  ((current-signal-interrupt-handler) info))
>
> It depends what you mean by “a signal”…  If you mean something related to POSIX signals then no it is not implemented that way because POSIX signals are executed asynchronously (with respect to the Gambit virtual machine) so they could happen in the middle of some VM operation that should not be interrupted because the VM state is temporarily inconsistent.  So instead a POSIX signal will register the signal in a bit set, and then raise an “interrupt flag” that is checked regularly at “safe points” and the handler checks the bit set.  This is a very low overhead polling mechanism that piggybacks on the stack overflow detection logic.  See my paper:

I meant a "VM signal", which could be triggered by a POSIX signal. I
was interested in how the high-level interface/high-level semantics
worked.  It seemed to me that thread-interrupt! is a sufficiently
general primitive.

>    Polling Efficiently on Stock Hardware, FPCA93 (http://www.iro.umontreal.ca/~feeley/papers/FeeleyFPCA93.pdf)

I have been experimenting a bit with virtual machines.  There, I
usually use hardware detection for a stack overflow.  The overflow
handler expands the stack but then sets an interrupt flag.  The actual
code then just has to poll the interrupt flag (some volatile
sig_atomic_t).

> Fun fact: that paper has achieved a certain notoriety because it is one of the few references in the book “The Java Language Specification” in the section 11.3.2 “Handling Asynchronous Exceptions”.

Cool!

Marc

[...]