1 ;;;; a timer facility based heavily on the timer package by Zach Beane
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
12 (in-package "SB!IMPL")
14 ;;; Heap (for the priority queue)
16 (declaim (inline heap-parent heap-left heap-right))
18 (defun heap-parent (i)
27 (defun heapify (heap start &key (key #'identity) (test #'>=))
28 (declare (function key test))
29 (flet ((key (obj) (funcall key obj))
30 (ge (i j) (funcall test i j)))
31 (let ((l (heap-left start))
32 (r (heap-right start))
35 (setf largest (if (and (< l size)
36 (not (ge (key (aref heap start))
37 (key (aref heap l)))))
41 (not (ge (key (aref heap largest))
42 (key (aref heap r)))))
44 (when (/= largest start)
45 (rotatef (aref heap largest) (aref heap start))
46 (heapify heap largest :key key :test test)))
49 (defun heap-insert (heap new-item &key (key #'identity) (test #'>=))
50 (declare (function key test))
51 (flet ((key (obj) (funcall key obj))
52 (ge (i j) (funcall test i j)))
53 (vector-push-extend nil heap)
54 (loop for i = (1- (length heap)) then parent-i
55 for parent-i = (heap-parent i)
57 (not (ge (key (aref heap parent-i))
59 do (setf (aref heap i) (aref heap parent-i))
60 finally (setf (aref heap i) new-item)
61 (return-from heap-insert i))))
63 (defun heap-maximum (heap)
64 (unless (zerop (length heap))
67 (defun heap-extract (heap i &key (key #'identity) (test #'>=))
68 (when (< (length heap) i)
69 (error "Heap underflow"))
72 (setf (aref heap i) (aref heap (1- (length heap))))
73 (decf (fill-pointer heap))
74 (heapify heap i :key key :test test)))
76 (defun heap-extract-maximum (heap &key (key #'identity) (test #'>=))
77 (heap-extract heap 0 :key key :test test))
81 (defstruct (priority-queue
83 (:constructor %make-priority-queue))
87 (defun make-priority-queue (&key (key #'identity) (element-type t))
88 (let ((contents (make-array 100
91 :element-type element-type)))
92 (%make-priority-queue :keyfun key
95 (def!method print-object ((object priority-queue) stream)
96 (print-unreadable-object (object stream :type t :identity t)
97 (format stream "~[empty~:;~:*~D item~:P~]"
98 (length (%pqueue-contents object)))))
100 (defun priority-queue-maximum (priority-queue)
101 "Return the item in PRIORITY-QUEUE with the largest key."
102 (symbol-macrolet ((contents (%pqueue-contents priority-queue)))
103 (unless (zerop (length contents))
104 (heap-maximum contents))))
106 (defun priority-queue-extract-maximum (priority-queue)
107 "Remove and return the item in PRIORITY-QUEUE with the largest key."
108 (symbol-macrolet ((contents (%pqueue-contents priority-queue))
109 (keyfun (%pqueue-keyfun priority-queue)))
110 (unless (zerop (length contents))
111 (heap-extract-maximum contents :key keyfun :test #'<=))))
113 (defun priority-queue-insert (priority-queue new-item)
114 "Add NEW-ITEM to PRIOIRITY-QUEUE."
115 (symbol-macrolet ((contents (%pqueue-contents priority-queue))
116 (keyfun (%pqueue-keyfun priority-queue)))
117 (heap-insert contents new-item :key keyfun :test #'<=)))
119 (defun priority-queue-empty-p (priority-queue)
120 (zerop (length (%pqueue-contents priority-queue))))
122 (defun priority-queue-remove (priority-queue item &key (test #'eq))
123 "Remove and return ITEM from PRIORITY-QUEUE."
124 (symbol-macrolet ((contents (%pqueue-contents priority-queue))
125 (keyfun (%pqueue-keyfun priority-queue)))
126 (let ((i (position item contents :test test)))
128 (heap-extract contents i :key keyfun :test #'<=)
133 (defun make-cancellable-interruptor (function)
134 ;; return a list of two functions: one that does the same as
135 ;; FUNCTION until the other is called, from when it does nothing.
136 (let ((mutex (sb!thread:make-mutex))
139 (declare (ignore mutex))
142 (sb!thread:with-recursive-lock (mutex)
144 (funcall function))))
146 (sb!thread:with-recursive-lock (mutex)
147 (setq cancelled-p t))))))
153 (:constructor %make-timer))
155 "Timer type. Do not rely on timers being structs as it may change in
161 (thread nil :type (or sb!thread:thread (member t nil)))
165 (def!method print-object ((timer timer) stream)
166 (let ((name (%timer-name timer)))
168 (print-unreadable-object (timer stream :type t :identity t)
170 (print-unreadable-object (timer stream :type t :identity t)
171 ;; body is empty => there is only one space between type and
175 (defun make-timer (function &key name (thread sb!thread:*current-thread*))
177 "Create a timer object that's when scheduled runs FUNCTION. If
178 THREAD is a thread then that thread is to be interrupted with
179 FUNCTION. If THREAD is T then a new thread is created each timer
180 FUNCTION is run. If THREAD is NIL then FUNCTION can be run in any
182 (%make-timer :name name :function function :thread thread))
184 (defun timer-name (timer)
186 "Return the name of TIMER."
189 (defun timer-scheduled-p (timer &key (delta 0))
191 "See if TIMER will still need to be triggered after DELTA seconds
192 from now. For timers with a repeat interval it returns true."
193 (symbol-macrolet ((expire-time (%timer-expire-time timer))
194 (repeat-interval (%timer-repeat-interval timer)))
195 (or (and repeat-interval (plusp repeat-interval))
197 (<= (+ (get-internal-real-time) delta)
202 (defvar *scheduler-lock* (sb!thread:make-mutex :name "Scheduler lock"))
204 (defmacro with-scheduler-lock ((&optional) &body body)
205 ;; don't let the SIGALRM handler mess things up
206 `(sb!sys:without-interrupts
207 (sb!thread:with-mutex (*scheduler-lock*)
210 (defun under-scheduler-lock-p ()
214 (eq sb!thread:*current-thread* (sb!thread:mutex-value *scheduler-lock*)))
216 (defparameter *schedule* (make-priority-queue :key #'%timer-expire-time))
218 (defun peek-schedule ()
219 (priority-queue-maximum *schedule*))
221 (defun time-left (timer)
222 (- (%timer-expire-time timer) (get-internal-real-time)))
224 ;;; real time conversion
226 (defun delta->real (delta)
227 (floor (* delta internal-time-units-per-second)))
231 (defun %schedule-timer (timer)
232 (let ((changed-p nil)
233 (old-position (priority-queue-remove *schedule* timer)))
234 ;; Make sure interruptors are cancelled even if this timer was
235 ;; scheduled again since our last attempt.
237 (funcall (%timer-cancel-function timer)))
238 (when (eql 0 old-position)
240 (when (zerop (priority-queue-insert *schedule* timer))
242 (setf (values (%timer-interrupt-function timer)
243 (%timer-cancel-function timer))
244 (values-list (make-cancellable-interruptor
245 (%timer-function timer))))
250 (defun schedule-timer (timer time &key repeat-interval absolute-p)
252 "Schedule TIMER to be triggered at TIME. If ABSOLUTE-P then TIME is
253 universal time, but non-integral values are also allowed, else TIME is
254 measured as the number of seconds from the current time. If
255 REPEAT-INTERVAL is given, TIMER is automatically rescheduled upon
257 ;; CANCEL-FUNCTION may block until all interruptors finish, let's
258 ;; try to cancel without the scheduler lock first.
259 (when (%timer-cancel-function timer)
260 (funcall (%timer-cancel-function timer)))
261 (with-scheduler-lock ()
262 (setf (%timer-expire-time timer) (+ (get-internal-real-time)
265 (- time (get-universal-time))
267 (%timer-repeat-interval timer) (if repeat-interval
268 (delta->real repeat-interval)
270 (%schedule-timer timer)))
272 (defun unschedule-timer (timer)
274 "Cancel TIMER. Once this function returns it is guaranteed that
275 TIMER shall not be triggered again and there are no unfinished
277 (let ((cancel-function (%timer-cancel-function timer)))
278 (when cancel-function
279 (funcall cancel-function)))
280 (with-scheduler-lock ()
281 (setf (%timer-expire-time timer) nil
282 (%timer-repeat-interval timer) nil)
283 (let ((old-position (priority-queue-remove *schedule* timer)))
285 (funcall (%timer-cancel-function timer)))
286 (when (eql 0 old-position)
287 (set-system-timer))))
290 (defun list-all-timers ()
292 "Return a list of all timers in the system."
293 (with-scheduler-lock ()
294 (concatenate 'list (%pqueue-contents *schedule*))))
296 ;;; Not public, but related
298 (defun reschedule-timer (timer)
299 (with-scheduler-lock ()
300 (setf (%timer-expire-time timer) (+ (get-internal-real-time)
301 (%timer-repeat-interval timer)))
302 (%schedule-timer timer)))
306 (defun real-time->sec-and-usec(time)
309 (multiple-value-bind (s u) (floor time internal-time-units-per-second)
310 (setf u (floor (* (/ u internal-time-units-per-second) 1000000)))
312 ;; 0 0 means "shut down the timer" for setitimer
316 (defun set-system-timer ()
317 (assert (under-scheduler-lock-p))
318 (let ((next-timer (peek-schedule)))
320 (let ((delta (- (%timer-expire-time next-timer)
321 (get-internal-real-time))))
322 (apply #'sb!unix:unix-setitimer
323 :real 0 0 (real-time->sec-and-usec delta)))
324 (sb!unix:unix-setitimer :real 0 0 0 0))))
326 (defun run-timer (timer)
327 (symbol-macrolet ((function (%timer-function timer))
328 (repeat-interval (%timer-repeat-interval timer))
329 (thread (%timer-thread timer)))
330 (when repeat-interval
331 (reschedule-timer timer))
335 (sb!thread:make-thread function))
338 (sb!thread:interrupt-thread thread function)
339 (sb!thread:interrupt-thread-error (c)
342 ;; Called from the signal handler.
343 (defun run-expired-timers ()
348 (with-scheduler-lock ()
349 (setq timer (peek-schedule))
351 (> (get-internal-real-time)
352 (%timer-expire-time timer)))
353 (return-from run-expired-timers nil))
354 (assert (eq timer (priority-queue-extract-maximum *schedule*))))
355 ;; run the timer without the lock
357 (with-scheduler-lock ()
358 (set-system-timer))))
360 (defmacro sb!ext:with-timeout (expires &body body)
362 "Execute the body, asynchronously interrupting it and signalling a
363 TIMEOUT condition after at least EXPIRES seconds have passed."
364 (with-unique-names (timer)
365 ;; FIXME: a temporary compatibility workaround for CLX, if unsafe
366 ;; unwinds are handled revisit it.
368 (let ((,timer (make-timer (lambda ()
369 (cerror "Continue" 'sb!ext::timeout)))))
370 (schedule-timer ,timer ,expires)
373 (unschedule-timer ,timer)))