From: Peter Keller on
Johan Ur Riise <johan(a)riise-data.no> wrote:
> I usually use a global variable *stop*, and test this in each thread
> function. If your main function runs in the REPL (I don't in these
> cases, I use a function called start that starts another thread, then
> returns), just open a new lisp source file in slime, and execute (setf
> *stop* t) by placing the cursor after the form and C-x C-e, then it is
> executed in a thread different from the REPL.(You can also create a
> separate REPL buffer).

I had thought about doing the method you propose. Suppose a thread looks
like this (this isn't the real code, just the flow, I'm also leaving
out condition handling and other things for this snippet):

(defun worker-thread ()
(let ((client *the-thread-specific-binding*))
(loop until *stop* do
(format client (read-line client))
(finish-output client))))

What happens is that if I'm blocked on the read-line from the client,
then even if I set the global *stop* binding, the thread waits around
until the client decides to send it data. This leaves threads possibly
piling up in the server process (here the REPL) allowing for denial
of service or latent threads mucking around with stuff if one decides
to start the server function again. In the above case, setting *stop*
to t, and restarting the server, which sets *stop* to nil could have
all happened while blocked on read-line. I can't force the close on the
server side, which is what I'd like to do.

> If I listen on a socket, I would wrap a with-timeout around the accept
> call, else I would have to wait for the next connect before the thread
> stops.

For my purpose, the main thread performs the listen and the accept, and
then the connected socket is given to the thread for communication purposes.

> If I have a long running thread that should wake up rather seldom, I
> use something like this to speed up the shutdown:
>
> (defun long-running ()
> (let ((previous-execution-time 0))
> (loop until *stop* do
> (sleep .5)
> (when (> (- (get-universal-time) previous-execution-time) (* 60 60 24))
> (do-whatever-needs-to-be-done)
> (setf previous-execution-time (get-universal-time)))
> )
> ))

I don't feel very good about polling like this when one has 10,000+ threads.
you end up consuming a lot of CPU even though you may sleep 1 second before
polling again.

> If you exit the image, the same would happen in CL.

Ah good, at least I have that going for me.

> If you destroy threads, you can not close the files and sockets. Killing
> threads from the outside is often not recommended, it is better to let
> each thread complete its thread function.

It turns out that it "works" in SBCL and bordeaux-threads in that the
unwind-protect forms in the thread seem to get called. I realize that might
be implementation defined behavior though, which is why I'm looking for
something better.

> No, this is mentioned only to underscore the fact that a saved image
> can not possibly save the threads and then have them running again
> when you restart the saved image. The same for network connections and
> open files. (Well, threads could theoretically be restored in some
> future lisp implementation, but not network connections). Even if you
> save an image, you must provide a function to start threads and open
> files and connections. SAVE-LISP-AND-DIE is not a tool to stop
> threads.

Yes, I understand. What I meant was I SAVE-LISP-AND-DIE with the :toplevel
function being set to the server start entry point. Then when I rerun the
executable the server starts, listens, binds, etc. Then, if the server
exits, all threads die with it immediately. You have already confirmed
for me that this is in fact what would happen.

Thank you.

-pete
From: Johan Ur Riise on
Peter Keller <psilord(a)merlin.cs.wisc.edu> writes:

> Johan Ur Riise <johan(a)riise-data.no> wrote:
>> I usually use a global variable *stop*, and test this in each thread
>> function. If your main function runs in the REPL (I don't in these
>> cases, I use a function called start that starts another thread, then
>> returns), just open a new lisp source file in slime, and execute (setf
>> *stop* t) by placing the cursor after the form and C-x C-e, then it is
>> executed in a thread different from the REPL.(You can also create a
>> separate REPL buffer).
>
> I had thought about doing the method you propose. Suppose a thread looks
> like this (this isn't the real code, just the flow, I'm also leaving
> out condition handling and other things for this snippet):
>
> (defun worker-thread ()
> (let ((client *the-thread-specific-binding*))
> (loop until *stop* do
> (format client (read-line client))
> (finish-output client))))
>
> What happens is that if I'm blocked on the read-line from the client,
> then even if I set the global *stop* binding, the thread waits around
> until the client decides to send it data.
> This leaves threads possibly
> piling up in the server process (here the REPL) allowing for denial
> of service or latent threads mucking around with stuff if one decides
> to start the server function again. In the above case, setting *stop*
> to t, and restarting the server, which sets *stop* to nil could have
> all happened while blocked on read-line. I can't force the close on the
> server side, which is what I'd like to do.

The WITH-TIMEOUT method could work also here, but it could make
it difficult to achieve some specific functionality.

> For my purpose, the main thread performs the listen and the accept, and
> then the connected socket is given to the thread for communication purposes.
>
>> If I have a long running thread that should wake up rather seldom, I
>> use something like this to speed up the shutdown:
>>
>> (defun long-running ()
>> (let ((previous-execution-time 0))
>> (loop until *stop* do
>> (sleep .5)
>> (when (> (- (get-universal-time) previous-execution-time) (* 60 60 24))
>> (do-whatever-needs-to-be-done)
>> (setf previous-execution-time (get-universal-time)))
>> )
>> ))
>
> I don't feel very good about polling like this when one has 10,000+ threads.
> you end up consuming a lot of CPU even though you may sleep 1 second before
> polling again.

