From: Daniel B. <da...@te...> - 2003-04-05 19:49:59
|
[ CCed to lispweb, where it may catch the attention of more people interested in Web programming ] Kevin Rosenberg <ke...@ro...> writes: > I'd like to use sb-threads with modlisp. I wonder if someone familar > with application programming with sb-threads would review the > following two functions that use cmucl's threads and share their > insight how to optimally port to sb-threads. At this stage in the development of sb-threads, anyone claiming to be familiar with application programming using it should be regarded in the same light as anyone claiming ten years experience with C# (Except, maybe, with slightly better taste in what they lie about) OK, so what the code here seems to be doing is spawning a thread for each listening socket (I doubt there are too many of these), then a new thread for each accepted connection. I don't know if mod_lisp uses the same connection for multiple requests or not. This can be translated fairly mechanically into the SBCL thread api: use sb-thread:create-thread instead of mp:make-process, and sb-sys:wait-until-process-usable instead of mp:wait-until-process-usable (one of the nice things about kernel threading: blocking operations don't block the rest of lisp :-) However: SBCL threads are presently pretty heavyweight things. Although the kernel guys have lavished a certain amount of attention on making thread creation lightweight, it's still a kernel task, plus we need to allocate stacks and so on - eventually we'll resource these, but getting it to work right is presently a priority over work fast. With that in mind, my gut instinct (I haven't benchmarked) says it's better to make them do a little more work in their life than one request each. In my (so far unpublished, uncommitted) patch for Araneida, I made it spawn 5-10 threads at startup[*], _all_ of which sit in the accept() call. We rely on the Linux kernel to be moderately sensible as to which process(es) it wakes up to get the request - yes, I've heard of "thundering herd", but so has everyone, and I'm pretty sure the kernel guys have acceptable wake-one semantics these days. So, I've not done any comparative testing of this approach - the particular web app I have in mind is mostly database-bound anyway, and if I wanted lightning-fast static file serving I'd be using roxen or something. [*] an obvious enhancement would be to size this dynamically based on load - e.g. by measuring the time that a thread spends sleeping in accept, and spawning more threads when it gets low. mod-lisp code for CMUCL: > (defvar *process-function*) > (defvar *process-name*) > > (defun make-socket-server (&key (name (format nil "socket server ~d " > (incf *server-count*))) > port > function > wait > (format :text)) > (let ((*process-function* function) > (*process-name* name)) > (mp:make-process #'(lambda () (make-apache-listener port))))) > > > (defun make-apache-listener (port) > (let ((socket (ext:create-inet-listener port))) > (unwind-protect > (loop > (mp:process-wait-until-fd-usable socket :input) > (multiple-value-bind (new-fd remote-host) > (ext:accept-tcp-connection socket) > (let ((stream (sys:make-fd-stream new-fd :input t > :output t))) > (mp:make-process #'(lambda () > (funcall > (symbol-function *process-function*) > stream)))))) > (unix:unix-close socket)))) Araneida code for SBCL #+sb-thread (excerpt, not expected to work on its own) (defun accept-handler (listener) (loop (handler-case (let ((socket (socket-accept listener))) (handle-a-request (socket-make-stream socket :element-type 'base-char :input t :output t :buffering :full) socket)) (sb-bsd-sockets::socket-error (c) (format t "socket error ~A~%" c))))) (defun install-thread-handlers (&optional (num-threads 5)) (let* ((ports (remove-duplicates (mapcar #'server-port *exported-servers*))) (threads nil) (listeners (mapcar (lambda (x) (listen-to-inet-port :port x :reuse 1)) ports))) (unless listeners (error "No sockets to listen on")) (dolist (socket listeners) (dotimes (n num-threads) (push (sb-thread:make-thread (lambda () (accept-handler socket))) threads))) threads)) Enjoy ... -dan -- http://www.cliki.net/ - Link farm for free CL-on-Unix resources |