Prev: [ANN] ABCL 0.20
Next: foo-user packages
From: Peter Keller on 30 May 2010 13:09 Captain Obvious <udodenko(a)users.sourceforge.net> wrote: > KT> standard FFI, no standard GUI, and no standard sockets. CFFI covers one > KT> of those. In your experience/opinion does IOLib answer the sockets > KT> objection? > > What's wrong with usocket? In my case, usocket was too much of a thin compatibility layer. I didn't want to write an i/o multiplexer, which was a requirement, over it. IMHO any serious codes which utilizes thousands or tens of thousands of clients will need an event multiplexer to deal with the nonblocking and often asynchronous i/o. Why should I write one over usocket when a very good quality one was written already? This is why I think IOLib is a great library. Thank you. -pete
From: szergling on 31 May 2010 02:06 On May 29, 5:30 pm, Peter Keller <psil...(a)cs.wisc.edu> wrote: > Hello, > > I've written an IOLib tutorial which describes how to write network > clients and servers with IPV4 TCP using blocking I/O with the stream > interface and nonblocking I/O with the event multiplexer also including > send-to and receive-from. Thanks for the well-written and content packed work! I have a question about ex4: (unwind-protect (multiple-value-bind (who port) (remote-name *ex4-tls-client*) (format t "A thread is handling the connection from ~A:~A!~%" who port) ;; Prepare the time and send it to the client. (multiple-value-bind (s m h d mon y) (get-decoded-time) (handler-case (progn (format t "Sending the time to ~A:~A..." who port) (format *ex4-tls-client* "~A/~A/~A ~A:~A:~A~%" mon d y h m s) (finish-output *ex4-tls-client*) (format t "Sent!~%")) (socket-connection-reset-error () (format t "Client ~A:~A reset the connection!~%" who port)) (hangup () (format t "Client ~A:~A closed connection.~%" who port))))) ;; Cleanup form for uw-p. (format t "Closing connection to ~A:~A!~%" (remote-host *ex4-tls-client*) (remote-port *ex4-tls- client*)) (close *ex4-tls-client*)) It is a bit tricky to robustly handle closing of the client socket in the thread. For example, if we bound the special variable *ex4-tls- client* to a lexically scoped variable and then did the UNWIND-PROTECT form to close the lexically scoped variable, then if this thread wakes up and gets destroyed after the lexical binding, but before the UNWIND-PROTECT, we'd lose a socket to a client into the garbage collector. Such incorrect code would look like: ;; This code is incorrect! (defun process-ex4-client-thread () (declare (ignorable *ex4-tls-client*)) (let ((client *ex4-tls-thread*)) ;; thread gets destroyed right here! client socket is left open! (unwind-protect ( <evaluable form=""> ) (close client)))) Aside/unrelated: you forgot to escape some html stuff in that "incorrect" code example here, it seems. But my question concerns the difference between lexical vs the special variable *ex4-tls-client*. Could you (or anyone) elaborate on this?
From: Peter Keller on 31 May 2010 03:26 szergling <senatorzergling(a)gmail.com> wrote: > Thanks for the well-written and content packed work! Thank you! > ;; This code is incorrect! > (defun process-ex4-client-thread () > (declare (ignorable *ex4-tls-client*)) > (let ((client *ex4-tls-thread*)) > ;; thread gets destroyed right here! client socket is left > open! > (unwind-protect > ( <evaluable form=""> ) > (close client)))) > Aside/unrelated: you forgot to escape some html stuff in that > "incorrect" code example here, it seems. In my local web copy of the tutorial, I've fixed this html quoting issue. Thank you for pointing it out. > But my question concerns the difference between lexical vs the > special variable *ex4-tls-client*. Could you (or anyone) elaborate > on this? I'll try and clarify the situation. *ex4-tls-client* is a special variable at all times. When the process-ex4-client-thread is born, *ex4-tls-client* is bound to whatever socket was specified in the *default-special-bindings* alist just before make-thread was called. If for whatever reason you wanted to rebind the locally bound (to the thread) special variable into a lexical variable closed to the thread and only operate on that variable inside of an unwind-protect, there is a race condition in that if the thread is killed after the binding happens, but before the unwind-protect is set up, you can leak the socket since only the thread has the reponsibility to close it. What I have labeled as incorrect code is an _explicit race condition_ concerning the mandatory cleanup of the client socket with respect to unwind-protect. In the incorrect code, there is a distinct continuation you can point to which has a live socket, but no protection form to clean it up. Of course, I'm making all kinds of assumptions, even in what I term "correct" code. The first major assumption that I am making, which I can't really hold to be true or portable in good faith is: The thread cannot be killed between the last instruction needed to create and run the thread in make-thread and the sequence of instructions to set up the unwind-protect context in process-ex4-client-thread. If in fact this could happen, the socket would be lost and not cleaned up. This is an _implicit race condition_ that the programmer can't control no matter what they write (to the best of my knowledge) and it seems to be the best I'm able to do with Bordeaux Threads--or possibly any threading package. BT's make-thread looks like it either returns a created and alive thread or signals an error if threading is not supported. It leaves unspecified if a problem happened during thread creation and the thread could not be created. One might suppose you could use thread-alive-p to determine if the returned thread was actually alive, but that function call is not recommended for use in production code according to the docs, and the thread might have legitimately come and gone by the time the parent thread was rescheduled, so what to do? The second major assumption that I am making: When a thread is killed for whatever reason, either internally or externally, the cleanup form of the unwind-protect is always evaluated. There is simply no guarantee that this is true. It would appear that unwind-protect and threading just don't play well together. Threading in lisp is a thorny issue (and one I'm not well versed in either) and this is why I left it as minimal as possible with hefty warnings about them in the examples that use them. I have just enough there to show it can be done, but to solve all of the threading issues would require more work for the application writer with support from their specific lisp implementation. Thank you. -pete
From: szergling on 1 Jun 2010 02:18 On May 31, 7:26 pm, Peter Keller <psil...(a)cs.wisc.edu> wrote: > szergling <senatorzergl...(a)gmail.com> wrote: > > ;; This code is incorrect! > > (defun process-ex4-client-thread () > > (declare (ignorable *ex4-tls-client*)) > > (let ((client *ex4-tls-thread*)) > > ;; thread gets destroyed right here! client socket is left open! Right. The "here" in your comment refers to <here>, as in right here. I mistakenly assumed that this comment block annotates the unwind-protect block below, and that the thread is killed/destroyed inside it... > > (unwind-protect > > ( <evaluable form=""> ) > > (close client)))) <<snip>> > ... there is a race condition in that if the thread is killed after > the binding happens, but before the unwind-protect is set up, you > can leak the socket since only the thread has the reponsibility to > close it. I see where I was confused now. This is not due to the lexical binding per se, but could happen with any code like (progn (foo) (bar) (unwind-protect ...)) The delay before we enter the unwind-protect block leaves us vulnerable to "leaky" sockets if the current thread is destroyed (or either foo or bar performs any non-local exits). More generally, even if there are no apparent high-level expressions between thread creation and entry into unwind-protect, there could be many low-level assembler instructions, giving a similar delay. So how safe is assumption 1 below? <<snipped full explanations>> > The first major assumption that I am making, which I can't really > hold to be true or portable in good faith is: > > The thread cannot be killed between the last instruction needed to > create and run the thread in make-thread and the sequence of > instructions to set up the unwind-protect context in > process-ex4-client-thread. If in fact this could happen, the > socket would be lost and not cleaned up. <<snip>> > The second major assumption that I am making: > > When a thread is killed for whatever reason, either internally or > externally, the cleanup form of the unwind-protect is always > evaluated. How safe is this assumption? Does anyone know any implementations well enough to comment? Thank you very much for the explanation, Pete. I continue to learn about my blind spots with regard to Lisp everyday. Yong
From: Peter Keller on 1 Jun 2010 11:57
szergling <senatorzergling(a)gmail.com> wrote: > I see where I was confused now. This is not due to the lexical binding > per se, but could happen with any code like > > (progn > (foo) > (bar) > (unwind-protect ...)) Indeed that is correct. > The delay before we enter the unwind-protect block leaves us > vulnerable to "leaky" sockets if the current thread is destroyed (or > either foo or bar performs any non-local exits). > More generally, even if there are no apparent high-level expressions > between thread creation and entry into unwind-protect, there could be > many low-level assembler instructions, giving a similar delay. Of course, it is all implementation dependent, but this is true. Garbage collection routines or whatnot could be scheduled there and execute in the new thread, but not in the unwind-protect. Unless one actually went and looked to see what compilers were actually doing or it was some kind af agreed upon semantic (which if it is I am unaware of it--but that would be due to simply me not knowing of it) this is what you have to assume. > So how safe is assumption 1 below? > >> The first major assumption that I am making, which I can't really >> hold to be true or portable in good faith is: >> >> The thread cannot be killed between the last instruction needed to >> create and run the thread in make-thread and the sequence of >> instructions to set up the unwind-protect context in >> process-ex4-client-thread. If in fact this could happen, the >> socket would be lost and not cleaned up. > >> The second major assumption that I am making: >> >> When a thread is killed for whatever reason, either internally or >> externally, the cleanup form of the unwind-protect is always >> evaluated. > > How safe is this assumption? Does anyone know any implementations well > enough to comment? Unfortunately, I am unable to answer these questions to the depth they deserve at point in my journey of learning lisp. Maybe someone else can help out answering them. Later, -pete |