From: Nikodemus S. <nik...@ra...> - 2007-03-28 12:36:46
|
James Y Knight wrote: > On Mar 27, 2007, at 8:42 AM, Brian Mastenbrook wrote: >> I think the interface which is needed here is: >> >> * A function to return the current deadline based on the current >> timer queue, >> * A function to trigger appropriate timeout actions when the deadline >> has passed, >> * A function to register a timeout, which is given an instance of a >> condition class to be signaled when the timeout expires >> * A function to cancel a timeout, which is given the condition class >> instance to find in the queue and cancel > Uggg. This sounds really overdesigned to me. What I'd like is simple: > timer support in the serve-event loop. I think that should cleanly cover > your UDP SIP server case as well. I confess I haven't thought about how this ties in with SERVE-EVENT yet. My first thought is that streams have timeouts of their own, which would then cause the even-loop to signal timeout. ...but considering how it is a recursive event-loop that makes me feel quite ill at ease. Need to think and see what I can implement. > Other than that, a timeout argument to various blocking functions is > useful, but I don't really see that they need such a sophisticated > support system to go along with them. One can always keep a global > variable *deadline* in user code and pass the appropriate timeout to > each function as you call it, no? An implicit failure-inducing global > like that just seems dangerous. I think I am pretty square in the middle between you two. The interface work I've done so far seems to speak strongly in favor of per-object default timeouts, per-call-site explicit timeouts, and a single global *DEADLINE*. I tried an interface similar to proposed by Brian, but it turned out that then writing a function exposing a timeout parameter to caller and respecting the global deadlines got quite hairy very quickly, and there would have been a whole lot of possible bignum computations going on. Here's a sketch of what I have in mind: ;;; Primary timeout user interface (defmacro with-timeout (seconds &body forms) `(let* ((*timeout* ,seconds) (*deadline* (min (+ (now) *timeout*) *deadline*))) ,@forms)) ;;; Primary timeout function writer interface (defun ensure-timeout (&optional default) (let ((default (if default (coerce default 'double-float) 0.0d0))) (if *deadline* (let ((timeout (- *deadline* (now)))) (unless (plusp timeout) (with-simple-restart (continue "Extend the deadline by ~A seconds." *timeout*) (error 'deadline :seconds *timeout*)) (setf timeout *timeout* *deadline* (+ (now) timeout))) (if (= default 0.0d0) timeout (min timeout default))) default))) ;;; A random timeout respecting function. (defun foo (thing &optional (timeout (ensure-timeout (thing-timeout thing)))) (loop (when (= +foreign-timeout+ (foreign-foo timeout)) (with-simple-restart (continue "Continue for ~A seconds more." timeout) (error 'simple-timeout :format-control "Timeout while doing FOO." :seconds timeout))))) This doesn't do nearly as much as Brian's proposal, but is simple to use for the common case. If you do have cases where you need to tell apart between different levels of timeout (time to die, time to abort connection, etc), then I suggest that you may be better off with timers. (Which would then fire off in a thread of their own in multithreaded builds, and during safe-points in unithreaded builds.) Cheers, -- Nikodemus |