From: Vladimir S. <vs...@gm...> - 2010-11-08 00:04:42
|
Looking at this some more, there's two places where a reference to the object to be finalized is returned: sb-ext:weak-pointer-value, and subsequently by sb-ext:finalize. I thought something like this would do the trick: (declaim (notinline finalize-our-thread)) (defun finalize-our-thread (ptr thread) (sb-ext:finalize (sb-ext:weak-pointer-value ptr) (lambda () (sb-thread:interrupt-thread thread (lambda () (throw 'done nil))))) nil) (defun weak-interrupt (ptr) (sb-thread:make-thread (lambda () (finalize-our-thread ptr sb-thread:*current-thread*) (catch 'done (sleep 1000000))))) (defvar thread8 (weak-interrupt (make-weak-pointer (make-instance 'foo)))) (sb-ext:gc :full t) (The goal here is to interrupt the sleeping thread btw, I probably explained that poorly). So the idea is that finalize-our-thread gets called, cleans up the stack after itself, and returns. I'm making three assumptions here - finalize-our-thread cleans up after itself on the stack, doesn't leave a pointer in a register, and doesn't keep intermediate values anywhere else but the stack or the registers. But it seems at least one of those assumptions is wrong, as the object isn't being collected while the thread is sleeping. Another thing is I'm on x86-32, so the conservative collector might be at fault. Would it help if I post the output of disassemble? (I can kind of read it but not understand what's going on) Thank you, Vladimir 2010/10/29 Nikodemus Siivola <nik...@ra...>: > On 29 October 2010 06:36, Vladimir Sedach <vs...@gm...> wrote: > > Wait, are you saying that > > (make-thread > (lambda () > (catch 'done > (finalize <obj> > (let ((thread *current-thread*)) > (lambda () > (sb-thread:interrupt-thread > thread (lambda () (throw 'done <computation>)))))) > (wait-forever)))) > > is the exact pattern you are using in your code? That you want to > delay a computation to be done by a thread till an object dies? > > There are couple of issues with this. > > Firstly, yes, you're going to have a very hard time making sure you > don't have life references to <OBJ> till that LAMBDA returns. > > If you're really want to try it: > > * (DEBUG 0) is going to be your friend (high debug settings increase > variable lifetimes). > > * You want to structure the code so that WAIT-FOREVER can be in a > tail-position, which it isn't here -- CATCH prevents that (as would > using BLOCK/RETURN-FROM and closing over the block.) Instead of > BLOCK/RETURN or CATCH/THROW you want WAIT-FOREVER to actually return > the value. (INTERRUPT-THREAD in production code is also always a bit > icky, IMO -- especially forcing a non-local exit using it.) Something > along the lines of > > (make-thread > (lambda () > (declare (optimize (debug 0))) > (let ((sem (sb-thread:make-semaphore))) > (finalize obj (lambda () (sb-thread:signal-semaphore sem))) > (wait-and-compute sem function)))) > > (defun wait-and-compute (sem function) > (sb-thread:wait-on-semaphore sem) > (funcall function)) > > might have a chance. If it still doesn't fire, you need to insert a > bit of frame-scrubbing between the FINALIZE and WAIT-AND-COMPUTE > calls. I think it should not be too hard to write a VOP > %NUKE-FRAME-REFS that takes a specific object and removes references > to it from the frame -- but it doesn't quite fit into this > metaphorical margin. > > Secondly, while a thread is cheap in comparison to a process, it's > still a pretty damn big object. Doing this is a rather expensive way > to defer computation. > > Cheers, > > -- Nikodemus > |