Ref-counted objects - use scoped_refptr; but better yet, rethink your design. Reference-counted objects make it difficult to understand ownership and destruction order, especially when multiple threads are involved. There is almost always another way to design your object hierarchy to avoid refcounting. Avoiding refcounting in multithreaded situations is usually easier if you restrict each class to operating on just one thread, and use PostTask() and the like to proxy calls to the correct thread. base::Bind(), WeakPtr, and other tools make it possible to automatically cancel calls to such an object when it dies. Note that too much of our existing code uses refcounting, so just because you see existing code doing it does not mean it's the right solution. (Bonus points if you're able to clean up such cases.)
Hi Chromium-dev,A quick question about ref-counted objects that are multi-threaded:According to http://www.chromium.org/developers/smart-pointer-guidelines :Ref-counted objects - use scoped_refptr; but better yet, rethink your design. Reference-counted objects make it difficult to understand ownership and destruction order, especially when multiple threads are involved. There is almost always another way to design your object hierarchy to avoid refcounting. Avoiding refcounting in multithreaded situations is usually easier if you restrict each class to operating on just one thread, and use PostTask() and the like to proxy calls to the correct thread. base::Bind(), WeakPtr, and other tools make it possible to automatically cancel calls to such an object when it dies. Note that too much of our existing code uses refcounting, so just because you see existing code doing it does not mean it's the right solution. (Bonus points if you're able to clean up such cases.)I'm a little confused about the above guidance. As an example, let's consider two designs:(1) An instance of Class [A] owns a scoped_ptr to an instance of Class [B]. Class [A] interacts on the UI thread and is only used on the UI thread. However, it touches the filesystem and so delegates some tasks to Class [B], which only performs actions on the FILE thread (or in the blocking pool, etc, not really important). So [A] gets a call to load a file, and posts a task to call a member function of [B] on the FILE thread, passing a WeakPtr in the callback for the posted task.(2) As above, but instead Class [A] owns a scoped_refptr to an instance of Class [B], and gives a scoped_refptr to the callback for the posted task.It seems like (1) is the recommended design, and (2) is discouraged. But it seems to me that (1) suffers from a fatal flaw that (2) does not: if the instance of [A] is deleted (say the browser shuts down which somehow causes the instance of [A]'s deletion), then the instance of [B] is deleted, possibly while its member function is running. This can mean (among other things) that the member function of [B] is highly likely to cause a crash. The fact that the callback will not begin running if the WeakPtr is invalid doesn't make a difference, because the callback may already be midway through execution when the WeakPtr is invalidated. And it's not necessarily possible to post a task to proxy B's destructor, either, since there might not be anything to post a task to during browser shutdown.
After some discussion with my team, we've concluded that when passing callbacks between threads, if [A] can be freed during browser shutdown, ref-counting is pretty much always necessary. As far as I can tell, the owning class could be freed at any time if it is owned either directly or indirectly by g_browser_process. And once that's happening, you can't post a task to the FILE thread to destroy [B], because the threads are already shut down and posting tasks won't be useful anymore.Am I missing some basic insight? Or some way around this problem without using ref counting? (Maybe the only solution is just to leak everything during shutdown?)Thanks for your guidance,
- Josh
--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.
(1) An instance of Class [A] owns a scoped_ptr to an instance of Class [B]. Class [A] interacts on the UI thread and is only used on the UI thread. However, it touches the filesystem and so delegates some tasks to Class [B], which only performs actions on the FILE thread (or in the blocking pool, etc, not really important). So [A] gets a call to load a file, and posts a task to call a member function of [B] on the FILE thread, passing a WeakPtr in the callback for the posted task.
(2) As above, but instead Class [A] owns a scoped_refptr to an instance of Class [B], and gives a scoped_refptr to the callback for the posted task.
After some discussion with my team, we've concluded that when passing callbacks between threads, if [A] can be freed during browser shutdown, ref-counting is pretty much always necessary.
As far as I can tell, the owning class could be freed at any time if it is owned either directly or indirectly by g_browser_process. And once that's happening, you can't post a task to the FILE thread to destroy [B], because the threads are already shut down and posting tasks won't be useful anymore.
On Mon, Mar 3, 2014 at 2:31 PM, <waf...@chromium.org> wrote:
(1) An instance of Class [A] owns a scoped_ptr to an instance of Class [B]. Class [A] interacts on the UI thread and is only used on the UI thread. However, it touches the filesystem and so delegates some tasks to Class [B], which only performs actions on the FILE thread (or in the blocking pool, etc, not really important). So [A] gets a call to load a file, and posts a task to call a member function of [B] on the FILE thread, passing a WeakPtr in the callback for the posted task.
Ideally, classes are created, destroyed, and accessed all one a single thread. (Optional: creation can happen on a different thread as long as it's guaranteed to complete before any further accesses occur.) Given your description, this suggests that [A] should be UI-thread-scoped and [B] should be file-thread-scoped.
If this is the case, [A] cannot own [B] via scoped_ptr<>, since as you note, that ties [B]'s destruction to [A]'s thread, which violates the above ideal, and in this case (as in most violations) is unsafe.
-- BR Maciej Pawlowski Opera Desktop Wroclaw
--
--
W dniu 2014-03-03 23:47, Peter Kasting pisze:
If this is the case, [A] cannot own [B] via scoped_ptr<>, since as you note, that ties [B]'s destruction to [A]'s thread, which violates the above ideal, and in this case (as in most violations) is unsafe.
But if [A] doesn't own [B], how does it call its methods?
PostTask requires a pointer. [A] cannot safely operate on some pointer to an object that is owned by FILE thread because it can become invalid at any moment.
Chrome shutdown (and in general process shutdown) is a chaotic moment. End of the world by large meteorite if you will. Last thing you need to be doing is folding your clothes. So if you have a neat design don't go changing it for shutdown time. Just make sure the critical data is saved and let the objects be killed when the tread they live on is killed.
I've got meta-comments on everyone else's comments :)* Dana's suggested pattern uses WaitableEvent. The advantage of this is you don't have to yield back to the MessageLoop and post a continuation that has to wait in the MessageLoop queue again. The gains are code simplicity and potentially performance. The disadvantages are potential for jank (you really want to avoid blocking certain threads, like the UI thread) and deadlock (in Dana's example, the UI thread waits on the IO thread, which is indeed a pattern we do elsewhere like https://code.google.com/p/chromium/codesearch#chromium/src/content/browser/renderer_host/render_widget_helper.cc&sq=package:chromium&q=RenderWidgetHelper::WaitForBackingStoreMsg&type=cs&l=139...but if someone does have a WaitableEvent on the IO thread waiting for signalling on the UI thread, then we have deadlock risk). The deadlock risk means that we should not generalize this mechanism across all threads, although it's perhaps safe for the specific UI=>IO case. As far as the performance vs jank consideration, there are definitely cases like the code I linked to earlier where, it's actually better overall for performance to tolerate some thread blocking in order to get the backing store IPC sooner. That said, in general, we prefer message passing to locking (http://www.chromium.org/developers/design-documents/threading#Overview) in order to reduce blocking on jank critical threads. I don't know enough about the Chromium compositor to say whether or not it makes sense in their case, but given the user experience critical nature of the compositor, I can well believe that maybe it makes sense for them. In your case though, you really don't want to block the UI thread for as long as a filesystem operation can take, because that's an excessively long period of time not to respond to UI events. Now, you're probably only doing this on shutdown, so that's a special case. That said, we also want shutdown to proceed as quickly as possible, so I would still advise against this approach here.
-- BR Maciej Pawlowski Opera Desktop Wroclaw
--
On Wed, Mar 5, 2014 at 1:01 AM, Maciej Pawlowski <mpawl...@opera.com> wrote:
W dniu 2014-03-04 21:08, Peter Kasting pisze:
I believe there's still a design problem with passing weak_ptrs between threads. If [B] is not a child of [A] and is instead owned by *someone* in FILE thread, that someone needs to give a weak_ptr<B> to [A]. How? To PostTask for a "A::SetB" method, it has to have a weak_ptr<A>...On Tue, Mar 4, 2014 at 1:15 AM, Maciej Pawlowski <mpawl...@opera.com> wrote:
W dniu 2014-03-03 23:47, Peter Kasting pisze:But if [A] doesn't own [B], how does it call its methods?If this is the case, [A] cannot own [B] via scoped_ptr<>, since as you note, that ties [B]'s destruction to [A]'s thread, which violates the above ideal, and in this case (as in most violations) is unsafe.
PostTask requires a pointer. [A] cannot safely operate on some pointer to an object that is owned by FILE thread because it can become invalid at any moment.
[A] can hold a weak pointer to [B]. I mentioned this at the end of my email.
PK
You may need to provide a concrete example in order to discuss this more completely. Fundamentally though, if you do not have a well-defined relationship between A and B, then the object lifetimes are also not well understood, so you probably have a bug if A and B are trying to communicate. Well-defined relationship does not require direct ownership. It can be transitive ownership (i.e. ancestor/descendent relationship) or a sibling relationship (member variables of the same object, which has a well-defined construction/destruction ordering) or cousin or whatever. Now, if you have a well-defined relationship between A and B, then it is definitely possible to plumb the WeakPtr<A> via the relevant nodes in that relationship chain.
On Tue, Mar 4, 2014 at 10:17 AM, William Chan (陈智昌) <will...@chromium.org> wrote:
I've got meta-comments on everyone else's comments :)* Dana's suggested pattern uses WaitableEvent. The advantage of this is you don't have to yield back to the MessageLoop and post a continuation that has to wait in the MessageLoop queue again. The gains are code simplicity and potentially performance. The disadvantages are potential for jank (you really want to avoid blocking certain threads, like the UI thread) and deadlock (in Dana's example, the UI thread waits on the IO thread, which is indeed a pattern we do elsewhere like https://code.google.com/p/chromium/codesearch#chromium/src/content/browser/renderer_host/render_widget_helper.cc&sq=package:chromium&q=RenderWidgetHelper::WaitForBackingStoreMsg&type=cs&l=139...but if someone does have a WaitableEvent on the IO thread waiting for signalling on the UI thread, then we have deadlock risk). The deadlock risk means that we should not generalize this mechanism across all threads, although it's perhaps safe for the specific UI=>IO case. As far as the performance vs jank consideration, there are definitely cases like the code I linked to earlier where, it's actually better overall for performance to tolerate some thread blocking in order to get the backing store IPC sooner. That said, in general, we prefer message passing to locking (http://www.chromium.org/developers/design-documents/threading#Overview) in order to reduce blocking on jank critical threads. I don't know enough about the Chromium compositor to say whether or not it makes sense in their case, but given the user experience critical nature of the compositor, I can well believe that maybe it makes sense for them. In your case though, you really don't want to block the UI thread for as long as a filesystem operation can take, because that's an excessively long period of time not to respond to UI events. Now, you're probably only doing this on shutdown, so that's a special case. That said, we also want shutdown to proceed as quickly as possible, so I would still advise against this approach here.+1 for never allowing a thread to block on another. We have base/threading/thread_restrictions.h specifically to catch threads waiting on another, which is an anti-pattern.The WaitForBackingStoreMsg is an exception, and it was replaced by the cc::CompletionEvent class. I didn't know that it's used to block shutdown, since the initial change that allowed it (https://chromiumcodereview.appspot.com/11085054/) sounded like it would only replace the WaitForBackingStoreMsg wait. In hindsight, we shouldn't have made cc::CompletionEvent have the ScopedAllowWait, but instead should have put ScopedAllowWait in each callsite, thereby auditing each place that it's used in.It would be great if for shutdown, another solution is found for the compositor that doesn't involve the UI thread waiting on another.
W dniu 2014-03-05 12:30, William Chan (陈智昌) pisze:
Let's try this with the relationship hinted in the original post. Class A is a UI widget which wants to store some data on disk via class B which is its FILE delegate. Let's assume then, that [B] must write all data requested by [A] to disk before dying and that it should die more or less along with [A] (no need to keep it alive after the UI widget associated with it has died on UI thread). I intentionally don't specify whether [B] should outlive [A] or the other way around, so that there may be more ways to make this scenario work.On Wed, Mar 5, 2014 at 1:01 AM, Maciej Pawlowski <mpawl...@opera.com> wrote:
W dniu 2014-03-04 21:08, Peter Kasting pisze:
I believe there's still a design problem with passing weak_ptrs between threads. If [B] is not a child of [A] and is instead owned by *someone* in FILE thread, that someone needs to give a weak_ptr<B> to [A]. How? To PostTask for a "A::SetB" method, it has to have a weak_ptr<A>...On Tue, Mar 4, 2014 at 1:15 AM, Maciej Pawlowski <mpawl...@opera.com> wrote:
W dniu 2014-03-03 23:47, Peter Kasting pisze:But if [A] doesn't own [B], how does it call its methods?If this is the case, [A] cannot own [B] via scoped_ptr<>, since as you note, that ties [B]'s destruction to [A]'s thread, which violates the above ideal, and in this case (as in most violations) is unsafe.
PostTask requires a pointer. [A] cannot safely operate on some pointer to an object that is owned by FILE thread because it can become invalid at any moment.
[A] can hold a weak pointer to [B]. I mentioned this at the end of my email.
PK
You may need to provide a concrete example in order to discuss this more completely. Fundamentally though, if you do not have a well-defined relationship between A and B, then the object lifetimes are also not well understood, so you probably have a bug if A and B are trying to communicate. Well-defined relationship does not require direct ownership. It can be transitive ownership (i.e. ancestor/descendent relationship) or a sibling relationship (member variables of the same object, which has a well-defined construction/destruction ordering) or cousin or whatever. Now, if you have a well-defined relationship between A and B, then it is definitely possible to plumb the WeakPtr<A> via the relevant nodes in that relationship chain.
The most obvious solution would be to make A own B. This won't work - ownership semantics don't seem to be applicable with objects that live in different threads, [A] cannot have a scoped_ptr<B>.
Now if we want to make [B] live in FILE thread and be owned by *something* there, first we must figure out what that thing will be. Who stores the scoped_ptr<B> in FILE thread? Is there a different class hierarchy? A copy of the UI thread class hierarchy? A big fat FileThreadWorkers god class which owns all assorted utils and delegates that must live in FILE thread?
Then we must figure out how does that something pass a weak_ptr<B> to [A] in UI thread. Or perhaps [A] should somehow find the owner of [B] which lives in FILE thread and request a weak_ptr<B>? Can we make this transaction safe in absence of thread-safe weak_ptr and/or locks?
Third, how do we ensure that whatever owns [B] in FILE thread won't delete it before [A] schedules its last work? Remember that all changes the user makes in UI thread have to be written down to disk, we can't say "eh, that weak_ptr is dead now, let's just not run that callback".
-- BR Maciej Pawlowski Opera Desktop Wroclaw
So, I've found that the clearest (and imo best) approach was described, in part, by willchan. I have one question about it though.Let me clarify the approach.A lives on UI threadB lives on FILE thread---A creates B on UI thread in A::A() and keeps a pointer to itif B needs to do work on the file thread for initialization it should use two-phase construction(B::B() is the only part of B that will run on the UI thread)A posts-tasks to Bcallbacks back to A use a weak_ptr<A>If B posts tasks to itself, it uses a weak_ptr<B>A::~A() posts a task to delete B---One alternative is that B can post tasks back to itself without a weak_ptr<B>, and then A::~A() can just post a task telling B to delete itself when finished. I think that's not as good, but is still clear.Doing it like this means that A is never in a state where B doesn't exist. This has worked for me very well for the case where B is basically doing work for A. I haven't had a case where B is actually making requests back to A (other than the callbacks from requests to B) so I don't know how that works, how I would approach it.The real question that I have is, how should this handle shutdown? My understanding is that the FILE thread shuts down before the UI thread. However, we don't want B to be deleted before A (then A can be in a state where B is gone, so it should probably have a weak_ptr... but then it can't grab that weak_ptr in its constructor because that's the wrong thread... etc. etc.). So, maybe we want A to be deleted at a point where it can still post the delete task to the FILE thread for B. I don't know how to address this, so any tips would be appreciated.
On Wed, Mar 5, 2014 at 9:16 AM, Chris Hopman <cjho...@chromium.org> wrote:
So, I've found that the clearest (and imo best) approach was described, in part, by willchan. I have one question about it though.Let me clarify the approach.A lives on UI threadB lives on FILE thread---A creates B on UI thread in A::A() and keeps a pointer to itif B needs to do work on the file thread for initialization it should use two-phase construction(B::B() is the only part of B that will run on the UI thread)A posts-tasks to Bcallbacks back to A use a weak_ptr<A>If B posts tasks to itself, it uses a weak_ptr<B>A::~A() posts a task to delete B---One alternative is that B can post tasks back to itself without a weak_ptr<B>, and then A::~A() can just post a task telling B to delete itself when finished. I think that's not as good, but is still clear.Doing it like this means that A is never in a state where B doesn't exist. This has worked for me very well for the case where B is basically doing work for A. I haven't had a case where B is actually making requests back to A (other than the callbacks from requests to B) so I don't know how that works, how I would approach it.The real question that I have is, how should this handle shutdown? My understanding is that the FILE thread shuts down before the UI thread. However, we don't want B to be deleted before A (then A can be in a state where B is gone, so it should probably have a weak_ptr... but then it can't grab that weak_ptr in its constructor because that's the wrong thread... etc. etc.). So, maybe we want A to be deleted at a point where it can still post the delete task to the FILE thread for B. I don't know how to address this, so any tips would be appreciated.Hm, I think maybe there's some confusion on how shutdown works? Let me try to explain.
On the UI thread, we stop Run()'ing the MessageLoop. After that happens, we eventually join all the BrowserThreads. Therefore, while it's true the FILE thread shuts down before the UI thread since the UI thread is the main thread, the UI thread's MessageLoop stops before any other threads' MessageLoop stops.
On Wed, Mar 5, 2014 at 11:02 AM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Mar 5, 2014 at 9:16 AM, Chris Hopman <cjho...@chromium.org> wrote:
So, I've found that the clearest (and imo best) approach was described, in part, by willchan. I have one question about it though.Let me clarify the approach.A lives on UI threadB lives on FILE thread---A creates B on UI thread in A::A() and keeps a pointer to itif B needs to do work on the file thread for initialization it should use two-phase construction(B::B() is the only part of B that will run on the UI thread)A posts-tasks to Bcallbacks back to A use a weak_ptr<A>If B posts tasks to itself, it uses a weak_ptr<B>A::~A() posts a task to delete B---One alternative is that B can post tasks back to itself without a weak_ptr<B>, and then A::~A() can just post a task telling B to delete itself when finished. I think that's not as good, but is still clear.Doing it like this means that A is never in a state where B doesn't exist. This has worked for me very well for the case where B is basically doing work for A. I haven't had a case where B is actually making requests back to A (other than the callbacks from requests to B) so I don't know how that works, how I would approach it.The real question that I have is, how should this handle shutdown? My understanding is that the FILE thread shuts down before the UI thread. However, we don't want B to be deleted before A (then A can be in a state where B is gone, so it should probably have a weak_ptr... but then it can't grab that weak_ptr in its constructor because that's the wrong thread... etc. etc.). So, maybe we want A to be deleted at a point where it can still post the delete task to the FILE thread for B. I don't know how to address this, so any tips would be appreciated.Hm, I think maybe there's some confusion on how shutdown works? Let me try to explain.Oh, there definitely is.
On the UI thread, we stop Run()'ing the MessageLoop. After that happens, we eventually join all the BrowserThreads. Therefore, while it's true the FILE thread shuts down before the UI thread since the UI thread is the main thread, the UI thread's MessageLoop stops before any other threads' MessageLoop stops.Great. So then, if I understand this correctly, if A is destroyed properly by whoever owns it, then B will also be destroyed properly.
(Optional: creation can happen on a different thread as long as it's guaranteed to complete before any further accesses occur.)
--
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.
Posting tasks to other message loops will indeed use synchronisation primitives to ensure that (see base::MessageLoop).
--