-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support cancellation of timer operations #110
base: master
Are you sure you want to change the base?
Conversation
* fibers/operations.scm (<base-op>): Rename constructor to ‘%make-base-operation’. [cancel-fn]: New field. (make-base-operation, cancel-other-operations): New procedures. (perform-operation)[block]: Define ‘resume’ to call ‘cancel-other-operations’ because calling the real ‘resume’. * fibers.texi (Operations): Adjust accordingly.
* fibers/timer-wheel.scm (timer-wheel-remove!): New procedure.
Fixes wingo#109. Previously, an operation like: (choice-operation (sleep-operation 1234) (get-operation channel)) would accumulate timer wheel entries every time the ‘get’ operation wins over the ‘sleep’ operation, potentially leading to unbounded memory usage (each ‘sleep’ timer and its associated continuation would remain on the wheel for 1234 seconds in this case). This commit fixes it by removing the timer wheel entry as soon as the timer operation is canceled. * fibers/timers.scm (timer-operation)[wheel-entry]: New variable. Set it in block function. Add cancel function. * fibers/scheduler.scm (scheduler-timers): Export. * tests/cancel-timer.scm: New file. * Makefile.am (TESTS): Add it.
Looking good, I do have a question though and it's a long one. Basically, I want to ensure that Concurrent ML's
To explain In fibers, we have considered Now back to
And then the example goes like this (translated): (define (annotate-operation op nack-fn)
(withNack
(lambda (nack-op)
(spawn-fiber (lambda ()
(nack-fn (perform-operation nack-op))))
op))) The idea is that when you make a nack-event, your nack guard function will be called on a fresh event (operation), as if from a The fiber spawned by the nack guard function could then, say, send a message back to a remote server to release some kind of resource. The corner cases come in for
Here we want the nack condition to signal if op-a synchronizes, but not to signal if either of op-b or op-c synchronize. In https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=9393e5ba6fa5cdcd981fee71c3bdfee8841c047d §5.2, Donnely and Fluet show how to implement So, again, my questions:
Also, when we settle on the answer, can I request some documentation, please? Thank you :) |
Hey @wingo, I'm afraid I cannot answer your questions, this being my first time hearing about What I can say is that this change preserves If we cannot answer the question of whether cancellation functions would prevent Regarding documentation, the commit updates the Thanks for your detailed review! |
(put-message channel 'hello) | ||
(loop (+ i 1)))))) | ||
|
||
(let ((initial-heap-size (heap-size))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're checking heap sizes, best do a gc beforehand, otherwise some heavy activity could lead to false negatives (as in 'no bug detected even though it exists').
Would it be possible (and sufficiently informative & meaningful) to instead check the length of the timer wheel? Seems less finicky to me (e.g. what if in the future tests are run in parallel in a single process).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if something finicky like heap sizes is avoided, I imagine the number of iterations could be reduced a lot (good for test performance).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found that when #109 is present, the heap would grow way beyond the 2x limit that's tested here; it would not go unnoticed. (Maybe we could make the test faster but it was already reasonably fast in my experience.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How fast is 'reasonably fast'?
;; (sched) -> () | ||
(cancel-fn base-op-cancel-fn)) | ||
|
||
(define* (make-base-operation wrap-fn try-fn block-fn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For ABI reasons, there needs to be a way to tell to Guile 'don't inline this' (not blocking this PR).
I have just seen this issue and have only skimmed the paper you linked so far, but I wrote about the lack of In particular, I'd highlight § 7 of Flatt and Findler, “Kill-Safe Synchronization Abstractions” (PLDI 2004):
I haven't read Donnely and Fluet closely enough (or re-read the implementation of I wrote in #97 that I don't think The mention of the fact in Flatt and Findler that “continuation jumps back into a guard are always blocked by our definition of |
This patch series fixes #109 by supporting a "cancel" function for base operations and using it to remove timer wheel entries when a timer "loses" in a choice operation.
I'd like feedback in particular on the last commit: can someone confirm
set!
good enough, or should we use an atomic box instead?I ran the test suite of the Shepherd and that of Cuirass against this branch: the former has good coverage though it uses a single POSIX thread, the latter has not-so-good coverage but uses multiple POSIX threads. Both passed.
Thoughts?