--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
Adam
On Mon, Oct 4, 2010 at 2:58 PM, Albert J. Wong (王重傑)
<ajw...@chromium.org> wrote:
Andrew (scherkus@) and I got together a few weeks ago to try and take a high-level look at our Callback system in Chromium. The primary motivator has been our experience trying to use the system in the media and chromoting code bases. One key observation was that we have too many callback systems, and none quite do everything you'd want. In an attempt to rationalize this, here is a design doc describing the state of the world, a proposal for what it could look like, and an outline for how to migrate there.
Cool!
Did you guys look at TR1 function/bind? They may be mature enough
nowadays that it *may* be feasible to implement a compatible callback
system using it. On the other hand, it may be more trouble than it's
worth.
ScopedRunnableMethodFactory is for objects that aren't RefCounted.
When the object dies, all the in-flight callbacks are cancelled.
Adam
Adam
Thanks for the questions and feedback so far. Going to respond to everyone in one mail.Matt Perry:ResultCallback annoyed me too. I'll play with specializations a bit to see if it can be done. There may be a symbol space trade-off there though since you add yet another template parameter.
As for RunnableMethodTraits, and usage with PostTask, Brett commented on this earlier. Currently, I was suggesting 2 different functions in MessageLoop, PostTask() and PostClosure(). PostTask() would only take an instance of RefClosure which gives the static type checking. An argument could be made to rename PostClosure into something scarier like PostUnsafeTask(). Would that resolve the concern?
Matt Perry:ResultCallback annoyed me too. I'll play with specializations a bit to see if it can be done. There may be a symbol space trade-off there though since you add yet another template parameter.I don't see why there needs to be anther template parameter. I was thinking of something like:
Agh...missed Peter.Looking through base/timer.h, I we can just port the Timer API over to the analogous new callback types and things should continue to work. Were you thinking of some sort of tighter integration?
--
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked. There's no good ScopedRunnableMethodFactory for the threadsafe case, which I suspect is why a lot of the objects become RefCountedThreadSafe. I'm not entirely sure if my conjecture is true, but that's my suspicion. I feel like refcounting in this case often confuses object ownership and lifetime. I think a potentially better solution would be to use threadsafe weak pointers (might need to fix base::WeakPtr since it's thread-unsafe).
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.
On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.
On Tue, Oct 5, 2010 at 11:41 AM, John Abd-El-Malek <j...@chromium.org> wrote:On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.You know more about these use cases than I do, so I definitely trust you when you say such scenarios exist. For my own clarification, can you point out some classes where we need the callback always to be run?I think that if we can minimize these scenarios, then I'll be happy. Once we have a good alternative for the multithreaded case and convert the majority of existing cases to using that, then I think that people will hopefully follow the examples of the code around them and will use the alternative.I looked at the document again, and I noticed that IsRefCounted() is a virtual member function on Closure. Is that necessary? I would have thought that we could use type traits for the RefClosure subclass in ChromeThread::PostTask to enforce this without needing the virtual. The virtual seems to imply that the MessageLoop will be doing the refcounting. I would have thought that the RefClosure itself would do the refcounting. Can you clarify this?
On Tue, Oct 5, 2010 at 11:41 AM, John Abd-El-Malek <j...@chromium.org> wrote:On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.You know more about these use cases than I do, so I definitely trust you when you say such scenarios exist. For my own clarification, can you point out some classes where we need the callback always to be run?
On Tue, Oct 5, 2010 at 1:30 PM, William Chan (陈智昌) <will...@chromium.org> wrote:On Tue, Oct 5, 2010 at 11:41 AM, John Abd-El-Malek <j...@chromium.org> wrote:On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.You know more about these use cases than I do, so I definitely trust you when you say such scenarios exist. For my own clarification, can you point out some classes where we need the callback always to be run?I think that if we can minimize these scenarios, then I'll be happy. Once we have a good alternative for the multithreaded case and convert the majority of existing cases to using that, then I think that people will hopefully follow the examples of the code around them and will use the alternative.I looked at the document again, and I noticed that IsRefCounted() is a virtual member function on Closure. Is that necessary? I would have thought that we could use type traits for the RefClosure subclass in ChromeThread::PostTask to enforce this without needing the virtual. The virtual seems to imply that the MessageLoop will be doing the refcounting. I would have thought that the RefClosure itself would do the refcounting. Can you clarify this?This is not for deciding if something will do refcounting, but rather just to facilitate DCHECKs. I thought it'd be nice to query the objects for this.
BTW, did people have an opinion on the naming of the factory methods?
On Tue, Oct 5, 2010 at 1:30 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Tue, Oct 5, 2010 at 11:41 AM, John Abd-El-Malek <j...@chromium.org> wrote:On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.You know more about these use cases than I do, so I definitely trust you when you say such scenarios exist. For my own clarification, can you point out some classes where we need the callback always to be run?Off the top of my head, in all the places of ResourceMessageFilter where we use the IPC_MESSAGE_HANDLER_DELAY_REPLY. We go off to a different thread to do some work, then post a message back to send the reply to the synchronous message. Even though it's shutdown when RMF is going away, if the reply is not thread, the renderer will be hung and it won't have a clean shutdown. It might even happen without shutdown, say if the process is closed while it has an outstanding synchronou XHR. Then we'd have a zombie renderer.
--
On Tue, Oct 5, 2010 at 3:08 PM, John Abd-El-Malek <j...@chromium.org> wrote:On Tue, Oct 5, 2010 at 1:30 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Tue, Oct 5, 2010 at 11:41 AM, John Abd-El-Malek <j...@chromium.org> wrote:On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.You know more about these use cases than I do, so I definitely trust you when you say such scenarios exist. For my own clarification, can you point out some classes where we need the callback always to be run?Off the top of my head, in all the places of ResourceMessageFilter where we use the IPC_MESSAGE_HANDLER_DELAY_REPLY. We go off to a different thread to do some work, then post a message back to send the reply to the synchronous message. Even though it's shutdown when RMF is going away, if the reply is not thread, the renderer will be hung and it won't have a clean shutdown. It might even happen without shutdown, say if the process is closed while it has an outstanding synchronou XHR. Then we'd have a zombie renderer.I think this is a fine example. John, in your experience, do you think this is the common case for RefCountedThreadSafe objects? My hunch is that it isn't. I feel like more would rather that the default for RunnableMethodTraits not keep their lifetimes across posted tasks. Maybe all those cases will go away once you fix ScopedRunnableMethodFactory to be thread-safe. Btw, how does this work? Will you have a lock? Does that mean that if thread A posts a ScopedRunnableMethodFactory::NewRunnableMethod to thread B, then thread A could potentially block until thread B finishes running the task (assuming thread B already started running the task before the factory tried to revoke it)?
On Tue, Oct 5, 2010 at 10:21 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Tue, Oct 5, 2010 at 3:08 PM, John Abd-El-Malek <j...@chromium.org> wrote:On Tue, Oct 5, 2010 at 1:30 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Tue, Oct 5, 2010 at 11:41 AM, John Abd-El-Malek <j...@chromium.org> wrote:On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.You know more about these use cases than I do, so I definitely trust you when you say such scenarios exist. For my own clarification, can you point out some classes where we need the callback always to be run?Off the top of my head, in all the places of ResourceMessageFilter where we use the IPC_MESSAGE_HANDLER_DELAY_REPLY. We go off to a different thread to do some work, then post a message back to send the reply to the synchronous message. Even though it's shutdown when RMF is going away, if the reply is not thread, the renderer will be hung and it won't have a clean shutdown. It might even happen without shutdown, say if the process is closed while it has an outstanding synchronou XHR. Then we'd have a zombie renderer.I think this is a fine example. John, in your experience, do you think this is the common case for RefCountedThreadSafe objects? My hunch is that it isn't. I feel like more would rather that the default for RunnableMethodTraits not keep their lifetimes across posted tasks. Maybe all those cases will go away once you fix ScopedRunnableMethodFactory to be thread-safe. Btw, how does this work? Will you have a lock? Does that mean that if thread A posts a ScopedRunnableMethodFactory::NewRunnableMethod to thread B, then thread A could potentially block until thread B finishes running the task (assuming thread B already started running the task before the factory tried to revoke it)?Definitely we would want to avoid one thread blocking on another, since that'd lead to deadlocks. I was thinking that perhaps there would be a way to post task to a static member function, along with passing an extra parameter that allows one to run a method on the original thread. This would solve a common use case of going to the file thread to do a slow operation, and then wanting to get back to the original thread. Even with objects that currently derive from RefCountedThreadSafe, often many of the member variables are not thread safe, and the user passes the needed information using parameters. The above would get rid of a bunch of hacky code I've personally written :)
On Wed, Oct 6, 2010 at 9:28 AM, John Abd-El-Malek <j...@chromium.org> wrote:
On Tue, Oct 5, 2010 at 10:21 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Tue, Oct 5, 2010 at 3:08 PM, John Abd-El-Malek <j...@chromium.org> wrote:On Tue, Oct 5, 2010 at 1:30 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Tue, Oct 5, 2010 at 11:41 AM, John Abd-El-Malek <j...@chromium.org> wrote:On Mon, Oct 4, 2010 at 8:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
Thanks for sending this out! I'm really excited to get curried functions!The one section I'd like to hear opinions on is the ref counting support in the callbacks. My opinion is primarily derived from net/ which is mostly single-threaded, so I have to admit that I probably have a very narrow perspective on the issue. I'd love to see counter-examples to the points I'm about to raise.I have to say that I feel like ref counting in callbacks is generally undesirable. I feel like people often don't think about, if the last ref is the ref added by RunnableMethodTraits, do we really want to keep the object alive? I'm suspicious that this is usually the case (if you have good counter-examples here, please list them!). For the single-threaded case, I think that ScopedRunnableMethodFactory is almost always what you want when posting a task, even if the object is RefCounted. In the multithreaded case, I think that people unfortunately make their object RefCountedThreadSafe in order to make sure they're alive when the callback is invoked. However, they wouldn't mind if the object were deleted and the callback not invoked.I think both scenarios exist, and so some objects will always want the callback to be run. However, as you point out, if you don't care about a callback not running if the object is deleted, currently the code only supports the single-threaded case (using ScopedRunnableMethodFactory). There's no clean solution to when the callback goes to a different thread. I think this is easy to fix, I'll take a stab at it real soon.You know more about these use cases than I do, so I definitely trust you when you say such scenarios exist. For my own clarification, can you point out some classes where we need the callback always to be run?Off the top of my head, in all the places of ResourceMessageFilter where we use the IPC_MESSAGE_HANDLER_DELAY_REPLY. We go off to a different thread to do some work, then post a message back to send the reply to the synchronous message. Even though it's shutdown when RMF is going away, if the reply is not thread, the renderer will be hung and it won't have a clean shutdown. It might even happen without shutdown, say if the process is closed while it has an outstanding synchronou XHR. Then we'd have a zombie renderer.I think this is a fine example. John, in your experience, do you think this is the common case for RefCountedThreadSafe objects? My hunch is that it isn't. I feel like more would rather that the default for RunnableMethodTraits not keep their lifetimes across posted tasks. Maybe all those cases will go away once you fix ScopedRunnableMethodFactory to be thread-safe. Btw, how does this work? Will you have a lock? Does that mean that if thread A posts a ScopedRunnableMethodFactory::NewRunnableMethod to thread B, then thread A could potentially block until thread B finishes running the task (assuming thread B already started running the task before the factory tried to revoke it)?Definitely we would want to avoid one thread blocking on another, since that'd lead to deadlocks. I was thinking that perhaps there would be a way to post task to a static member function, along with passing an extra parameter that allows one to run a method on the original thread. This would solve a common use case of going to the file thread to do a slow operation, and then wanting to get back to the original thread. Even with objects that currently derive from RefCountedThreadSafe, often many of the member variables are not thread safe, and the user passes the needed information using parameters. The above would get rid of a bunch of hacky code I've personally written :)
Doh, I should have read your email before tonight, so I could have talked to you about this today in person. I fail. Anyway, I think that for this case, you can probably use base::WeakPtr. Construct a Task (1) that keeps a base::WeakPtr<T>. In Run(), it checks ptr_.get() to make sure it's non-NULL, and if so, invokes a function pointer with the params. You can imagine a NewWeakRunnableMethod() implementation that does this. Take this task and pass it as a parameter to the task you're posting to the static member function (2). NewWeakRunnableMethod()'s constructed Task can inherit from NonThreadSafe to help enforce safety.