
Yes it does: order of method call.
Ah. That's useful to know: I would prefer to use a non-threading-related call to achieve this eventual-send functionality. I'll add a TODO test to check that this promise is being met.
It should be fixable by storing the last time of an element added to the queue, and adding epsilon if current time is the same.
Wouldn't you have to guarantee that epsilon is smaller than the resolution of your time.time() return value? What if you sorted on a tuple of (time, counter) instead? (the down side is that you'd have to search for all existing timers with the same time value to figure out what counter value you ought to use. ick.).
We should clarify what guarantees are made by callLater. I think there may be several separate ones here:
1: callLater(0, A); callLater(0, B) will result in A being invoked before B will result in both A and B being invoked before any other DelayedCalls
2: callLater(N, A); callLater(N, B) will result in A being invoked before B
3: callLater(N, A); callLater(N+M, C); callLater(N, B) will result in A being invoked before B (think of this as a unit test for the adding-epsilon concern above)
The second and third ones are not so important to me, just in terms of what I need to use it as a plan-coordination tool. I only intend to use this with N=0.
To that end, using a separate queue for timers that are ready to go "now" (i.e. ones that will be fired before calling select() or the like) might be useful, basically making N=0 a special case. This would avoid the overhead of inserting the DelayedCall into an arbitrary place and maintaining the ordering guarantees of #2 and #3, and would avoid an extra select() spin between the time an N=0 timer was inserted and the time it was fired.
The existing threadCallQueue happens to behave exactly this way, although I'd want to write some additional tests to make sure it gets serviced as many times as it's supposed to be (specifically, when threads are unavailable and therefore wakeUp() is not used, does a call inserted from within an N=0 callback get serviced before the reactor sleeps again?). The problem is both the word "thread" in the name, and the fact that we might not be making the same guarantees about the behavior of callFromThread as we are about that of callLater.
Hmm. Most reactors split off a list of timers that are ready to go "now" on each spin, right? And/or there's that _pendingTimedCalls list I see in t.i.base .. maybe we could take advantage of one of those, just appending the call to those lists and making sure they'll be serviced again, rather than adding the overhead of maintaining ordering guarantees #2 and #3.
hmm-ingly, -Brian