Shadowing special variables in threaded handlers
Stelian Ionescu
sionescu at cddr.org
Fri Dec 31 08:51:17 UTC 2021
On Fri, 2021-12-31 at 15:48 +1100, Duncan Bayne wrote:
> Chun Tian writes:
>
> > The difference here is that now (lambda (stream) ...) is a closure
> > which will contain a local version of *foo* at the time when
> > (create-server) is called. This kind of uses of lambda functions
> > is
> > like a cheap object with a member variable.
>
> Thanks for the suggestion - yes, this does work as expected, but
> introduces a difficulty with the API of the library.
>
> The germinal code is as follows (edited to remove large swathes and
> just
> focus on the relevant bits):
>
> =====
> (defvar *germinal-cert* "/etc/germinal/cert.pem")
> (defvar *germinal-cert-key* "/etc/germinal/key.pem")
> (defvar *germinal-tls-context* nil "Variable used to store global TLS
> context")
>
> ;; snip
>
> (with-global-context (*germinal-tls-context* :auto-free-p (not
> background))
> (usocket:socket-server host port #'gemini-handler ()
> :multi-threading t
> :element-type '(unsigned-byte 8)
> :in-new-thread background)))
>
> ;; snip
>
> (defun gemini-handler (stream)
> "The main Gemini request handler. Sets up TLS and sets up request
> and response"
> (handler-case
> (let* ((tls-stream (make-ssl-server-stream stream
> :certificate
> *germinal-cert*
> :key *germinal-cert-
> key*))
> ;; snip
> =====
>
> So replacing the handler function with a lambda that creates a
> closure
> works ... but breaks the non-testing case where you just want to setq
> the special variables in your app startup and be done with it.
>
> The best approach I can think of is something like ...
>
> =====
> ;; snip
> (with-global-context (*germinal-tls-context* :auto-free-p (not
> background))
> (usocket:socket-server host port
> (let ((*threaded-cert* *germinal-cert*)
> (*threaded-cert-key* *germinal-cert-
> key*))
> (lambda (stream) (gemini-handler
> stream)))
> ;; snip
> (let* ((tls-stream (make-ssl-server-stream stream
> :certificate
> *threaded-cert*
> :key *threaded-cert-
> key*))
> ;; snip
> =====
>
> Which seems weird, but also gives the best of both worlds; the
> ability
> to shadow variables for testing purposes, but also setq the *same*
> variables for global configuration.
>
> Thoughts / opinions?
You're overusing special variables. You should also make up your mind
on the concurrency model and try to avoid allowing both foreground and
background operations.
(defun gemini-handler (stream cert key)
"The main Gemini request handler. Sets up TLS and sets up request and response"
(handler-case
(let* ((tls-stream (make-ssl-server-stream stream
:certificate cert
:key key))))))
(defun make-gemini-handler (cert key)
(lambda (stream) (gemini-handler stream cert key)))
(with-global-context (*germinal-tls-context* :auto-free-p (not background))
(usocket:socket-server host port (make-gemini-handler *germinal-cert* *germinal-cert-key*)))
--
Stelian Ionescu
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: This is a digitally signed message part
URL: <https://mailman.common-lisp.net/pipermail/usocket-devel/attachments/20211231/43827be5/attachment.sig>
More information about the usocket-devel
mailing list