Aha |thread-state| - neat! Thank you for clarifying, was not aware of it.

Well it implements the critical logics really, so extending it would be rather for convenience than necessity.


(Reasoning:

Without the awareness of |thread-state|, what brought me to suggest this ##thread-status which is indeed a |thread-state| with continuation capturing, was this reasoning:

 * As a user you want to be able to know what is going on in Gambit, in the sense, you want to be able to get a ,be printout of every running thread.
   (And to know if it's initializing/running/ended with a result value and if so which/ended with exception and if so which, though that's completely secondary here)

 * Getting the ,be requires catching the thread's current continuation.

 * Catching the continuation of an uninitialized thread gives SIGSEGV

 * Knowing if a thread is initialized or not requires access to thread-internal structures using macros that only Gambit is compiled with. To extract this information reliably, we need to extend Gambit with some kind of getter for it.

 * Alas: You need a checker for thread status, as to ensure it was initialized so you don't attempt to capture its continuation unless it was, and this checker needs to be implemented in Gambit

This line of reasoning brought |##thread-status| which checks for every possible thread state, and optionally captures the thread's continuation if the thread is running.


|thread-state| returns all of this info, so the only thing that could be added to it would be that if thread is active it catches its continuation and returns it - and then the special logic that makes this a bit complex, which would be the central reason for making |thread-state| do it:

Catching the continuation can only be done while Gambit's threading system is running, and this means that catching the continuation can fail as the thread may end during |thread-state|'s course of execution. Thus, |thread-state| needs to have the logics to detect this, and start over from the beginning if it happens.

Perhaps with future Gambit versions this logic will change, that could be a good reason to extend |thread-state| with it instead of requiring the user to implement it.

Also perhaps it could be said that this continuation dumping mechanism is within the definition of what Gambit is supposed to do out of the box, so that it should be included by this reason.

Now that Gambit provides the critical logics, no hacks that bring unstability are needed anymore, so from my side I'm good with either way.


That |thread-state| reports what IO object it's waiting for and with what timeout is neat!


Anyhow so by this my question transformed to:
)


