Gorgeous. I was already setting the runtime options -:dar. The exception handler was the problem; because it's not running on the primordial thread I need to explicitly call the repl exception handler in the repl thread.
Basically I wrap an exception handler around the call to ##repl-debug-main which manually fires off ##repl-exception-handler-hook. If anyone knows a better way to do this please enlighten me.
The remote node (on which the repl worker for the local node is running) is sitting at a repl in the primordial thread itself. I'm a little curious: before I added my exception handler, why didn't the primordial thread on the remote node catch the exception and throw it into a sub-repl?
I have attached the new version which works great. I also optimized the remote reading and writing; previously it was sending and receiving every char as a message, which was dreadfully slow. Now it batches it up where possible. Reading from the repl was a little tricky - we don't want to poll, but we also want to read as many characters at once where possible. The current implementation sends off the characters if nothing is written after .1 seconds.
Also, in order to handle both the ",q" command and any possibility of the remote node crashing/quitting, I had to add a heartbeat to the repl worker process. The only other way I can think of handling quitting is if there's some cleanup function that is called when the primordial thread is terminated - I doubt it though. The heartbeat method isn't too bad - since it handles the worse case scenario of a hard crash.
Thanks again -
;;;;; --- REMOTE REPL ---
(define (start-repl-services node repl-input-proc-port repl-output-proc-port repl-worker-port)
;; Handles printing out to the remote repl (define output-handler (spawn (lambda ()
;; Block until a character is available. ;; When one is available, continually read ;; until nothing is written for .1 seconds, send ;; off the buffer for output, and start over. (let loop1 () (peek-char repl-worker-port) (input-port-timeout-set! repl-worker-port .1) (proc-port-display (read-all repl-worker-port read-char) repl-output-proc-port) (input-port-timeout-set! repl-worker-port +inf.0) (loop1)))))
;; Service to tell the remote repl we're alive (define repl-worker-heartbeat (spawn (lambda () (let loop () (thread-sleep! 1) (! node 'heartbeat) (loop)))))
;; Main loop - the input (spawn (lambda () (let loop () (let ((cmd (proc-port-read-line repl-input-proc-port))) (display cmd repl-worker-port) (write-char #\return repl-worker-port) (write-char #\linefeed repl-worker-port) (force-output repl-worker-port)
(if (equal? cmd ",q") (begin (terminate! output-handler) (terminate! repl-worker-heartbeat)) (loop)))))))
(define (make-repl node) (let ((op (current-output-proc-port)) (ip (current-input-proc-port)) (repl (self))) (remote-spawn node (lambda () (receive (pipe1 pipe2) (open-string-pipe)
(start-repl-services repl ip op pipe1) (set! ##thread-make-repl-channel (lambda () (##make-repl-channel-ports pipe2 pipe2)))
;; Fire up the repl with an explicit repl exception handler (with-exception-handler (lambda (exc) (##repl-exception-handler-hook exc ##thread-end-with-uncaught-exception!)) (lambda () (display "Firing up repl...\n") (##repl-debug-main)))))))
(let loop () (recv (('heartbeat) (loop)) (after 2 (display "\n------ Remote REPL closed -------- \n")))))
On Dec 4, 2007 7:11 AM, Christian Jaeger christian@pflanze.mine.nu wrote:
PS. see also the -:da flag of the Gambit system. Maybe it's enough if you somehow start up your remote nodes with that flag.
$ gsi -:da Gambit v4.0.1
(define t (thread-start! (make-thread (lambda() (error "hello") (+ 3
4)))))
(thread-join! t)
------------- REPL is now in #<thread #2> ------------- *** ERROR IN #<procedure #3>, (console)@1.49 -- hello
,b
0 #<procedure #3> (console)@1:49 (error "hello") 1 ##thread-start-action!
,y
0 #<procedure #3> (console)@1.49 (error "hello")
,(c 3)
7 ------------- REPL is now in #<thread #1 primordial> -------------
*** EOF again to exit $ gsi Gambit v4.0.1
(define t (thread-start! (make-thread (lambda() (error "hello") (+ 3
4)))))
(thread-join! t)
*** ERROR IN (console)@2.1 -- Uncaught exception: #<error-exception #2> (thread-join! '#<thread #3>) 1>