For the occasional wake-up, you really need only one thread. The 0.5
sec wake-ups are really peanuts. Trust me.

But if you want to handle this many clients, you certainly can not do
it with this one-thread-per-connection style anyway. This can work for
say 30 threads, no more. You need to explore the asynchronous sockets
libraries of your operating system and use a single thread for it
all. With asynchrous sockets you can also stop your application
quicker than 0.5 secs.

>> If you exit the image, the same would happen in CL.
>
> Ah good, at least I have that going for me.
>
>> If you destroy threads, you can not close the files and sockets. Killing
>> threads from the outside is often not recommended, it is better to let
>> each thread complete its thread function.
>
> It turns out that it "works" in SBCL and bordeaux-threads in that the
> unwind-protect forms in the thread seem to get called.

I forgot. You need to wrap it in a handler case also, to catch the
TIMEOUT condition, else the condition will flow up.

> I realize that might
> be implementation defined behavior though, which is why I'm looking for
> something better.

It is somewhat implementation dependent. With-timeout could be in
bordeaux threads, but I suspect it is implemented only for sbcl. Both
threads and sockets would be implementation dependent. There is no
implementation independent way.

>
>> No, this is mentioned only to underscore the fact that a saved image
>> can not possibly save the threads and then have them running again
>> when you restart the saved image. The same for network connections and
>> open files. (Well, threads could theoretically be restored in some
>> future lisp implementation, but not network connections). Even if you
>> save an image, you must provide a function to start threads and open
>> files and connections. SAVE-LISP-AND-DIE is not a tool to stop
>> threads.
>
> Yes, I understand. What I meant was I SAVE-LISP-AND-DIE with the :toplevel
> function being set to the server start entry point. Then when I rerun the
> executable the server starts, listens, binds, etc. Then, if the server
> exits, all threads die with it immediately. You have already confirmed
> for me that this is in fact what would happen.

Restarting the image could be useful in this case. Of course, to make
the start quick afterwards. An image (made with save-lisp-and-die) which
has all the libraries and your application, is also useful.

Exiting an image is also implementation dependant, by the way.

From: Johan Ur Riise on
Johan Ur Riise <johan(a)riise-data.no> writes:

> Of course, to make
> the start quick afterwards. An image (made with save-lisp-and-die) which
> has all the libraries and your application, is also useful.

I wanted to say that the exiting of the image is what you need,
save-lisp-and-die solves another problem.

> Exiting an image is also implementation dependant, by the way.
From: Alex Mizrahi on
PK> In the lisp toplevel, there is no such exiting of the entire process.

Well, I'm pretty sure you can exit from the whole process as well...

PK> This "works", but the problem is that the manual for bordeaux-theads
PK> specifically states that I should not use those calls for this type of
PK> thing and other commentors (in an offline discussion I'm having) have
PK> mentioned the same thing.

It is because bordeaux-threads is just an compatibility wrapper and
different CL implementations implement it in different ways -- some
implement cleanup, other do not.

Of course it is better to implement shutdown on application level --
notifying threads via flags or something like that -- but you can't do that
if threads get blocked in I/O or are doing some computations w/o checking
flag for a long time.

PK> want to leave toplevel? If I understand Lisp properly (I'm new to it),
PK> there isn't a concept like UNIX signals to ansychronously alter the
PK> continuation of a thread.

Common Lisp standard has no relationship whatsoever with threads.

Bordeaux-threads library has interrupt-thread function, but what it does
exactly is implementation-dependent. (Probably depends on OS too.)
If it can interrupt I/O operations, it might help you, otherwise it won't.

So here's a solution: if implementation implements destroy-thread in clean
fashion or has interrupt-thread which interrupts I/O, you can use them to
take down your threads.
Some operating systems allow to cancel I/O operations outside the thread
which issued them, it might be a solution too. (Albeit complex and
OS-dependent.)

Otherwise you just CANNOT use indefinitely blocking I/O safely in that
implementation if you need ability to cancel it.

There are some options you have in this case, but they will all be
implementation-dependent. (E.g. you can provide timeout for I/O operations.)

Now about particular implementation, SBCL manual says:

----
Function: sb-thread:terminate-thread thread

Terminate the thread identified by thread, by causing it to run
sb-ext:quit - the usual cleanup forms will be evaluated
----

SO I guess in SBCL you can just use terminate-thread (or destroy-thread
bordeaux-threads wrapper) and it will be ok.
For other implementations you'll need to consult manuals.

Multithreading is not standardized in CL and it heavily depends on OS
anyway, so you shouldn't assume portability for granted.
Things like this need to be carefuly verified and possibly adjusted for each
concrete implementation.

From: Johan Ur Riise on
Peter Keller <psilord(a)merlin.cs.wisc.edu> writes:

> It turns out that it "works"

I wonder what is the difference of ""works"", as opposed to "works"
here. May be it just looks to good to be true, to you. I can
understand that, because I have not seen the likes of the WITH-TIMEOUT
abstraction in any other popular language.