[gambit-list] Remote REPL

James Long longster at gmail.com
Tue Dec 4 22:04:15 EST 2007


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 at 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>
>
>



-- 
James Long
Coptix, Inc.
longster at gmail.com



More information about the Gambit-list mailing list