Coordinating profile owned object shutdown across threads sucks. I'd like to discuss preferred paradigms for handling this so we can recommend a "best practice". I think any solution that is actually complete is going to look ugly and complicated. Let me throw out some straw man proposals for people to criticize and come up with better ones. Note when I say profile owned object, I don't just mean ProfileKeyedServices, but things that are transitively owned by the Profile too. And these proposals don't strictly apply to profile owned objects, but I think those are the most common cases that people run into.
==========
(1) Refcounted class that is used on multiple threads (possibly a RefcountedProfileKeyedService)
As we primarily use message passing instead of locking in Chromium, a refcounted class like this would generally segregate its member variables by thread they were accessed on. Profile destruction starts on the UI thread, so typically these classes have a ShutdownOnUIThread() member function, which destroys the UI thread state and posts messages to the other threads that access this object, such as ShutdownOnIOThread() and what not. These should nuke their thread specific state and note that the object has been shutdown on that thread (is_shutdown_on_ui_thread_ or what not) so that it can reject future messages on the thread by checking |is_shutdown_on_ui_thread_| prior to actually doing anything. One way to combine this would be to use a scoped_ptr<Foo::UIState> member that aggregated all the UI thread state for the class. The scoped_ptr would be reset during the ShutdownOnUIThread() call. All messages received on the UI thread would check |ui_state_ != NULL| (the use of a scoped_ptr will help make it obvious that member function definitions should check for NULL). Note that all state should be destroyed manually in the ShutdownOnFooThread() member functions, rather than relying on the destructor, since it is unclear which thread the destructor runs on. A disadvantage of this approach is that ownership is less obvious due to the use of refcounting, but the ownership concern is less relevant given the fact that all state is freed in the ShutdownOnFooThread() calls.
(2) Non-refcounted class that is used on multiple threads, using thread-specific WeakPtrs to track object shutdown on specific threads
Rather than relying on checking |is_shutdown_on_ui_thread_| style checks in each member function, we can use thread-specific scoped_ptr<WeakPtrFactory> members. Use an InitializeOnFooThread() and ShutdownOnFooThread() member functions to construct/destroy these weak pointer factories. We would still have segregation of member variables by thread, and destruction of this state by thread too. These thread-specific weak pointer factories allow for thread-specific WeakPtrs. An obvious disadvantage of this approach would be that the WeakPtrFactory must be constructed on a thread first before that thread's WeakPtr can be passed to another thread for use. For example, for an object used on the UI and IO threads and constructed on the UI thread, the UI thread would need an IO thread WeakPtr in order to safely post messages to the IO thread. This means it'd have to roundtrip through the InitializeOnIOThread() member function back to the UI thread to get the appropriate WeakPtr, which is clearly ugly and often unacceptable. Also, using this technique requires a clear shutdown ordering, for example, ShutdownOnUIThread() posting a deletion message to the IO thread. On the upside, that makes the shutdown ordering well-understood. The reliance on WeakPtrs obviates the need for refcounting and keeps the ownership semantics clean.
(3) Non-refcounted class with a refcounted inner class.
An example of this is URLFetcher. This works when the interface is simple and only accessed on a single thread, although the internals of the class may be accessed on multiple threads. This keeps the ownership clear since the interface is not refcounted, but it only works if the object does not need to be accessed on multiple threads. It should probably still use ShutdownOnFooThread() mechanisms and segregation of state by thread.
==========
Thoughts? I've historically favored (2) for some of the complicated cases, but I think the kludginess of it is overwhelming and have changed my mind on recommending it. I'd like to use (3) where possible, and despite its warts, recommend (1) for the more complicated cases. I'm very curious to hear what others think about how to handle shutdown of these objects correctly. I think having an agreed paradigm and documented practice would be good for folks.