What about adding to the |thread-state-active| structure a slot |continuation| or |current-continuation|, and making |thread-state| capture the continuation or not depending on an optional argument (capture-continuation? #t) etc.?


Best regards,
Mikael

2013/7/31 Marc Feeley <feeley@iro.umontreal.ca>
There is already a thread-state procedure with similar functionality.  Can you explain what is lacking in that procedure and extend it with the functionality you need?

Marc

On 2013-07-12, at 6:00 AM, Mikael <mikael.rcv@gmail.com> wrote:

> Three fixes to code from previous post.
>
> Sent a pull request for it for inclusion of this procedure, https://github.com/feeley/gambit/pull/38 .
>
> Test:
>
> > (define t (make-thread (lambda () (thread-sleep! 10) 'over)))
> > (##thread-status t)
> (created)
> > (begin (thread-start! t) (##thread-status t))
> (starting)
> > (##thread-status t)
> (running)
> > (##thread-status t #t)
> (running #<continuation #2>)
> > (##thread-status t)
> (ended over)
> > (define t (thread-start! (make-thread (lambda () (raise "error")))))
> > (##thread-status t)
> (ended/exception "error")
>
> To it included in your Gambit binary until Marc recompiles the C files in the next Gambit release, you need to do:
>
> ./configure
> make
> cd lib
> ../gsc/gsc -:~~bin=../bin,~~lib=../lib,~~include=../include -c  _thread.scm
> ../gsc/gsc -:~~bin=../bin,~~lib=../lib,~~include=../include -f -link -flat -o _gambc.c _kernel.c _system.c _num.c _std.c _eval.c _io.c _nonstd.c _thread.c _repl.c
> cd ..
> make
>
> ; (##thread-status thread #!optional capture-continuation-if-running?)
> ; => list ('created) = The thread has not yet been started
> ;    list ('starting) = The thread has been started but execution has not yet begun
> ;    list ('running [continuation-object]) = The thread is running
> ;    list ('ended result) = The thread terminated ordinarily, with the return value result
> ;    list ('ended/exception exception) = The thread terminated with the exception exception
> (define (##thread-status thread #!optional capture-continuation-if-running?)
>
>   (let loop ()
>
>     ; Make snapshot of thread state
>     ; Quantum manipulation just to ensure get a consistent snapshot
>     (define quantum-pre (thread-quantum (current-thread)))
>     (thread-quantum-set! (current-thread) +inf.0)
>     (let* ((end-condvar (macro-thread-end-condvar thread))
>            (exception?  (macro-thread-exception?  thread))
>            (result      (macro-thread-result      thread)))
>       (thread-quantum-set! (current-thread) quantum-pre)
>
>       ; Generate return value
>       (cond ((not end-condvar)
>              ;; Thread has terminated
>              (if exception?
>                  ;; Thread has terminated with exception
>                  (list 'ended/exception result)
>                  ;; Thread has terminated with result
>                  (list 'ended result)))
>             (exception?
>              ;; Thread has never run and is not terminated
>              (if (not result)
>                  ;; Thread is not yet started
>                  (list 'created)
>                  ;; Thread is started but has not yet run
>                  (list 'starting)))
>             (else
>              (if capture-continuation-if-running?
>                  ; continuation = #f here indicates that the thread state changed during the course
>                  ;                of execution of this procedure, in such a way that the thread no
>                  ;                longer has a continuation. Thisis the case if the thread ended.
>                  (let ((continuation
>                         (with-exception-catcher
>                          (lambda (e) #f)
>                          (lambda () (##thread-continuation-capture thread)))))
>                    (if continuation
>                        (list 'running continuation)
>                        (loop)))
>                  (list 'running)))))))
>
>
> 2013/6/23 Mikael <mikael.rcv@gmail.com>
> (dump-threads) uses ##thread-continuation-capture to get the current continuation of a target thread.
>
> This presumes that the target thread has been started, if it has not ##thread-continuation-capture causes SIGSEGV.
>
> Checking for if it has been started requires access to thread object fields that are not exported currently, and thus to do this right now you need to make a hack consisting of copying out the thread structure's definition into your code and doing the check there - this is not version safe.
>
> I propose the introduction in Gambit of a procedure
>
> (##thread-status thread)
> => list ('created) = The thread has not yet been started
>    list ('starting) = The thread has been started but execution has not yet begun
>    list ('running) = The thread is running
>    list ('ended result) = The thread terminated ordinarily, with the return value result
>    list ('ended/exception exception) = The thread terminated with the exception exception
>
> as the definition is quite short and its presence enables the programmer to without hacks reliably dump threads, which is like a core thing for debug.
>
> Here follows a definition for inclusion into Gambit, based on an email Marc sent 2011-12-16, quoted below:
>
> (define (##thread-status thread #!optional capture-continuation-if-running?)
>
>   (let loop ()
>
>     ; Make snapshot of thread state
>     ; Quantum manipulation just to ensure get a consistent snapshot
>     (define quantum-pre (thread-quantum (current-thread)))
>     (thread-quantum-set! (current-thread) +inf.0)
>     (let* ((end-condvar (macro-thread-end-condvar thread))
>            (exception?  (macro-thread-exception?  thread))
>            (result      (macro-thread-result      thread)))
>       (thread-quantum-set! (current-thread) quantum-pre)
>
>       ; Generate return value
>       (cond ((not end-condvar)
>              ;; Thread has terminated
>              (if exception?
>                  ;; Thread has terminated with exception
>                  (list 'ended/exception result)
>                  ;; Thread has terminated with result
>                  (list 'ended result)))
>             (exception?
>              ;; Thread has never run and is not terminated
>              (if (not result)
>                  ;; Thread is not yet started
>                  (list 'created)
>                  ;; Thread is started but has not yet run
>                  (list 'starting)))
>             (else
>              (if capture-continuation-if-running?
>                  (let ((continuation
>                         (with-exception-catcher
>                          (lambda (e) (loop))
>                          (lambda () (capture-continuation-if-running? thread)))))
>                    (list 'running continuation)
>                    (list 'running))))))))
>
> Use would look something like:
>
> > (define t (make-thread (lambda () (thread-sleep! 5))))
> > (##thread-status t)
> (created)
> > (thread-start! t)
> > (##thread-status t)
> (running)
> > (thread-sleep! 5)
> > (##thread-status t)
> (ended #!void)
>
> With such a definition, a (dump-threads) could be defined as easy as:
>
> (define (dump-threads #!key threads thread-group thread #!rest display-continuation-backtrace-args)
>   (map (lambda (thread)
>          (let loop ()
>            (let ((status (##thread-status thread #t)))
>              (if (eq? (car status) 'running)
>                  (list 'running (call-with-output-string
>                                  '()
>                                  (lambda (port) (apply display-continuation-backtrace
>                                                        `(,(cadr status) ; = continuation
>                                                          ,port
>                                                          .,display-continuation-backtrace-args)))))
>                  status))))
>        (or threads (thread-group->thread-list (or thread-group (thread-thread-group (or thread (current-thread))))))))
>
> Please feel free to provide feedback.
>
> Brgds, Mikael
>
> 2011/12/16 Marc Feeley
>
> On 2011-12-15, at 6:22 PM, Mikael wrote:
>
> [..]
> > Aha, that is good to be aware of - before running ##thread-continuation-capture on a thread, how can i check it's started (as to safeguard from this)?
>
> The attached code checks for all possible states of a thread.  Please try to dump the state of your threads with it to see if the segmentation fault goes away.
>
> (##include "~~lib/_gambit#.scm")
>
> (define (dump-thread thread)
>
>   (write thread)
>
>   (let* ((end-condvar (macro-thread-end-condvar thread))
>          (exception? (macro-thread-exception? thread))
>          (result (macro-thread-result thread)))
>     (cond ((not end-condvar)
>            ;; thread has terminated
>            (if exception?
>                (begin
>                  ;; thread has terminated with exception
>                  (display " is terminated with exception:\n")
>                  (display-exception result))
>                (begin
>                  ;; thread has terminated with result
>                  (display " is terminated with result:\n")
>                  (pretty-print result))))
>           (exception?
>            ;; thread has never run and is not terminated
>            (if (not result)
>                (begin
>                  ;; thread is not yet started
>                  (display " is not yet started\n"))
>                (begin
>                  ;; thread is started but has not yet run
>                  (display " is started but has not yet run\n"))))
>           (else
>            ;; thread is started and has run
>            (let ((c (##thread-continuation-capture thread)))
>              (display " is running with continuation:\n")
>              (display-continuation-backtrace c)))))
>
>   (newline))
>
> (define t2 (make-thread (lambda () 222)))
> (define t3 (make-thread (lambda () 333)))
> (define t4 (make-thread (lambda () (/ 1 0))))
> (define t5 (make-thread (lambda () 555)))
>
> (thread-start! t3)
> (thread-start! t4)
>
> (thread-sleep! 0.01)
>
> (dump-thread (current-thread))
> (dump-thread t2)
> (dump-thread t3)
> (dump-thread t4)
> (thread-start! t5)
> (dump-thread t5)
>
> ;; % gsi dump-thread.scm
> ;; #<thread #1 primordial> is running with continuation:
> ;; 0  dump-thread             "dump-thread.scm"@32:21 (##thread-continuation...
> ;; 1  (interaction)           "dump-thread.scm"@48:1  (dump-thread (current-...
> ;; 2  ##load
> ;; 3  ##load
> ;; 4  ##main
> ;;
> ;; #<thread #2> is not yet started
> ;;
> ;; #<thread #3> is terminated with result:
> ;; 333
> ;;
> ;; #<thread #4> is terminated with exception:
> ;; Divide by zero
> ;; (/ 1 0)
> ;;
> ;; #<thread #5> is started but has not yet run
>
>
> _______________________________________________
> Gambit-list mailing list
> Gambit-list@iro.umontreal.ca
> https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list