Sent mail to David personally; meant to include the list.
---------- Forwarded message ---------- From: Erik Huelsmann ehuels@gmail.com Date: Sat, Apr 24, 2010 at 9:37 AM Subject: Re: [armedbear-devel] threads, join, clos To: David Kirkman dkirkman@ucsd.edu
Hi David,
On Sat, Apr 24, 2010 at 6:52 AM, David Kirkman dkirkman@ucsd.edu wrote:
Hi,
Is there an equivalent of java.lang.Thread.join() on the lisp side of things? I looked around in LispThread.java and threads.lisp, but I don't see anything obvious. I'd like to be able to write something like:
(let ((threads (loop for i from 0 to 5 collect (make-thread #'(lambda() (format t "hey~%")))))) (mapc #'thread-join threads) (format t "is for horses~%"))
I've attached a patch that implements this version of thread-join. It implements java semantics, this thread-join always returns nil.
I guess we didn't join our threads until now :-) Thanks for taking the time to code it up.
I have one question regarding the patch though: How would the caller know the difference between the scenario where an Interrupted exception occurs and the one where the thread is really joined?
Should we use the return value NIL to signal "not joined" and T for "joined"?
Bye,
Erik.
On Sat, Apr 24, 2010 at 1:57 AM, Erik Huelsmann ehuels@gmail.com wrote:
I have one question regarding the patch though: How would the caller know the difference between the scenario where an Interrupted exception occurs and the one where the thread is really joined?
Should we use the return value NIL to signal "not joined" and T for "joined"?
Yes! Here is an updated patch that does that.
The other option for thread-join return values is to have it return the value of the function used in make-thread. That way thread-join will both wait for the thread to finish and collect it's value, and I don't have to set up global variables to collect the results. (Which is the way I always *want* join to work!)
I've included a second patch in which implements join that way. It also returns a second T/NIL value indicating if the thread finished normally or was interrupted. The only disadvantage to this is that in other languages join either returns nothing or a success/failure code.
Cheers,
-david
Hi David,
On Sat, Apr 24, 2010 at 10:58 PM, David Kirkman dkirkman@ucsd.edu wrote:
On Sat, Apr 24, 2010 at 1:57 AM, Erik Huelsmann ehuels@gmail.com wrote:
I have one question regarding the patch though: How would the caller know the difference between the scenario where an Interrupted exception occurs and the one where the thread is really joined?
Should we use the return value NIL to signal "not joined" and T for "joined"?
Yes! Here is an updated patch that does that.
The other option for thread-join return values is to have it return the value of the function used in make-thread. That way thread-join will both wait for the thread to finish and collect it's value, and I don't have to set up global variables to collect the results. (Which is the way I always *want* join to work!)
I've included a second patch in which implements join that way. It also returns a second T/NIL value indicating if the thread finished normally or was interrupted. The only disadvantage to this is that in other languages join either returns nothing or a success/failure code.
Thanks for your contribution! I committed the second variant in r12634.
I'll look at the threading issues you've raised in the other thread tomorrow. Bedtime now.
Bye,
Erik.
David Kirkman dkirkman@ucsd.edu writes:
On Sat, Apr 24, 2010 at 1:57 AM, Erik Huelsmann ehuels@gmail.com wrote:
I have one question regarding the patch though: How would the caller know the difference between the scenario where an Interrupted exception occurs and the one where the thread is really joined?
Should we use the return value NIL to signal "not joined" and T for "joined"?
Yes! Here is an updated patch that does that.
The other option for thread-join return values is to have it return the value of the function used in make-thread. That way thread-join will both wait for the thread to finish and collect it's value, and I don't have to set up global variables to collect the results. (Which is the way I always *want* join to work!)
I've included a second patch in which implements join that way. It also returns a second T/NIL value indicating if the thread finished normally or was interrupted. The only disadvantage to this is that in other languages join either returns nothing or a success/failure code.
A thread can return multiple values. Will those be returned in list form as primary return value?
SBCL returns the values as multiple values, and signals an error in case it couldn't join. I'm not sure what interface I'd like more. On SBCL, you pretty much always have to wrap HANDLER-CASE around join-thread which makes it unappropriate to use with MAPC.
At the project I'm currently working on, I added the following wrapper:
(defun join-thread (thread &key timeout (on-timeout :timeout) (on-failure :error)) ...)
The ON-TIMEOUT/FAILURE arguments are returned as primary results, making it potentially ambiguous (does :error come from the return value of the thread or because of a failure?); however the user can specify these values -- and the user should almost always know in what range a thread's return value is going to be -- so it's my humble opinion on how I think JOIN-THREAD should look like. :-)
-T.
On Sat, Apr 24, 2010 at 3:43 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
A thread can return multiple values. Will those be returned in list form as primary return value?
Well, in the original patch it just returned the first value, and the second value is the completed/interrupted flag. That's on obvious error, so I'm attaching a new patch that returns all of the values, plus the completed interrupted flag (for a function returning (values 1 2) thread-join will now return 1, 2, T)
SBCL returns the values as multiple values, and signals an error in case it couldn't join. I'm not sure what interface I'd like more. On SBCL, you pretty much always have to wrap HANDLER-CASE around join-thread which makes it unappropriate to use with MAPC.
At the project I'm currently working on, I added the following wrapper:
(defun join-thread (thread &key timeout (on-timeout :timeout) (on-failure :error)) ...)
The ON-TIMEOUT/FAILURE arguments are returned as primary results, making it potentially ambiguous (does :error come from the return value of the thread or because of a failure?); however the user can specify these values -- and the user should almost always know in what range a thread's return value is going to be -- so it's my humble opinion on how I think JOIN-THREAD should look like. :-)
I'm not committed to any particular semantics, other than I want thread-join to exist in some form! Naturally, I kind of like the attached patch :) (and I don't know how to get at keyword arguments in a java implemented primitive!).
Is there a proposed threading standard for common lisp, or a de facto standard?
Cheers
-david
David Kirkman dkirkman@ucsd.edu writes:
On Sat, Apr 24, 2010 at 3:43 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
A thread can return multiple values. Will those be returned in list form as primary return value?
Well, in the original patch it just returned the first value, and the second value is the completed/interrupted flag. That's on obvious error, so I'm attaching a new patch that returns all of the values, plus the completed interrupted flag (for a function returning (values 1 2) thread-join will now return 1, 2, T)
That strictly couples the caller with its return sites; pain that multiple values are usually supposed to obliterate from.
SBCL returns the values as multiple values, and signals an error in case it couldn't join. I'm not sure what interface I'd like more. On SBCL, you pretty much always have to wrap HANDLER-CASE around join-thread which makes it unappropriate to use with MAPC.
At the project I'm currently working on, I added the following wrapper:
(defun join-thread (thread &key timeout (on-timeout :timeout) (on-failure :error)) ...)
The ON-TIMEOUT/FAILURE arguments are returned as primary results, making it potentially ambiguous (does :error come from the return value of the thread or because of a failure?); however the user can specify these values -- and the user should almost always know in what range a thread's return value is going to be -- so it's my humble opinion on how I think JOIN-THREAD should look like. :-)
I'm not committed to any particular semantics, other than I want thread-join to exist in some form! Naturally, I kind of like the attached patch :) (and I don't know how to get at keyword arguments in a java implemented primitive!).
You'd have to parse the &rest args by hand. I suggest you implement a %join-thread in Java which only takes required arguments, and implement JOIN-THREAD in Lisp.
Is there a proposed threading standard for common lisp, or a de facto standard?
Not really. You can look at other implementation's documentation.
-T.
On Sat, Apr 24, 2010 at 5:21 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
Well, in the original patch it just returned the first value, and the second value is the completed/interrupted flag. That's on obvious error, so I'm attaching a new patch that returns all of the values, plus the completed interrupted flag (for a function returning (values 1 2) thread-join will now return 1, 2, T)
That strictly couples the caller with its return sites; pain that multiple values are usually supposed to obliterate from.
I think that the biggest problem with returning a status code at the end of multiple values is that if you want to write a wrapper to change the way the call operates (e.g. if you want to write a sb-thread-join that throws like the sbcl version) at some point you've got to convert the multiple values into a list, strip off the last element, and then convert the list back into multiple values -- which seems a bit inelegant.
SBCL returns the values as multiple values, and signals an error in case it couldn't join. I'm not sure what interface I'd like more. On SBCL, you pretty much always have to wrap HANDLER-CASE around join-thread which makes it unappropriate to use with MAPC.
At the project I'm currently working on, I added the following wrapper:
(defun join-thread (thread &key timeout (on-timeout :timeout) (on-failure :error)) ...)
The ON-TIMEOUT/FAILURE arguments are returned as primary results, making it potentially ambiguous (does :error come from the return value of the thread or because of a failure?); however the user can specify these values -- and the user should almost always know in what range a thread's return value is going to be -- so it's my humble opinion on how I think JOIN-THREAD should look like. :-)
This version looks very convenient to use most of the time. But it's not easy to write a generic wrapper to change the semantics without knowing the allowed return values of the underlying function. Or is it? (That's a real question!)
It could be made fully general if we add an interrupted flag to LispThread, and a function for querying it. I think that way thread-join is easy to use most of the time, but if there is uncertainty to if :error is an allowed return value you can just query the thread to see if it completed normally.
Whatever we do, we should have a timeout keyword.
I'm happy to change things, if we can agree what to change them to!
Cheers,
-david
David Kirkman dkirkman@ucsd.edu writes:
On Sat, Apr 24, 2010 at 5:21 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
Well, in the original patch it just returned the first value, and the second value is the completed/interrupted flag. That's on obvious error, so I'm attaching a new patch that returns all of the values, plus the completed interrupted flag (for a function returning (values 1 2) thread-join will now return 1, 2, T)
That strictly couples the caller with its return sites; pain that multiple values are usually supposed to obliterate from.
I think that the biggest problem with returning a status code at the end of multiple values is that if you want to write a wrapper to change the way the call operates (e.g. if you want to write a sb-thread-join that throws like the sbcl version) at some point you've got to convert the multiple values into a list, strip off the last element, and then convert the list back into multiple values -- which seems a bit inelegant.
Yes, and inefficient, at least with the MV tools provided by CL.
At the project I'm currently working on, I added the following wrapper:
(defun join-thread (thread &key timeout (on-timeout :timeout) (on-failure :error)) ...)
The ON-TIMEOUT/FAILURE arguments are returned as primary results, making it potentially ambiguous (does :error come from the return value of the thread or because of a failure?); however the user can specify these values -- and the user should almost always know in what range a thread's return value is going to be -- so it's my humble opinion on how I think JOIN-THREAD should look like. :-)
This version looks very convenient to use most of the time. But it's not easy to write a generic wrapper to change the semantics without knowing the allowed return values of the underlying function. Or is it? (That's a real question!)
My argument is in the passage you quoted. The API above is convenient without restricting flexibility.
It's a known trick, but perhaps it's new to you, so let me show you can use the above function to do regardless of knowing the return value(s) of the thread:
(let* ((timeout-dummy (list :timeout)) (failure-dummy (list :failure)) (result (multiple-value-list (join-thread thread :timeout <n> :on-timeout timeout-dummy :on-failure failure-dummy)))) (cond ((eq result timeout-dummy) ...handle timeout...) ((eq result failure-dummy) ...handle failure...) (t (values-list result)))) ; or frob however you want
To the quickly-skimming reader:
Please note that the above is the most general case, and not what you'll need most of the time. It's very unlikely that you want to join a thread without knowing anything about it.
To David:
The above works because LIST will create a new cons which is unique across the image.
The only debatable point I see is that perhaps :ON-TIMEOUT/FAILURE should be made such that JOIN-THREAD will return the ON-TIMEOUT/FAILURE argument as _secondary_ rather than primary value. This trades off the common case of a thread only wanting to return one value for a little bit added complexity for the general case depicted above.
It could be made fully general if we add an interrupted flag to LispThread, and a function for querying it. I think that way thread-join is easy to use most of the time, but if there is uncertainty to if :error is an allowed return value you can just query the thread to see if it completed normally.
So you'd have a THREAD-ABORTED-P function. Sure that'd do, too.
But then there's still the problem of how to indicate the timeout?
Whatever we do, we should have a timeout keyword.
I'm happy to change things, if we can agree what to change them to!
Someone from the ABCL team should decide. They can also document any API decision as experimental, thus reserving the right to modify it if the need should arise.
-T.
Please note that I see value in continuing this discussion, even though I have committed a version of the patch already: it's not been released yet and we have an opportunity to settle on the most-useable API now; changing later always causes pain.
Bye,
Erik.
On Sun, Apr 25, 2010 at 2:03 AM, David Kirkman dkirkman@ucsd.edu wrote:
On Sat, Apr 24, 2010 at 3:43 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
A thread can return multiple values. Will those be returned in list form as primary return value?
Well, in the original patch it just returned the first value, and the second value is the completed/interrupted flag. That's on obvious error, so I'm attaching a new patch that returns all of the values, plus the completed interrupted flag (for a function returning (values 1 2) thread-join will now return 1, 2, T)
SBCL returns the values as multiple values, and signals an error in case it couldn't join. I'm not sure what interface I'd like more. On SBCL, you pretty much always have to wrap HANDLER-CASE around join-thread which makes it unappropriate to use with MAPC.
At the project I'm currently working on, I added the following wrapper:
(defun join-thread (thread &key timeout (on-timeout :timeout) (on-failure :error)) ...)
The ON-TIMEOUT/FAILURE arguments are returned as primary results, making it potentially ambiguous (does :error come from the return value of the thread or because of a failure?); however the user can specify these values -- and the user should almost always know in what range a thread's return value is going to be -- so it's my humble opinion on how I think JOIN-THREAD should look like. :-)
I'm not committed to any particular semantics, other than I want thread-join to exist in some form! Naturally, I kind of like the attached patch :) (and I don't know how to get at keyword arguments in a java implemented primitive!).
Is there a proposed threading standard for common lisp, or a de facto standard?
Cheers
-david
armedbear-devel mailing list armedbear-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/armedbear-devel
armedbear-devel@common-lisp.net