Hi Jakob, I apologize for the long delay in addressing this issue. Thank you very much for your bug report and fix for this deadlock condition. The patch has been committed to SVN (along with some small optimizations in the way repeating timers are re-scheduled).
Your fix will be particularly helpful with the upcoming 1.4.1 release, since the GUI system will now rely on AG_Timeout extensively in order to avoid unnecessary widget redrawing. Thanks again, On Tue, Mar 16, 2010 at 09:49:16AM +0000, Jakob Reschke wrote: > Hi, > > I'm currently trying something with timeouts but I run into some problems: > > I've got some functions that are to be called when a specific timeout > is no longer scheduled (i. e. has run through). If the other timeout > is still scheduled (checked with AG_TimeoutIsScheduled), the function > execution is deferred by some ival to try again later. > > Under some conditions this mechanism locks up, however. Even though > the one function does not reverse depend on the other. (A depends on > B's timeout, but B doesn't depend on A). > > This scheduling order doesn't cause a lockup but the callbacks are > called in the wrong order: > > AG_ScheduleTimeout(NULL, pTimeoutA, 1); > > This is the list of ticks of the currently scheduled timeouts in the > queue afterwards (to->ticks of each timeout): > (4442776) > > AG_ScheduleTimeout(NULL, pTimeoutB, 2000); > > The new list of ticks of the scheduled timeouts: (4449696 4442776) As > you can see, the timeout that should be executed later is on the head > of the tailqueue. > > My trace reads that the callback for timeoutB is called before that of > timeoutA, even though timeoutA was both scheduled earlier and has the > smaller ival. > > Now the combination that locks up, scheduling the other way around: > > AG_ScheduleTimeout(NULL, pTimeoutB, 2000); > AG_ScheduleTimeout(NULL, pTimeoutA, 1); > > ticks in the timeout tailqueue: (6049273 6051271). This time the order > is correct or better still correct. The first value belongs to > timeoutA. > > The function for timeoutA is called first and rescheduled until the > 2000 ticks are over, because A depends on B. But even when the 2000 > have passed, timeoutA is always rescheduled on the head of the > tailqueue (pushed to its head). That means after 2000 ticks the ticks > of the timeout on the head of the queue are greater than the following > ones, again. > > AG_ProcessEvents now checks for the first timeout, which is timeoutA, > and runs it's function. Because timeoutB is still scheduled, timeoutA > is rescheduled but is added on the head of the timeout tailqueue > again by AG_ScheduleTimeout. AG_ProcessEvents does now its goto pop; > and checks the first timeout from the queue, which is timeoutA again > but this time with its ticks in the future. That's why timeoutB's > function is never called and timeoutA waits forever. > > I can't imagine that AG_ScheduleTimeout works correctly here when it > always adds at the head of the timeout queue regardless of the ticks > differences. But maybe I got the interface wrong... in the latter > case, can you give me any hints please, what I could do instead? > > I'm afraid that it's not useful to post my code, since it's Lisp code > that calls Agar's functions and the timeout checking and rescheduling > is somewhat spread, it would need a while to explain what every macro > and function involved does. But so far I assume that to be correct, I > really wonder about the timeout tailqueue and the order of it's > elements. > > Jakob > _______________________________________________ > Agar mailing list > [email protected] > http://libagar.org/lists.html _______________________________________________ Agar mailing list [email protected] http://libagar.org/lists.html
