From: Nikodemus S. <nik...@ra...> - 2012-05-05 07:48:12
|
On 4 May 2012 18:17, Keith Browne <tu...@de...> wrote: > On Fri, 4 May 2012, Nikodemus Siivola wrote: > >> No, this is not a known problem -- or at least it doesn't ring any >> bells for me. I have your test running here, and will try to >> reproduce. >> >> A few extra questions: >> >> - SBCL version? > > > We're running 1.0.56 right now, but we've seen the same problem on a few > earlier versions. > > >> - Output from uname -a? > > > The two machines I can reach handily for now are: > > Linux lowry 3.0.0-17-generic #30-Ubuntu SMP Thu Mar 8 20:45:39 UTC 2012 > x86_64 x86_64 x86_64 GNU/Linux > > Linux coruscant 2.6.32-5-686 #1 SMP Mon Oct 3 04:15:24 UTC 2011 i686 > > >> - How exactly are you running the test? You say you connect to SBCL: >> do you mean just M-x slime, or do you have SBCL already running with a >> swank server that you connect to? > > > We've been running under M-x slime. > > >> Ok, I can reproduce this, but I'm almost out of time for today, but I >> should have a couple of hours tomorrow. > > > I'm glad to hear that. Thanks for any insight--let me know if there's any > more information we can provide. I think I have a couple of leads on this. ...but, in the meanwhile, I'd like to point out that using timers -- and asynch interrupts they imply -- makes the control paths of a system much more complex than alternative means of scheduling repeating jobs. This means you're more likely to run into heisenbugs in both your own and SBCL's code. Also, spawning threads isn't exactly cheap, so unless your evens are rather rare, running each event in a new thread isn't probably as performant as some other options. Here's a possible sketch: (defclass task () ((thread :initform nil :reader task-thread) (lock :initform (sb-thread:make-mutex :name "Task Lock") :reader task-lock) (control-lock :initform (sb-thread:make-mutex :name "Task Control Lock") :reader task-control-lock) (function :initarg :function :reader task-function) (interval :initarg :interval :reader task-interval))) (defun stop-task (task &key timeout) (sb-thread:with-mutex ((task-control-lock task)) (sb-thread:with-mutex ((task-lock task)) (setf (slot-value task 'interval) nil)) (let ((thread (task-thread task))) (when thread (sb-thread:join-thread thread :default t :timeout timeout))) (setf (slot-value task 'thread) nil))) (defun start-task (task interval &optional (function (task-function task))) (tagbody (sb-thread:with-mutex ((task-control-lock task)) (when (task-interval task) (let ((thread (task-thread task))) (when (and thread (sb-thread:thread-alive-p thread)) (go :oops)))) (setf (slot-value task 'interval) interval (slot-value task 'function) function (slot-value task 'thread) (sb-thread:make-thread (lambda () (loop for s = (sb-thread:with-mutex ((task-lock task)) (task-interval task)) while s do (sleep s) (funcall (task-function task)))) :name (format nil "Task ~S / ~S" function interval)))) (return-from start-task task) :oops (error "Task already running."))) (defmethod initialize-instance :after ((task task) &key) (let ((interval (task-interval task))) (when interval (start-task task interval)))) I could see myself using timers spwaning new threads if I had some task that needed to be started approximately every N ticks, even if the previous one had not finished yet. However, this doesn't currently work as one might expect: a new instance of a timer will not start running user code until the last one finishes -- and even then grabbing a new thread every time would be wasteful. A more complicated design would be to have one scheduler thread firing off events in worker threads. If no workers are currently free, spawn a new one. Anyways, digging into timers now. Cheers, -- Nikodemus |