From: <don...@is...> - 2017-12-25 08:34:18
|
I'm trying to expand my use of MT, inc. with-timeout, and so want to understand better where interrupts can occur. I'm imagining that the following idiom should work: (catch 'timeout (with-timeout (10 (throw 'timeout)) (do-stuff))) There should be no problem if do-stuff reads data, or writes data that does not survive the call to do-stuff. So I'm mainly worrying about writing data that will survive the possibly interrupted call. One kind of data I want to write is the result of whatever was found in the 10 seconds allowed, e.g., (let (results) (catch 'timeout (with-timeout (10 (throw 'timeout)) (do-stuff))) where do-stuff includes things like (push newresult results) I'm expecting the (setf results) par of this will either be done completely or not done at all no matter where an interrupt occurs. (I'm actually viewing it as an atomic write into memory.) Am I ok so far? I'd like the same to be true of any setf of a single place, such as writing into a hash table. I'm hoping that this is similarly atomic in the sense that the table remains in a valid state after an interrupt and throw, either the state it was in before the (setf gethash) or the state that would be expected after the (setf gethash). Is that true in the current implementation? If not, does it solve the problem to put the setf inside with-deferred-interrupts ? (Is there some way I should have known the answer from the doc?) Note that I'm already protecting that hash table from being written by more than one thread at a time. What does preemption mean in this doc? MT:WITH-DEFERRED-INTERRUPTS &BODY body) Defer thread interrupts (but not thread preemption) Does that mean that the OS can stop running this thread at a point where the table is not in one of those two valid states, and that either the thread might never be resumed (and the table might end up in an undesirable state) or later when the thread is resumed it might be interrupted in an undesirable state? Impnotes contains a very useful warning pointing out that interrupts can occur in cleanup forms. I suggest that be changed -- almost all cleanup forms you really want to complete. This is especially true for functions like with-open-file that I imagine use unwind-protect internally and the user has no way to add protection to the cleanup forms (other than protecting the entire with-open-file). Is it already the case that such internal cleanup forms are protected from interrupts? Is it even possible to protect (only) the cleanup forms with deferred-interrupts? (unwind-protect (foo) (with-deferred-interrupts (bar))) could still interrupt just before (with-deferred-interrupts (bar)), right? Or is that not one of the places GC could occur? I'm guessing that even without worrying about cleanup forms, something like (with-open-file (f filename :direction :output)(print x f)) could be interrupted during the print, and if aborted, could end up with pretty much any part of x in the file. So generally these forms will require some sort of unwind protect that tests whether the file is expected to be in a satisfactory state and tries to recover if not. It would be nice to provide a variant of with-open-file for the common case that checks whether the stuff inside with-open-file was completed, and if not deletes the file (if it was created) or resets the file length (if it was appended) or restores the previous version (if a new version was created). The section on GC suggests that a loop that never conses or makes system calls might be uninterruptible. Is this true? I hope not. Would it make sense to make any backward jump or function call interruptible? BTW, it would be really nice to use threads to do GC while other threads continue to run. What's the prospect of that? Sorry this message is so long and wide ranging. I'll be happy to get answers or discussion on small subsets of the questions here. |