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

> Johan Ur Riise <johan(a)riise-data.no> wrote:
>> I think that in general, you should not rely on automatic releasing of
>> resources. I think each thread should fall off the end of its thread
>> function (it doesn't really return). You should make sure that sockets
>> and files are closed, either by calling close or use with-open-file or
>> something, before you leave them for garbage collection. This is the
>> same in all languages.
>
> This is true, but if the thread is something like an echo server to a client
> then until the client sends a pre-agreed upon sentinel value, the thread
> will sit there happily copying bytes without any way to stop it.

If you ask for data and wait indefinitely, that is what you get. The
WITH-TIMEOUT abstraction is built on the operating system's signal
system. It asks the kernel to send a signal to the process after the
requested amount of time. The signal interrupts the blocking system
call and causes a signal handler to be executed, which takes you back
into lisp. Is not that what you want?

You could probably build something yourself using signals, but you are
moving further away from lisp.

> Well, there
> is a way with your method before, via inspecting a global *stop* variable,
> but even that has peril and it still isn't an active close from the point of
> view of the client.

I do not understand why this method is not active enough. Maybe you
should start over and take a look at nonblocking asynchronous sockets.

>
> I actively want to clean up all of my non-memory resources, but killing
> threads allows for situations where unwind-protect won't function,
> thereby voiding the semantics of unwind-protect.

Why did you ever expect the unwind-protect form to continue its work
after the thread where it runs is killed?

If you want to kill the thread and have an API for that, use it, and
hope that the operating system does what is needed for you. I suspect
you run out of file handles after a while, but what do I know.

>
>> About the variable *client* - most implementations treat the global
>> special variables as common between all threads, useful for
>> communication between threads.
>
> That is true. But, in my haste I did not mention that I am using the
> bordeaux threads special mechanism of creating thread specific global
> bindings with *default-special-bindings*. It is there that I would set
> up *client* bound to the client socket before starting the thread. In this
> manner is *client* specific to each thread.

Ok, I was not aware of that mechanism.
From: Peter Keller on
Johan Ur Riise <johan(a)riise-data.no> wrote:
> If you ask for data and wait indefinitely, that is what you get. The
> WITH-TIMEOUT abstraction is built on the operating system's signal
> system. It asks the kernel to send a signal to the process after the
> requested amount of time. The signal interrupts the blocking system
> call and causes a signal handler to be executed, which takes you back
> into lisp. Is not that what you want?

It is, but WITH-TIMEOUT seems to be SBCL specific and I was looking for
portability and staying as close to ANSI Common Lisp as possible.

> I do not understand why this method is not active enough. Maybe you
> should start over and take a look at nonblocking asynchronous sockets.

The threading code I'm writing is part of a larger set of examples
where eventually multiplexed nonblocking socket I/O is utilized and the
threading model goes away.

> Why did you ever expect the unwind-protect form to continue its work
> after the thread where it runs is killed?

http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/speope_unwind-protect.html

That page implies the reading which I presented. Since I wasn't using a
non-local exit as specified in the definition of that term, I thought it
would work. It somewhat goes against sense that I could kill a thread and
yet somehow the unwind-protect cleanup form would still fire, but breaking
into the debugger and asking to abort the computation of a protected
form didn't seem that far away conceptually from destroying a thread.

> If you want to kill the thread and have an API for that, use it, and
> hope that the operating system does what is needed for you. I suspect
> you run out of file handles after a while, but what do I know.

The one thing you definitely have taught me is that threading in Common
Lisp is Hard. :) If I can't eagerly clean up non-memory resources perfectly
then I'll run out at some point...

But I think I know what to do now for my code and my explanation of it.

I would like to thank you for our conversation and I appreciate your time.

Later,
-pete

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

> Johan Ur Riise <johan(a)riise-data.no> wrote:
>>
>> Why did you ever expect the unwind-protect form to continue its work
>> after the thread where it runs is killed?
>
> http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/speope_unwind-protect.html
>
> That page implies the reading which I presented. Since I wasn't using a
> non-local exit as specified in the definition of that term, I thought it
> would work. It somewhat goes against sense that I could kill a thread and
> yet somehow the unwind-protect cleanup form would still fire, but breaking
> into the debugger and asking to abort the computation of a protected
> form didn't seem that far away conceptually from destroying a thread.

Destroying a thread is like someone coming into your computer room
with a big axe and destroying your machine. Two threads are like two
different programs, they just happen to share some data, not too much
different than two programs reading the same file, or two computers
downloading the same file from the internet, in this sense. If the
authors of the page you referred to thaught about threads at all when
they wrote it, I think they regarded it as self evident that the
cleanup form would have to execute in the same thread.

Maybe you want this: Keep a list of sockets and id of the thread where
each socket are used somewhere global, then after you kill a process
from outside, you find the sockets belonging to that thread and close
them. That is cleanup from another thread, but I do not know why this
would be better.