Making sending from a separate thread work when using Woo as a backend server
In current websocket-driver there is a problem – when you start a separate thread on the server and trying to send message to the client, it is delivered with delay after only 60 seconds. The issue exists only when using Woo server. On Hunchentoot, message is delivered immediately.
The problem is the libev's event loop. This commit is a hack around and should be improved to work with all backend servers.
@fukamachi any help from you will be very appreciated here.
I'm trying to use websocket-driver with (new) version of the Weblocks framework. And want to push data to the client from a separate background thread. But this does not work with Woo server.
This patch is a workaround which works for me but should be improved before merge.
Eitaro, what do you think?
Thank you for reporting. I'm still really not sure, but perhaps this should be done somehow in Woo socket code. I'll look into it and try to fix it.
Can you retry it with Woo's allow-multithread-write branch?
Can you retry it with Woo's allow-multithread-write branch?
No, does not work. Nothing changed.
I wrote the reproducible sample code. And allow-multithread-write branch just works expected.
% sbcl --load this.lisp
;;% uname -rpms
;;Darwin 18.2.0 x86_64 i386
(ql:quickload
'("clack"
"websocket-driver"
"woo"))
(defparameter *port* 8000)
(defparameter *server* ;; please change this to switch another backend.
:woo
;:hunchentoot
)
(use-package :websocket-driver)
(defvar *echo-server*
(lambda (env)
(let ((ws (make-server env)))
(on :message ws (lambda (message)
;; The code simulates sending message from another thread context.
(bt:make-thread (lambda () (send-text ws message)))))
(lambda (responder)
(declare (ignore responder))
(start-connection ws)))))
;; Client
(bt:make-thread
(lambda ()
(sleep 3)
(let ((ws (make-client (format nil "ws://localhost:~D/" *port*))))
(start-connection ws)
(on :message ws (lambda (message)
(format t "Receive ~A~%" message)))
(send ws "Hi"))))
;; Server
(clack:clackup *echo-server* :server *server* :port *port*)
Great, will try it again soon. Thank you!
Another workaround (SBCL only)
(sb-ext:schedule-timer (sb-ext:make-timer (lambda ()
(wsd:send-text ws "message"))
:thread th)
0)
with ws is websocket object and th is thread (bt:current-thread) with the time you'v been call wsd:make-server.
The above code can run on current master branch of websocket-driver.
@kkazuo interesting feature! I didn't know about it! Thank you.
INTERRUPT-THREAD and SCHEDULE-TIMER are unsafe unless it is known that injecting code into an executing thread is okay. In this case, this would require libev to be reentrant among other things. Using a queue, as svetlyak40wt does, seems inevitable.