[I'm finding the doc sidebar comments to be difficult to track for this very high-level discussion, so I'm popping up into the email thread.]
SummaryIf I were to distill this proposal, it would be: "Currently we have SequencedTaskRunnerHandle which offers strong guarantees. Migrating this is nontrivial and currently only one caller currently takes advantage of those guarantees. If we weaken the guarantees things will be simpler to implement and we'll fix the one place that uses it."Currently, the notion of the "current context" as defined by SequencedTaskRunnerHandle is1. It's the current thread if the current thread has a normal message loop2. It's the current sequence if the code is running on a sequenced worker pool with a sequence token.3. It's the current task if the code is running on a pool with no sequence token.
The proposal is effectively to change:3. It's the current pool if the code is running on a pool with no sequence token.In practice this means that things posted to the "current context" when running without a sequence token previously would be guaranteed to run after the current task, but now can run in parallel.
An alternative proposed in the comments by Gab was:3. It's the current pool but all tasks are guaranteed to run after the current task completes if it's running on a pool with no sequence token.[Please correct my understanding if any of the above is wrong, since it will mean everything below is also wrong.]Theoretical ruminationsDefining a "current context" to mean an entire worker pool that can also be overlapped with the current task is so broad as to be not useful. In this case, it is better to say there's no current context at all and Get() doesn't exist. If some code needs to post tasks that can be called in such a context, perhaps it should explicitly post them on some thread or worker pool according to its requirements.
The alternative of deferring posted tasks until after the current task completes, but potentially running the in parallel to each other seems more plausible than having Get return the current pool with no restrictions. In the context of an API that also includes GetSequenced() and GetSingleThreaded(), it would stand to reason that the thing returned by Get() would not necessarily be sequenced or single threaded. If the tasks have requirements between them, the calling code can ensure that they're posted with some kind of sequence.But independent of this, I have a growing concern that out task runner stuff is already too complicated to be understood by humans, and TaskScheduler is making it worse (not to criticize the team, it seems unavoidable in the short term). I also don't know how in practice using our current primitives one would actually implement "I want to post a sequence of 3 tasks that run after my function on whatever the current context is" and maybe implementing that is actually too complicated. I wonder how much we can actually reason about such general code and write it reliably.
The pointCan we just delete SequencedTaskRunnerHandle? Although this makes it possible to write some very general code that can be called in any context, it also makes it difficult to reason about. If your code expects to be called from various threads and pools in different contexts and needs to post tasks, it should probably do so to a specific place with the requirements it needs. Storing thread-local state of the context of the current sequence seems undesirably global-variable-like to me and makes things difficult to reason about. If the tasks that it posts need to be sequenced with respect to the calling code, the SequencedTaskRunner could be passed in. In this case, everything is very explicit about what's run where and what the ordering requirements are.
If we can delete this, it seems like we don't actually need the proposed change at all. For those people more familiar with how we hope people will use the scheduler to work, does this seem possible?
Brett
Brett--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
ping: any thoughts on last 2 replies? Thanks!
ping: any thoughts on last 2 replies? Thanks!
Le lun. 11 juil. 2016 21:35, Gabriel Charette <g...@chromium.org> a écrit :Actually, this makes me think : essentially the use cases I think TaskRunnerHandle::GetAny() applies to are PostTaskAndReply use cases in which the component keeps the "origin" TaskRunner for an eventual reply (or has the PostTaskAndReplyRelay do so implicitly).In those cases I think it's okay to allow the "origin" TaskRunner to merely be backed by a pool (i.e. "parallel") so long as the reply is not executed while the original poster is still running.So perhaps what we need (as was discussed on doc) is something like TaskRunnerHandle::GetDeferred() instead of GetAny() which would return a plain TaskRunner but would guarantee tasks posted to it don't run until the task it was obtained from has completed?Then we get the benefits of this being usable from components that merely care about replying (i.e. that don't need to force the caller to be sequenced), without the headaches of an occasional reply unintentionally running in parallel with its request.
Actually, this makes me think : essentially the use cases I think TaskRunnerHandle::GetAny() applies to are PostTaskAndReply use cases in which the component keeps the "origin" TaskRunner for an eventual reply (or has the PostTaskAndReplyRelay do so implicitly).In those cases I think it's okay to allow the "origin" TaskRunner to merely be backed by a pool (i.e. "parallel") so long as the reply is not executed while the original poster is still running.So perhaps what we need (as was discussed on doc) is something like TaskRunnerHandle::GetDeferred() instead of GetAny() which would return a plain TaskRunner but would guarantee tasks posted to it don't run until the task it was obtained from has completed?Then we get the benefits of this being usable from components that merely care about replying (i.e. that don't need to force the caller to be sequenced), without the headaches of an occasional reply unintentionally running in parallel with its request.Does that make sense? If so I can update the doc to reflect that before everyone takes another look.
On Mon, Jul 11, 2016 at 6:35 PM, Gabriel Charette <g...@chromium.org> wrote:Actually, this makes me think : essentially the use cases I think TaskRunnerHandle::GetAny() applies to are PostTaskAndReply use cases in which the component keeps the "origin" TaskRunner for an eventual reply (or has the PostTaskAndReplyRelay do so implicitly).In those cases I think it's okay to allow the "origin" TaskRunner to merely be backed by a pool (i.e. "parallel") so long as the reply is not executed while the original poster is still running.So perhaps what we need (as was discussed on doc) is something like TaskRunnerHandle::GetDeferred() instead of GetAny() which would return a plain TaskRunner but would guarantee tasks posted to it don't run until the task it was obtained from has completed?Then we get the benefits of this being usable from components that merely care about replying (i.e. that don't need to force the caller to be sequenced), without the headaches of an occasional reply unintentionally running in parallel with its request.Does that make sense? If so I can update the doc to reflect that before everyone takes another look.First, I agree that PostTaskAndReply should never run the reply in parallel. Also, we agree what PostTaskAndReply should do on single threads or worker pool tasks with sequences and nothing is being changed.But it's not clear to me even what PostTaskAndReply means in the context of requesting it from a task with no sequence and no well-known thread.
Replying to a worker pool and saying "just run on any thread" isn't a "reply" at all and actually this has no meaning in Chrome's current architecture. If you don't care what thread the callback is run on, it seems like you really want to post a task that calls your function and then does something on that thread.With the task scheduler the calling pool could have a different priority or something so theoretically this could have meaning. But I still fail to see how this could be used in practice and doing this actually seems likely to be a bug. It seems actually beneficial to not support PostTaskAndReply with the reply being an unsequenced worker pool. If you want a reply, you need to have a reasonably specific place that the reply goes to.Brett
On Wed, Jul 13, 2016, 7:07 AM Brett Wilson <bre...@chromium.org> wrote:On Mon, Jul 11, 2016 at 6:35 PM, Gabriel Charette <g...@chromium.org> wrote:Actually, this makes me think : essentially the use cases I think TaskRunnerHandle::GetAny() applies to are PostTaskAndReply use cases in which the component keeps the "origin" TaskRunner for an eventual reply (or has the PostTaskAndReplyRelay do so implicitly).In those cases I think it's okay to allow the "origin" TaskRunner to merely be backed by a pool (i.e. "parallel") so long as the reply is not executed while the original poster is still running.So perhaps what we need (as was discussed on doc) is something like TaskRunnerHandle::GetDeferred() instead of GetAny() which would return a plain TaskRunner but would guarantee tasks posted to it don't run until the task it was obtained from has completed?Then we get the benefits of this being usable from components that merely care about replying (i.e. that don't need to force the caller to be sequenced), without the headaches of an occasional reply unintentionally running in parallel with its request.Does that make sense? If so I can update the doc to reflect that before everyone takes another look.First, I agree that PostTaskAndReply should never run the reply in parallel. Also, we agree what PostTaskAndReply should do on single threads or worker pool tasks with sequences and nothing is being changed.But it's not clear to me even what PostTaskAndReply means in the context of requesting it from a task with no sequence and no well-known thread.+1 to this point. I think it's an accident that this has ever worked. Is it widespread?
Replying to a worker pool and saying "just run on any thread" isn't a "reply" at all and actually this has no meaning in Chrome's current architecture. If you don't care what thread the callback is run on, it seems like you really want to post a task that calls your function and then does something on that thread.With the task scheduler the calling pool could have a different priority or something so theoretically this could have meaning. But I still fail to see how this could be used in practice and doing this actually seems likely to be a bug. It seems actually beneficial to not support PostTaskAndReply with the reply being an unsequenced worker pool. If you want a reply, you need to have a reasonably specific place that the reply goes to.
On Wed, Jul 13, 2016 at 12:12 PM Greg Thompson <g...@chromium.org> wrote:On Wed, Jul 13, 2016, 7:07 AM Brett Wilson <bre...@chromium.org> wrote:On Mon, Jul 11, 2016 at 6:35 PM, Gabriel Charette <g...@chromium.org> wrote:Actually, this makes me think : essentially the use cases I think TaskRunnerHandle::GetAny() applies to are PostTaskAndReply use cases in which the component keeps the "origin" TaskRunner for an eventual reply (or has the PostTaskAndReplyRelay do so implicitly).In those cases I think it's okay to allow the "origin" TaskRunner to merely be backed by a pool (i.e. "parallel") so long as the reply is not executed while the original poster is still running.So perhaps what we need (as was discussed on doc) is something like TaskRunnerHandle::GetDeferred() instead of GetAny() which would return a plain TaskRunner but would guarantee tasks posted to it don't run until the task it was obtained from has completed?Then we get the benefits of this being usable from components that merely care about replying (i.e. that don't need to force the caller to be sequenced), without the headaches of an occasional reply unintentionally running in parallel with its request.Does that make sense? If so I can update the doc to reflect that before everyone takes another look.First, I agree that PostTaskAndReply should never run the reply in parallel. Also, we agree what PostTaskAndReply should do on single threads or worker pool tasks with sequences and nothing is being changed.But it's not clear to me even what PostTaskAndReply means in the context of requesting it from a task with no sequence and no well-known thread.+1 to this point. I think it's an accident that this has ever worked. Is it widespread?Note: PostTaskAndReply itself doesn't actually work from parallel tasks today (it is meant to from the API though -- i.e. the plain TaskRunner API exposes it -- but the impl (PostTaskAndReplyRelay) uses ThreadTaskRunnerHandle which is specific to SingleThreadTaskRunners...).Replying to a worker pool and saying "just run on any thread" isn't a "reply" at all and actually this has no meaning in Chrome's current architecture. If you don't care what thread the callback is run on, it seems like you really want to post a task that calls your function and then does something on that thread.With the task scheduler the calling pool could have a different priority or something so theoretically this could have meaning. But I still fail to see how this could be used in practice and doing this actually seems likely to be a bug. It seems actually beneficial to not support PostTaskAndReply with the reply being an unsequenced worker pool. If you want a reply, you need to have a reasonably specific place that the reply goes to.I think post-task-and-reply semantics from unsequenced tasks actually has valid use cases. e.g. a one-off task (LoadSTHsFromDisk) wants to post another one-off task to do some parsing (through SafeJsonParserImpl) and then do something with the response (once again as a one-off task).This is merely a chain of callbacks (which I guess is "sequenced" in a way but not in the traditional sense) and I don't see why it shouldn't be supported (at least the current API intends for it to be by (1) declaring PostTaskAndReply on the plain TaskRunner and (2) PostTaskAndReplyWithResult taking plain TaskRunners as arguments).
Ok, so conclusion:Keep ThreadTaskRunnerHandle and SequencedTaskRunnerHandle (forget doc proposal). Ban implicit creation of sequences. Force any caller that uses a component that cares to have a SequencedTaskRunnerHandle available to create a sequence token (in our canonical example, the poster of LoadSTHsFromDisk would have to not post directly to the BlockingPool but rather to a sequence on it)?That solves my immediate problem and puts the burden on the occasional odd caller which I think is reasonable.While we're on this topic: do we think PostTaskAndReply should only be supported on SequencedTaskRunners? I'm happy to make this change (it "should" be a simple replacement per the impl actually only working from SingleThreadTaskRunners in practice and existing usage thus always coming from one today in theory -- though some (hopefully not many :-S!) callsites may have a generic TaskRunner which happens to be a more specific one).And I would of course also add support for PostTaskAndReply to work from sequences in practice (have PostTaskAndReplyRelay use SequencedTaskRunnerHandle instead of ThreadTaskRunnerHandle) -- another prereq for the TaskScheduler.