On 2 April 2010 12:45, Tobias C. Rittweiler <tcr@...> wrote:
>> > I'd vote for with-interrupts affecting deadlines and existing code be audited.
>> An audit does cover my concern I guess, along with an Incompatible
>> Change note in NEWS.
> An audit along what policy? I came to the realization that deadlines
> should be _signaled_ at places not only safe to unwind, but as reentrant
> as possible, too. In particular, this means deadlines should be signaled
> outside of critical sections.
> [To be precise: user's handler should be run outside of critical
> sections; the deadlines may be signaled no matter where as long as the
> signal is unwinded to safe place e.g. via a facility like your
> HANDLER-UNWIND idea.]
Yes. Though the audit I'm interested is about making sure that
"deadlines are respected", that is, we don't sit inside
> That's not enough for a policy like above. For that, it seems like it
> has to boil down to a HANDLER-CASE on SERIOUS-CONDITION (e.g. along your
> proposed modification of WAIT-ON-SEMAPHORE you showed me a few days
> ago.) I'm not sure how expensive that is.
Not so expensive these days, as most handler-case forms don't cons
anymore. Note that I'm not seriously considering HANDLER-UNWIND --
there's too much to go wrong with it, and this stuff is hard enough to
think about without adding magical unwinds.
I'm virtually convinced that deadline/timeout for a sychronization
construct should never be a condition, but expressed in the return
signature of the function / a special cleanup section for a macro. (I
suspect timeout and deadlines for IO can be conditions just fine.)
Below is a sketch of an alternative strategy form some time back:
Instead of WITH-TIMEOUT, use WITH-DEADLINE and explicit :TIMEOUT
In functions and macros accepting TIMEOUT arguments, the default is
always computed from the current deadline established using
WITH-DEADLINE, if any. If an explicit TIMEOUT argument is provided,
it overrides any previously estabilished deadline. TIMEOUT of NIL
means there is no timeout.
To write your own functions which provide a TIMEOUT argument, you
should follow one of the following patterns:
;;; Pattern A, where you need to use the timeout on just once.
(defun get-foo (foostore &key (timeout (deadline-seconds)))
;; Just pass it on.
(with-lock ((foostore-lock foostore) :timeout timeout)
(or (pop (foostrore-list foostore))
;;; Pattern B, where you need to use the timeout multiple times.
(defun frob-bar (bar &key (timeout (deadline-seconds)))
;; We can't use TIMEOUT multiple times, because we want the
;; _total_ wait of this function to be bounded by it.
;; Instead, we establish a new deadline using the timeout,
;; and let the defaulting mechanism recompute the correct timeout
;; each time they are needed.
(with-deadline (:seconds timeout :override t)
(loop with did-something
do (with-lock ((bar-lock bar))
(setf did-something (update-bar bar))
(return-from frob-bar nil)))
Returns number of seconds left till current deadline, NIL if there
is no current deadline.
macro WITH-DEADLINE &key seconds override
Estabilish a new deadline specified number of SECONDS in the future.
macro WITH-LOCK (lock &key timeout) locked-form &optional (:TIMEOUT
Waits to acquire lock, and then executes LOCKED-FORM, finally
releasing the lock. If a timeout occurs while waiting for the
lock, TIMEOUT-FORMs are executed instead.