How can I properly thread serve/servlet?

Hi,

I am working on a Racket web server. Having used Clojure in the past, I quite like being able to have convenience procedures for working within my code.

Particularly, I want to implement a (start-server!) and a (stop-server!) procedure that enable me to start / stop the web-server within the current REPL.

My approach so far looks like this:

#lang/racket 

;; (...)

(define servlet-thread #f)

(define (start-server!)
  (unless servlet-thread
    (set! servlet-thread (thread (thunk (serve/servlet app-dispatch
                                                       #:launch-browser? #f
                                                       #:port 8080
                                                       #:server-root-path (build-path (current-directory))
                                                       #:servlet-path "/"
                                                       #:listen-ip #f
                                                       #:log-file (build-path "app.log")
                                                       #:extra-files-paths (list "htdocs")))))))

(define (stop-server!) 
 (kill-thread servlet-thread)
 ;; (set! servlet-thread #f)
)

Starting the server works fine and I can continue using the REPL, but when running (stop-server!) nothing really happens.

The thread is dead according to (thread-dead?), but I can still reach and interact with my server when browsing to localhost:8080.

Why is that? I expected the thread to be actually dead and not executing code anymore.

Check me on this: when you run serve/servlet without the thread wrapper, it returns immediately after starting the server, right? Assuming so: the thread you started will die as soon as the call to serve/servlet returns. You can probably also check this by calling thread-dead? before you call stop-server!. I believe you will find that the thread dies immediately when the server starts up.

In order to get the behavior you want, I suspect you will either need to

  • find a function in the web-server interface that lets you inspect and halt running server instances, or
  • start the server with a lower-level blocking call that does not immediately return, but actually runs the (main?) web server thread on the current thread.

There are other heavier-handed approaches, e.g. custodians or literally running another process using process or system or similar.

... But I bet there's a way to do this using only web server functions.

John

When I run start-server! and then check (thread-dead? servlet-thread) I get #f.

Furthermore, when just running serve/servlet the procedure blocks and the server is listening to incoming requests:

Your Web application is running at http://localhost:8080.
Stop this program at any time to terminate the Web Server.

Indeed I then cannot run further procedures in the same REPL (unlike with start-server!) until I CTRL-C.

I'll try to have a look if I can find other functions in the web-server interface that might suit my needs more, thanks.

You want one of the procedures listed here: 3 Launching Servers . My preference is to always use serve, which returns a procedure that can be used to stop the server. This post might also be helpful.

Also, generally, kill-thread should be avoided unless you know (i.e. the documentation mentions) that the data structures used by the thread you're killing are kill-safe.

3 Likes

This seems to be the way to go, experimenting right now with serve.

I will post my working result and resolve the thread when finished, thank you.

I ended up with the following:

;; (...)

(define servlet-thread #f)
(define server-semaphore (make-semaphore 0))

(define (start-server!)
  (unless servlet-thread
    (set! servlet-thread (thread (thunk (let ([stop (serve
                                                     #:dispatch (sequencer:make
                                                                 (dispatch/servlet app)
                                                                 (dispatch/servlet not-found))
                                                     #:listen-ip "127.0.0.1"
                                                     #:port 8080)])
                                          (semaphore-wait server-semaphore)
                                          (stop)))))))

(define (stop-server!)
  (unless (thread-dead? servlet-thread)
    (semaphore-post server-semaphore)))

Works for me :slight_smile: