From: <don...@is...> - 2009-02-25 06:14:25
|
Sam Steingold writes: > Don Cohen wrote: > > Is it possible to interrupt between the incf and the setf? > > If so, what is to be done about it? Defer interrupts around the setf ? > > Is there some reasonably easy way to understand/predict where > > interrupts can occur? > > clisp/doc/multithread.txt says: > > Hash Tables and Sequences ... > I.e., if you want to shoot yourself, it is YOUR responsibility to > wear armor. Other than the last line (your summary about the programmer's responsibility), I don't see a strong relationship between the problems of simultaneous access described in multithread.txt and skipping code after an interrupt. I still need some idea of where interrupts can occur in order to be able to protect against them. A few other questions still on the queue: - why is the term exemption appropriate, as opposed to something like thread-condition? - how can one (if at all) use the debugger to skip some unwind protect cleanup code? Perhaps the answer is that this can only be done by starting to execute the cleanup code and then (if there's time) interrupting it. It occurs to me that there ought to be two different abort options from the debugger, one that tries to finish the current cleanup that was interrupted and the other that does not. And there should be a way to set a breakpoint at the completion of the current cleanup. |
From: Vladimir T. <vtz...@gm...> - 2009-02-25 12:58:30
|
On Wed, 25 Feb 2009 00:58:13 +0200, Don Cohen <don...@is...> wrote: > > While looking at Vladimir's implementation of read/write locks > I begin to notice that there are many other places where returns > from interrupts cause problems. > (defun rwlock-read-lock (rwlock) > (with-lock ((rwlock-lock rwlock)) > (when (rwlock-active-writer rwlock) ; write in progress > (incf (rwlock-waiting-readers rwlock)) > (exemption-wait (rwlock-r-exemption rwlock) (rwlock-lock rwlock)) > (decf (rwlock-waiting-readers rwlock))) > (incf (rwlock-active-readers rwlock)))) As before - thread-interrupt and call-with-timeout are "dangenorous". Even in 7 lines of code that seem to behave well in normal flow of execution problems arise when mixed with async interrupts. And here you are "happy" with rather simple code/case. Many CL libraries will have similar issues (imagine inplace nreverse implementation being interrupted by timeout). > Is it possible to interrupt between the incf and the setf? Yes. Even in the middle of incf (not that it matters in this case). > If so, what is to be done about it? Defer interrupts around the setf ? I would not rely on any piece of data that has been changed by a form that was interrupted (which means I would avoid to modify "non-local state" from forms that can be interrupted). Of course any form can be evaluated with with-timeout - but it is responsibility of the user to enforce some hygiene. > All of this suggests that writing code that works correctly in the > presence of timeouts will be substantially harder to write than code > that does not. Yes. So best avoid timeouts and stick to mutexes/exemptions unless you can manage the side effects. (btw: (with-timeout (timeout nil) (mutex-lock mutex)) can be used for "wait with timeout" and is safe - interrupt will not leave it in some incosistent state). thread-interrupt is not so bad if non-local exits are not used. call-with-timeout/with-timeout does exactly this throws a tag. Until there is no with-deferred-interrupts - cleanup forms may not execute entirely. Even when this is implemented - there is no guarantee that it will finish in timely manner. with-deferred-interrupts may also help avoid interrupts between setf/incf/etc. Actually some implementation (SCL) do not provide with-timeout and try hardly to discourage use of thread-interrupt (http://www.scieneer.com/scl/doc/multi-processing.html). |
From: <don...@is...> - 2009-02-25 20:01:27
|
The summary seems to be that with-timeout was really only meant for a few very simple things like waiting for input. I thought I had an ideal use: a web request results in a DB query and the server tries to generate results. I want to stop after any of - all results generated - a maximum number of results generated - a maximum amount of time spent Since I'm only reading the database (the theory goes), I only had to worry about a small amount of data expected to be written inside the timeout and then read after, namely, the list of results. The solution was something like this: (let (results) (with-timeout (...) (... <generate> ... (push next-result results) )) <show results>) I'm assuming that push is atomic in the sense that no matter where it is interrupted, either the new result is completely on the list or completely off. Even if this were not the case I could still get the right effect by adding with-deferred-interrupts around it. The lesson from this discussion is that there may be a lot more stuff to worry about than one might at first expect. I might be able to ensure that all of MY code used to read the DB is appropriately protected by with-deferred-abort. That now seems to include the code to acquire a DB read lock. BTW, I don't like the name with-deferred-interrupts. I suggest that "timeout" condition be a subtype of "abort" condition and that abort conditions be deferred until the end of "with-deferred-abort". I also suggest that any attempt to abort (inc. from the debugger) from the inside of with-deferred-abort should cause a cerror. In fact, it would also make sense for with-deferred-abort to include some description of what data is endangered, to be used by the cerror. It seems that I still need some help from the lisp implementers in order to protect from aborts. I see two approaches. One is that the implementation should do the equivalent of with-deferred-abort internally in places where aborts could lead to data corruption. The other is that the programmer could be expected to do this, but then the documentation would have to provide enough information to allow him to do so. Of course, some combination is also possible. I generally prefer the former approach. My guess is that this is not an unreasonable run time expense, and that the cost in effort to the lisp impementers is not much more than is already being spent in other similar issues related to MT. (It seems possible that the work of finding the places that need such protection is already done, and that the programming effort is therefore minor.) Vladimir Tzankov writes: > And here you are "happy" with rather simple code/case. not sure what you mean by this > Many CL libraries will have similar issues (imagine inplace nreverse > implementation being interrupted by timeout). Clearly this is an example of something that should be abort protected. > thread-interrupt is not so bad if non-local exits are not used. > call-with-timeout/with-timeout does exactly this throws a tag. Until there > is no with-deferred-interrupts - cleanup forms may not execute entirely. > Even when this is implemented - there is no guarantee that it will finish > in timely manner. > with-deferred-interrupts may also help avoid interrupts between > setf/incf/etc. > > Actually some implementation (SCL) do not provide with-timeout and try > hardly to discourage use of thread-interrupt > (http://www.scieneer.com/scl/doc/multi-processing.html). I now view this as more general than a timeout or even an mt issue. It's due to the ability to abort from an arbitrary interrupt point. For non-local exits in normal code it seems reasonable to expect that the programmer understands the states in which the exit could be done and the states that could result. This seems unreasonable in the case of an interrupt or an error. So even in a lisp with no MT and no timeout, if you can type C-C and interrupt a computation, and the debugger offers an abort option, I think - you should be told if you're in a place where an abort could be expected to corrupt data (and which data) - you should have a way to finish the operation(s) that, if not finished might lead to data corruption and then either break again or abort. If you type (throw 'foo) to the debugger then I would not expect this to be treated as an abort operation. The reaction to a timeout inside a with-timeout clearly is an abort operation, as is an abort option in the debugger. It now occurs to me that there is a very large gray area of handled conditions. I already have lots of code that uses ignore-errors, and this seems subject to the same problems and arguments. I think that mostly this is used when some particular errors/conditions are expected, and it would be more help than harm for these to check for deferred abort. Clearly there should be a way to do either. |
From: <don...@is...> - 2009-02-26 22:21:44
|
Don Cohen writes: > Vladimir Tzankov writes: > > Many CL libraries will have similar issues (imagine inplace nreverse > > implementation being interrupted by timeout). > Clearly this is an example of something that should be abort protected. Just thought this might interest you: [1]> (setf *print-circle* t) T [2]> (setf l (list 1 2 3)) (1 2 3) [3]> (setf (cdr (last l)) l) #1=(1 2 3 . #1#) [4]> (nreverse l) #1=(2 . #1#) I was surprised that this finished. [5]> (reverse l) This hangs, so I try to interrupt it. ^C Signal 6 while exiting on a signal; cleanup may be incomplete Aborted [2009-02-26 14:08:43 root@number11 ~/clisp/build-dir] It seems clear to me that all such functions like nreverse or reverse that could take arbitrarily long ought to be interruptible. Of course, they should also use something like with-deferred-abort to warn the user that an abort will leave some data in an unexpected state. On the subject of the interpreter, I view this as similar to reverse in the sense that it's a limited amount of code, and should contain checks for interrupts at places that guarantee that it cannot ignore an interrupt request for very long. I'd think that the beginning of a form would be adequate. That would include (go ...). And in that case, perhaps it would not even need to defer aborts. |
From: Vladimir T. <vtz...@gm...> - 2009-02-26 22:28:04
|
On Fri, 27 Feb 2009 00:21:46 +0200, Don Cohen <don...@is...> wrote: > Don Cohen writes: > > Vladimir Tzankov writes: > > > Many CL libraries will have similar issues (imagine inplace > nreverse > > > implementation being interrupted by timeout). > > Clearly this is an example of something that should be abort > protected. > Just thought this might interest you: > [1]> (setf *print-circle* t) > T > [2]> (setf l (list 1 2 3)) > (1 2 3) > [3]> (setf (cdr (last l)) l) > #1=(1 2 3 . #1#) > [4]> (nreverse l) > #1=(2 . #1#) > I was surprised that this finished. > [5]> (reverse l) > This hangs, so I try to interrupt it. > ^C Yes - there are functions in C code that need safe points. |
From: Sam S. <sd...@gn...> - 2009-02-25 18:54:24
|
Vladimir Tzankov wrote: > (btw: (with-timeout (timeout nil) (mutex-lock mutex)) can be used for > "wait with timeout" and is safe - interrupt will not leave it in some > incosistent state). maybe mutex-lock should take a timeout argument? > Actually some implementation (SCL) do not provide with-timeout and try maybe we should treat every use of with-timeout as a missing feature in clisp? > hardly to discourage use of thread-interrupt and they are right! |
From: Vladimir T. <vtz...@gm...> - 2009-02-26 08:03:55
|
On Wed, 25 Feb 2009 20:54:19 +0200, Sam Steingold <sd...@gn...> wrote: > Vladimir Tzankov wrote: >> (btw: (with-timeout (timeout nil) (mutex-lock mutex)) can be used for >> "wait with timeout" and is safe - interrupt will not leave it in some >> incosistent state). > > maybe mutex-lock should take a timeout argument? POSIX mutexes do not have "timed wait" operation. So the above is the only way to wait for certain time on in. Of course we can do the above in a wrapper >> Actually some implementation (SCL) do not provide with-timeout and try > > maybe we should treat every use of with-timeout as a missing feature in > clisp? I think it is useful in limited number (but no so uncommon) problems. We may not export it from MT and warn against it's usage. > >> hardly to discourage use of thread-interrupt > > and they are right! At least this is the only way to kill thread nicely. The user should be careful when the interrupt function may cause stack unwinding. |
From: Sam S. <sd...@gn...> - 2009-02-25 21:15:58
|
Don Cohen wrote: > Since I'm only reading the database (the theory goes), I only had to > worry about a small amount of data expected to be written inside the > timeout and then read after, namely, the list of results. > The solution was something like this: > (let (results) > (with-timeout (...) > (... <generate> ... (push next-result results) )) > <show results>) > I'm assuming that push is atomic in the sense that no matter where it > is interrupted, either the new result is completely on the list or > completely off. Even if this were not the case I could still get the > right effect by adding with-deferred-interrupts around it. > > The lesson from this discussion is that there may be a lot more stuff > to worry about than one might at first expect. I might be able to > ensure that all of MY code used to read the DB is appropriately > protected by with-deferred-abort. That now seems to include the code > to acquire a DB read lock. the lesson, IMO, is that you should think carefully which operation you want to time out, and put with-timeout around them, not around the whole thing. then think carefully whether there is a way to use select(2) (i.e., socket-status) or something similar instead of with-timeout. (IMNSHO, the above paragraph, suitably expanded and clarified, should be in the doc string of with-timeout and in the impnotes). |
From: <don...@is...> - 2009-02-25 23:45:07
|
Sam Steingold writes: > Don Cohen wrote: > > Since I'm only reading the database (the theory goes), I only had to > > worry about a small amount of data expected to be written inside the > > timeout and then read after, namely, the list of results. > > The solution was something like this: > > (let (results) > > (with-timeout (...) > > (... <generate> ... (push next-result results) )) > > <show results>) > the lesson, IMO, is that you should think carefully which operation > you want to time out, and put with-timeout around them, not around > the whole thing. then think carefully whether there is a way to > use select(2) (i.e., socket-status) or something similar instead of > with-timeout. In the example above, I don't think that select will help, and I don't see that the scope of the timeout can be reduced. It is exactly this operation of generating results that I want to time limit. I don't want to time limit the generation of any particular result, and even if I did, this would not help with the particular problem of aborting write operations that ought to have transactional semantics. In the particular case of the read lock, it happens that recursive read locking is treated as a noop, so it would actually solve the problem to do (withreadaccess (let (results) ...)) But I'm gradually starting to think of other data that may be written by what I think of as essentially a read operation. I wouldn't be surprised if there were analogous things going on inside the lisp implementation. As an example, there's probably some caching going on in clos, and I wouldn't be surprised if interrupting and returning from some place could leave the wrong data in this cache. I don't claim it's hard to write such code in a way that is immune to such problems, but this was probably not on the mind of the person who wrote that code originally. And clearly the programmer can't be expected to think about that data. > (IMNSHO, the above paragraph, suitably expanded and clarified, > should be in the doc string of with-timeout and in the impnotes). What do you think of my argument that this issue applies more widely than timeouts or MT ? |
From: <don...@is...> - 2009-02-26 19:34:20
|
Vladimir Tzankov writes: > Currently GC safe spots are (new may be added if needed): > 1. Any heap allocation > 2. Any JMP bytecode instruction (this includes all variants of JMP > instruction - JMPIF, JMPIF1, etc). This is too strong - but seems it does > not cause noticeable performance decrease. (If you only wanted to avoid infinite loops you could limit the checks to backward jumps.) > 3. GO spec form in interpreted code > 4. While the execution is in marked as "possibly blocking" foreign > non-lisp code (read from socket for example). This is substantially less than I thought. I had imagined that, e.g., the beginning of every byte code instruction was a potential interrupt point, since there is probably a well defined invariant that is expected to hold at that point, which is not required while in the middle of a bytecode instruction. I'm also surprised to see #3, since I expected that intepreted code would be interpreted by some compiled code that would follow the rules of interruptibility simply as a consequence of being compiled code. This suggests to me that what's really going on here is that you check for interrupt requests at these points, and the check has enough cost that you want to minimize it. Clearly you don't want to allow infinite computation that never check, but you probably also don't want to allow very long ones. Sam had said that he thought an interrupt could occur between the end of (f) and setf in (setf x (f)), but I don't see that listed above. Now that I think of it, I'd have expected function call to be on your list. It looks like an infinite recursion cannot be interrupted until it runs out of stack. And there could be very long sequences of function calls that don't allow interrupts. Perhaps the compiler should even insert interrupt checks in long sequences of byte code instructions that don't do any checking. > > More to the point, how does one look at a piece of lisp code and > > determine in which states it could interrupt? > > you cannot (and should not rely on this - since in future safe spots may > change). I guess this means that I have to assume the worst if I want my code to work in this indefinite future. I worry that this will (or at least should) lead to a very large number of with-abort-deferred's. |
From: Sam S. <sd...@gn...> - 2009-02-26 20:07:34
|
Don Cohen wrote: > Sam had said that he thought an interrupt could occur between the end > of (f) and setf in (setf x (f)), but I don't see that listed above. what I said: yes in interpreted code, no in compiled code. (and I am no longer sure about the interpreted code). |
From: Sam S. <sd...@gn...> - 2009-02-26 20:03:50
|
Don Cohen wrote: > Vladimir Tzankov writes: > > Currently GC safe spots are (new may be added if needed): > > 1. Any heap allocation > > 2. Any JMP bytecode instruction (this includes all variants of JMP > > instruction - JMPIF, JMPIF1, etc). This is too strong - but seems it does > > not cause noticeable performance decrease. > (If you only wanted to avoid infinite loops you could limit the checks > to backward jumps.) is is probably cheaper to always get a spinlock than to figure out whether the jump is backward |
From: Vladimir T. <vtz...@gm...> - 2009-02-26 20:22:56
|
On Thu, 26 Feb 2009 21:34:16 +0200, Don Cohen <don...@is...> wrote: > Vladimir Tzankov writes: > > > Currently GC safe spots are (new may be added if needed): > > 1. Any heap allocation > > 2. Any JMP bytecode instruction (this includes all variants of JMP > > instruction - JMPIF, JMPIF1, etc). This is too strong - but seems it > does > > not cause noticeable performance decrease. > (If you only wanted to avoid infinite loops you could limit the checks > to backward jumps.) > > > 3. GO spec form in interpreted code > > 4. While the execution is in marked as "possibly blocking" foreign > > non-lisp code (read from socket for example). > > This is substantially less than I thought. > > I had imagined that, e.g., the beginning of every byte code > instruction was a potential interrupt point, since there is > probably a well defined invariant that is expected to hold at > that point, which is not required while in the middle of a bytecode > instruction. I have not tested it - but it will be much slower I think. > I'm also surprised to see #3, since I expected that intepreted code > would be interpreted by some compiled code that would follow the rules > of interruptibility simply as a consequence of being compiled code. [10]> (macroexpand '(loop)) (BLOCK NIL (TAGBODY #:LOOP-3150 (GO #:LOOP-3150))) ; T In the above there is no compiled bytecode (am I wrong, Sam)? There is problem with current JMP safe point in bytecode interpreter. If I do not find a solution for it - I'll revert it. mv_space should be preserved in some cases - but sometimes contains bogus values (already invalid becasue of GC). These values are never going to be used and if we store them on the stack - GC damages the heap. Not sure after which instructions mv_space should be preserved. > Sam had said that he thought an interrupt could occur between the end > of (f) and setf in (setf x (f)), but I don't see that listed above. It looks that it is not possible to be interrupted but do not rely on it. Some implementations provide atomic-setf for some places. > Now that I think of it, I'd have expected function call to be on your > list. It looks like an infinite recursion cannot be interrupted until > it runs out of stack. And there could be very long sequences of > function calls that don't allow interrupts. Perhaps the compiler > should even insert interrupt checks in long sequences of byte code > instructions that don't do any checking. Yes you are right (besides tail recursion that - tat will go through JMPTAIL). Sam which is a good place to add safe point? with_saved_context() ? > I guess this means that I have to assume the worst if I want my code > to work in this indefinite future. I worry that this will (or at > least should) lead to a very large number of with-abort-deferred's. Why? you will need with-abort-deferred only if you expect to be interrupted asynchronously. If all you use are mutexes/exemptions - I do not see why you would want to guard against them (or on just few places). |
From: <don...@is...> - 2009-02-26 20:55:34
|
Sam Steingold writes: > Don Cohen wrote: > > (If you only wanted to avoid infinite loops you could limit the checks > > to backward jumps.) > is is probably cheaper to always get a spinlock than to figure out > whether the jump is backward I don't know what the spin lock has to do with this. I was imagining that you could tell at compile time whether a jump was forward or backward and compile into either a forward jump that does not check or a backward jump that does. Vladimir Tzankov writes: > I have not tested it - but it will be much slower I think. Ok, but it sounds like you agree with me that between any two byte code instructions should be a safe place to gc or interrupt. > > I'm also surprised to see #3, since I expected that intepreted code > > would be interpreted by some compiled code that would follow the rules > > of interruptibility simply as a consequence of being compiled code. > [10]> (macroexpand '(loop)) > (BLOCK NIL (TAGBODY #:LOOP-3150 (GO #:LOOP-3150))) ; > In the above there is no compiled bytecode (am I wrong, Sam)? I'm missing something here. How is that code executed? Is the interpreter written in c or in lisp? If in lisp then when you execute this you're executing byte codes, right? > There is problem with current JMP safe point in bytecode interpreter. (I don't understand any of this - I guess it was meant for others.) > > I guess this means that I have to assume the worst if I want my code > > to work in this indefinite future. I worry that this will (or at > > least should) lead to a very large number of with-abort-deferred's. > > Why? you will need with-abort-deferred only if you expect to be > interrupted asynchronously. Isn't this always the case? Can't you always be interrupted and aren't all interrupts asynchronous? > If all you use are mutexes/exemptions - I do > not see why you would want to guard against them (or on just few places). I don't see why one would expect the code to get read access should be different in this respect from lots of other code. |
From: Vladimir T. <vtz...@gm...> - 2009-02-26 21:25:48
|
On Thu, 26 Feb 2009 22:55:28 +0200, Don Cohen <don...@is...> wrote: > Vladimir Tzankov writes: > > > I have not tested it - but it will be much slower I think. > Ok, but it sounds like you agree with me that between any two byte > code instructions should be a safe place to gc or interrupt. No - not between any two. If you want to be able to interrupt between any two instructions - all the intermedite data should be stored in GC safe locations (stack). > > > I'm also surprised to see #3, since I expected that intepreted code > > > would be interpreted by some compiled code that would follow the > rules > > > of interruptibility simply as a consequence of being compiled code. > > [10]> (macroexpand '(loop)) > > (BLOCK NIL (TAGBODY #:LOOP-3150 (GO #:LOOP-3150))) ; > > In the above there is no compiled bytecode (am I wrong, Sam)? > I'm missing something here. > How is that code executed? > Is the interpreter written in c or in lisp? In C. > > Why? you will need with-abort-deferred only if you expect to be > > interrupted asynchronously. > Isn't this always the case? Can't you always be interrupted and > aren't all interrupts asynchronous? CTRL-C is cerror - so you will be able to continue/fix manually. If you avoid thread-interrupt and call-with-timeout - you may not need deferred aborts so much. |
From: Sam S. <sd...@gn...> - 2009-02-26 21:41:35
|
Vladimir Tzankov wrote: > > [10]> (macroexpand '(loop)) > (BLOCK NIL (TAGBODY #:LOOP-3150 (GO #:LOOP-3150))) ; > T > > In the above there is no compiled bytecode (am I wrong, Sam)? you are right. it is easy to check: run under gdb and set a breakpoint in interpret_bytecode_. > There is problem with current JMP safe point in bytecode interpreter. If I > do not find a solution for it - I'll revert it. mv_space should be > preserved in some cases - but sometimes contains bogus values (already > invalid becasue of GC). These values are never going to be used and if we > store them on the stack - GC damages the heap. Not sure after which > instructions mv_space should be preserved. shouldn't you be able to figure this out from the value of mv_count? (instructions which invalidate mv_space should reset it to 0) I think this is documented in http://clisp.cons.org/impnotes/bytecode.html, look for "values undefined"... > Sam which is a good place to add safe point? with_saved_context() ? probably. |
From: Vladimir T. <vtz...@gm...> - 2009-02-27 13:52:18
|
On Thu, 26 Feb 2009 23:41:30 +0200, Sam Steingold <sd...@gn...> wrote: > Vladimir Tzankov wrote: >> There is problem with current JMP safe point in bytecode interpreter. >> If I do not find a solution for it - I'll revert it. mv_space should >> be preserved in some cases - but sometimes contains bogus values >> (already invalid becasue of GC). These values are never going to be >> used and if we store them on the stack - GC damages the heap. Not sure >> after which instructions mv_space should be preserved. > > shouldn't you be able to figure this out from the value of mv_count? > (instructions which invalidate mv_space should reset it to 0) > I think this is documented in > http://clisp.cons.org/impnotes/bytecode.html, look for "values > undefined"... Yes, exactly. I removed the safe points at JMP instructions (currently it damages the heap in certain cases). Soon will add them again properly together with ones for function calls. |
From: Sam S. <sd...@gn...> - 2009-02-26 22:39:20
|
Don Cohen wrote: > > It seems clear to me that all such functions like nreverse or reverse > that could take arbitrarily long ought to be interruptible. maybe some other functions should be, but reverse takes long only on circular lists, so if it takes long - it's a bug. |
From: <don...@is...> - 2009-02-26 22:48:54
|
Sam Steingold writes: > > It seems clear to me that all such functions like nreverse or reverse > > that could take arbitrarily long ought to be interruptible. > maybe some other functions should be, but reverse takes long only > on circular lists, so if it takes long - it's a bug. It's mostly progams with bugs that people want to interrupt. And especially those where the bugs result in infinite loops. |
From: Sam S. <sd...@gn...> - 2009-02-25 18:48:36
|
Don Cohen wrote: > > Other than the last line (your summary about the programmer's > responsibility), I don't see a strong relationship between the > problems of simultaneous access described in multithread.txt and > skipping code after an interrupt. > I still need some idea of where interrupts can occur in order to be > able to protect against them. Sorry, I misread your question. interrupt can occur at any gc-safe spot. in interpreted (setq x (f)) the return value of F can be lost to an interrupt. in compiled code it cannot (IIUC). > A few other questions still on the queue: > - why is the term exemption appropriate, as opposed to something like > thread-condition? in CL, conditions have a clear meaning, different from the pthread condition variables. I decided that exemption a good word with a somewhat similar meaning. what would you like to replace it with? prerequisite? requirement? qualification? > - how can one (if at all) use the debugger to skip some unwind protect > cleanup code? Perhaps the answer is that this can only be done by > starting to execute the cleanup code and then (if there's time) > interrupting it. > It occurs to me that there ought to be two different abort options > from the debugger, one that tries to finish the current cleanup > that was interrupted and the other that does not. And there should be > a way to set a breakpoint at the completion of the current cleanup. I think you are trying to subvert the notion of a cleanup form :-) |
From: <don...@is...> - 2009-02-26 08:27:49
|
Sam Steingold writes: > interrupt can occur at any gc-safe spot. Then which spots are gc safe? More to the point, how does one look at a piece of lisp code and determine in which states it could interrupt? > > - why is the term exemption appropriate, as opposed to something like > > thread-condition? > in CL, conditions have a clear meaning, different from the pthread condition > variables. > I decided that exemption a good word with a somewhat similar meaning. So far I don't see why the meaning is similar. I don't know what these things have been called in what other places except that they seem to be called conditions in the pthread doc. Are they are related to the "gates" here? http://www.franz.com/support/documentation/8.1/doc/multiprocessing.htm#gates-1 > prerequisite? > requirement? > qualification? So far I don't see why any of these names is appropriate. Nor does the doc I've seen seem to adequately explain either the names or the facilities themselves. If you can offer an explanation ... > > - how can one (if at all) use the debugger to skip some unwind protect > > cleanup code? Perhaps the answer is that this can only be done by > > starting to execute the cleanup code and then (if there's time) > > interrupting it. > > It occurs to me that there ought to be two different abort options > > from the debugger, one that tries to finish the current cleanup > > that was interrupted and the other that does not. And there should be > > a way to set a breakpoint at the completion of the current cleanup. > > I think you are trying to subvert the notion of a cleanup form :-) If the cleanup form contains an infinite loop should you be able to get out of it? |
From: Vladimir T. <vtz...@gm...> - 2009-02-26 09:07:47
|
On Thu, 26 Feb 2009 10:28:00 +0200, Don Cohen <don...@is...> wrote: > Sam Steingold writes: > > interrupt can occur at any gc-safe spot. > Then which spots are gc safe? Currently GC safe spots are (new may be added if needed): 1. Any heap allocation 2. Any JMP bytecode instruction (this includes all variants of JMP instruction - JMPIF, JMPIF1, etc). This is too strong - but seems it does not cause noticeable performance decrease. 3. GO spec form in interpreted code 4. While the execution is in marked as "possibly blocking" foreign non-lisp code (read from socket for example). > More to the point, how does one look at a piece of lisp code and > determine in which states it could interrupt? you cannot (and should not rely on this - since in future safe spots may change). |