Skip to content

Add callback for when a task is added to a run-loop schedule#337

Merged
kirkshoop merged 2 commits intoReactiveX:masterfrom
studoot:add-runloop-callback
Jan 26, 2017
Merged

Add callback for when a task is added to a run-loop schedule#337
kirkshoop merged 2 commits intoReactiveX:masterfrom
studoot:add-runloop-callback

Conversation

@studoot
Copy link
Copy Markdown
Contributor

@studoot studoot commented Jan 25, 2017

See #154 for motivation for this PR...

Implementation decisions:

  1. I've added a single callback as a std::function<void()>:
    1. I don't think the task details are too relevant for the callback
    2. I think there's only likely to be one callback needed to be registered at any one time, given the use case of integrating the Rx loop with external event loops.
  2. The callback can be disconnected by setting it to std::function<void()>{}

@ghost
Copy link
Copy Markdown

ghost commented Jan 25, 2017

Hi @studoot, I'm your friendly neighborhood Microsoft Open Technologies, Inc. Pull Request Bot (You can call me MSOTBOT). Thanks for your contribution!

This seems like a small (but important) contribution, so no Contribution License Agreement is required at this point. Real humans will now evaluate your PR.

TTYL, MSOTBOT;

@ghost ghost added the cla-not-required label Jan 25, 2017
Copy link
Copy Markdown
Member

@kirkshoop kirkshoop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, Thank you!

st->r.reset(false);
std::function<void()> callback{st->add_task_callback};
guard.unlock(); // So we can't get attempt to recursively lock the state
if (callback) callback();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schedule() is called from other threads in order to schedule work on the thread that owns the runloop.

callback() will be called from other threads. It is not okay to dispatch any events from the callback since they would be running on the wrong thread. The only thing that the callback can do is reset the timer to an earlier value if the new item is supposed to occur earlier than the previous timer was set. This would be easier if the callback was passed the new time.

If so, perhaps it would be better if the callback was only called when the new when is earlier than the previous queue top. In that case, the name of the callback might be notify_next_wakeup instead of add_task_callback.

st->r.reset(false);
std::function<void()> callback{st->add_task_callback};
guard.unlock(); // So we can't get attempt to recursively lock the state
if (callback) callback();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schedule() is called from threads other than the thread the owns the runloop.

callback() is also called from other threads. The callback must not dispatch runloop items. The only thing the callback could safely do is set a timer to trigger at an earlier time than it currently is set.

passing the new when would make that easier.
only calling the callback if the new time is earlier than the previous top item would be reasonable as well.

If those changes are made then the name of the callback might change as well. Perhaps notify_when_changed?

@kirkshoop
Copy link
Copy Markdown
Member

:) I was sure that github had lost my comment - it just disappeared. So I wrote it again!

@studoot
Copy link
Copy Markdown
Contributor Author

studoot commented Jan 25, 2017

Hmmmm - without an easy way of signalling the run loop's thread, I think this needs a bit more thought.

One thing I was thinking of was to just provide more information about the state of the queue (are there tasks ready for dispatch or newly added since the last dispatch?). Or to document that the callback is purely to wake the run loops thread (I know how to do that with Qt...)... How does that sound?

@kirkshoop
Copy link
Copy Markdown
Member

One thing I was thinking of was to just provide more information about the state of the queue (are there tasks ready for dispatch ...

I think that passing the when to the callback and only calling the callback if the item replaced the front/top of the queue will indicate that a task is ready for dispatch and what time the QT timer should be created to process the item on the runloop thread.

I think that a small change to what you have in this PR will create this semantic.

... or newly added since the last dispatch?)). Or to document that the callback is purely to wake the run loops thread (I know how to do that with Qt...)... How does that sound?

IMO, 'newly added' should not signal the runloops thread. Many times the new item will be in the future, or at the same time as the item already at the front/top of the runloop. Waking the thread to run nothing or waking the thread repeatedly for the same time, when each item is scheduled, would be very chatty.

Do you think that you could do what you need in QT with the changes to the callback I proposed?

Modified callback to provide new wakeup time as a callback parameter, so that it can schedule an event to wake the thread owning the run loop, rather than (as was the case) having to iterate through the dispatch loop.
@studoot
Copy link
Copy Markdown
Contributor Author

studoot commented Jan 26, 2017

To answer your question - yes, and here's an updated PR... This works with Qt and (if anything) simplifies that code.

Rationale for some of the change:

  1. I've made the run_loop's clock_type public, to allow its use in the definition of the callback function's type.
  2. The callback is triggered while the mutex is locked. This is so that the scheduler queue can't be manipulated within the callback, encouraging minimal code in the callback.

@kirkshoop
Copy link
Copy Markdown
Member

I am happy that this works well for QT, that gives me confidence.

Thank you for this change!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants