handler: serialize dynamic request context

ober

e335f1ba871aae238684f26684d9a825349416ab

diff --git a/sinatra/handler.ss b/sinatra/handler.ss
index f157657..826e5e7 100644
--- a/sinatra/handler.ss
+++ b/sinatra/handler.ss
@@ -1,5 +1,9 @@
 (import (std text json)
         (std sugar)
+        (rename (only (std misc thread) make-mutex mutex-lock! mutex-unlock!)
+                (make-mutex sinatra-make-mutex)
+                (mutex-lock! sinatra-mutex-lock!)
+                (mutex-unlock! sinatra-mutex-unlock!))
         (only (std secmon telemetry)
               secmon-telemetry-from-env
               secmon-telemetry-emit!)
@@ -18,12 +22,24 @@
 
 (export sinatra-handler)
 
+(def *sinatra-handler-context-mutex* (sinatra-make-mutex))
+
 ;; Create an httpd handler function for the given sinatra app.
 ;; Returns (lambda (req) ...) suitable for Jerboa std/net/httpd.
 (def (sinatra-handler app)
   (let ((base-handler (make-base-handler app))
         (mw-list (app-middleware app)))
-    (compose-middleware mw-list base-handler)))
+    (let ((handler (compose-middleware mw-list base-handler)))
+      (lambda (req)
+        ;; Chez dynamic parameters used for request context are not isolated
+        ;; reliably across simultaneous raw HTTP worker threads. Keep request
+        ;; context deterministic; expensive preprocessing runs in separate
+        ;; worker processes and all normal route results are cached.
+        (sinatra-mutex-lock! *sinatra-handler-context-mutex*)
+        (try
+          (handler req)
+          (finally
+            (sinatra-mutex-unlock! *sinatra-handler-context-mutex*)))))))
 
 ;; secmon daemon telemetry