Just as a general tip, in many cases where you might use refcounting to allow an object to be safely used on multiple threads, you can often find a way to use WeakPtrFactory instead, which is almost always safer and easier to reason about.PK
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
Unfortunately, that document (http://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures) describes WeakPtr as "mostly thread-unsafe", and that "it should only be used on the original thread it was created on". Also, the header file says "When you get a WeakPtr ... the WeakPtr becomes bound to the current thread." And, the fatal blow: "You may only dereference the WeakPtr on that thread". Ouch ouch ouch!I don't know if I'm typical of most engineers here, but these statements are enough to scare me off using WeakPtr altogether, in favor of using RefCountedThreadSafe (which feels impossible to use wrongly, and therefore much safer).If I can't "use" (whatever that means) a WeakPtr on a different thread, surely that makes the class almost useless? What am I missing?If you are editing that document, and you understand WeakPtr, please explain when it is safe to use, and why it's safe in those circumstances. Otherwise, I'm simply too scared to use it at all! :)Lambros
Ah, that makes sense! So, by "using" a WeakPtr, we mean either "dereferencing it to call a method on it", or "dereferencing it to destroy the object pointed to" (which is the same thing, really).
In the case where the WeakPtr is unsafe to use (deferencing it in two different threads), then the operation is inherently unsafe anyway on the underlying object (without locks). Gotcha! So the WeakPtr interface isn't introducing any extra danger in that case.
It does mean, however, that if you have an object that might be destroyed on a different thread (and you have locks to deal with that if necessary), the WeakPtr class doesn't help. Of course, the better solution is to have the would-be-destroyer post a task to the proper thread, instead of destroying it directly.
I'm now guessing that the reason we have WeakPtrFactory is so we can ensure that the WeakPtr gets created on the correct thread. So the shortcoming of the WeakPtr interface (the fact that it's thread-unsafe) is addressed by the WeakPtrFactory class. Have I got this right?
Ah, that makes sense! So, by "using" a WeakPtr, we mean either "dereferencing it to call a method on it", or "dereferencing it to destroy the object pointed to" (which is the same thing, really).In the case where the WeakPtr is unsafe to use (deferencing it in two different threads), then the operation is inherently unsafe anyway on the underlying object (without locks). Gotcha! So the WeakPtr interface isn't introducing any extra danger in that case.It does mean, however, that if you have an object that might be destroyed on a different thread (and you have locks to deal with that if necessary), the WeakPtr class doesn't help. Of course, the better solution is to have the would-be-destroyer post a task to the proper thread, instead of destroying it directly.I'm now guessing that the reason we have WeakPtrFactory is so we can ensure that the WeakPtr gets created on the correct thread. So the shortcoming of the WeakPtr interface (the fact that it's thread-unsafe) is addressed by the WeakPtrFactory class. Have I got this right?Thanks for helping me understand this. I still think the documentation could be improved to make others less scared of using this class. I will try to digest Ryan's reply as well, before tackling this further.
Lambros
On Mon, Jul 2, 2012 at 10:46 AM, Peter Kasting <pkas...@chromium.org> wrote:
One obviously-safe technique is to ensure that all accesses to your object happen on the same thread, even if callers might normally live on different threads. Then you can vend weak pointers to all the callers and they can rely on PostTask()/PostTaskAndReply()/etc. auto-cancelling once the weak pointers are NULLed. This is an appealing route anyway because it means your object doesn't need locks to protect members that get accessed across threads, which makes your class simpler, safer, and more performant.PK
Secondly, the important threading constraint is that WeakPtrs should only ever be dereferenced on the thread on which the object they refer to will be deleted.
On Mon, Jul 2, 2012 at 3:04 PM, Peter Kasting <pkas...@chromium.org> wrote:
On Mon, Jul 2, 2012 at 2:21 PM, Wez <w...@chromium.org> wrote:Secondly, the important threading constraint is that WeakPtrs should only ever be dereferenced on the thread on which the object they refer to will be deleted.Wait, is that right? Or is it that a WeakPtr must only by accessed on the thread on which the WeakPtr was created?(I realize that in the scenario I outlined, where an object is only accessed on one thread, and WeakPtrs are all vended on that thread, the distinction is immaterial.)PKThe WeakPtr must only be accessed on the thread on which the WeakPtr was created.
Generally, this will/should be the same thread on which the object-pointed-to will also be deleted.
The distinction is that, with a WeakPtrFactory does not necessarily have to be a member of the object-being-pointed-to - it COULD be that ClassA owns-a ClassB* (class_b_) and ClassA has-a WeakPtrFactory<ClassB> (weak_ptr_factory_ , initialized with WeakPtrFactory(class_b_) )
On Mon, Jul 2, 2012 at 4:16 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Mon, Jul 2, 2012 at 3:04 PM, Peter Kasting <pkas...@chromium.org> wrote:
On Mon, Jul 2, 2012 at 2:21 PM, Wez <w...@chromium.org> wrote:Secondly, the important threading constraint is that WeakPtrs should only ever be dereferenced on the thread on which the object they refer to will be deleted.Wait, is that right? Or is it that a WeakPtr must only by accessed on the thread on which the WeakPtr was created?(I realize that in the scenario I outlined, where an object is only accessed on one thread, and WeakPtrs are all vended on that thread, the distinction is immaterial.)PKThe WeakPtr must only be accessed on the thread on which the WeakPtr was created.This is enforced by a ThreadChecker, so definitely true in practice.Generally, this will/should be the same thread on which the object-pointed-to will also be deleted.Better to do all of these on 1 thread. But if it wasn't for the use of ThreadChecker, the thread it's deleted on would matter more. You don't want a race where the pointer appears to be valid just before you use it.The distinction is that, with a WeakPtrFactory does not necessarily have to be a member of the object-being-pointed-to - it COULD be that ClassA owns-a ClassB* (class_b_) and ClassA has-a WeakPtrFactory<ClassB> (weak_ptr_factory_ , initialized with WeakPtrFactory(class_b_) )I would advise against doing the above. SupportsWeakPtr is a safer way. It's critical that the lifetime of the WeakPtrFactory is scoped to the lifetime of its pointee, or all bets are off. If you separate the two, you're opening yourself up to make a mistake about their order of destruction.
On Mon, Jul 2, 2012 at 10:33 AM, Lambros Lambrou <lambros...@chromium.org> wrote:
Unfortunately, that document (http://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures) describes WeakPtr as "mostly thread-unsafe", and that "it should only be used on the original thread it was created on". Also, the header file says "When you get a WeakPtr ... the WeakPtr becomes bound to the current thread." And, the fatal blow: "You may only dereference the WeakPtr on that thread". Ouch ouch ouch!I don't know if I'm typical of most engineers here, but these statements are enough to scare me off using WeakPtr altogether, in favor of using RefCountedThreadSafe (which feels impossible to use wrongly, and therefore much safer).If I can't "use" (whatever that means) a WeakPtr on a different thread, surely that makes the class almost useless? What am I missing?If you are editing that document, and you understand WeakPtr, please explain when it is safe to use, and why it's safe in those circumstances. Otherwise, I'm simply too scared to use it at all! :)LambrosWith WeakPtr, they can be copy-constructed and destructed on arbitrary threads. They're thread-safe in that regard, as they're necessary for base::Bind/base::Callback semantics. What they're not thread-safe about is de-referencing, which the docs specifically call out.Largely, it means that this pattern is UNSAFE// Called on file threadvoid MyClass::DoSomethingOnFileThread() {// Do some workint result = DoSomeWork();// NOT SAFE.io_thread_->PostTask(FROM_HERE, base::Bind(&MyClass::DoSomethingOnIOThread, weak_ptr_factory_.GetWeakPtr(), result);}This is because the returned WeakPtr is created on the FILE thread, but will be dereferenced on the IO thread.However, the encouragement for WeakPtr is that it can be used to decouple the lifetimes between callers, particularly when used with PostTaskAndReply/PostTaskAndReplyWithResult.// Called on FILE threadvoid ClassA::DoSomething() {class_b->DoSomethingElse(base::Bind(&ClassA::HandleResults, weak_ptr_factory_.GetWeakPtr()));}// Called on ANY thread.// |callback| will be invoked on the same thread this is called from. In this case, the FILE thread.void ClassB::DoSomethingElse(const base::Callback<int>& calback) {PostTaskAndReplyWithHelper(io_thread_, FROM_HERE, base::Bind(&ClassB::DoSomethingOnIOThread, base::Unretained(this)), callback);}
On Mon, Jul 2, 2012 at 3:49 PM, David Michael <dmic...@google.com> wrote:
On Mon, Jul 2, 2012 at 4:16 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Mon, Jul 2, 2012 at 3:04 PM, Peter Kasting <pkas...@chromium.org> wrote:
On Mon, Jul 2, 2012 at 2:21 PM, Wez <w...@chromium.org> wrote:Secondly, the important threading constraint is that WeakPtrs should only ever be dereferenced on the thread on which the object they refer to will be deleted.Wait, is that right? Or is it that a WeakPtr must only by accessed on the thread on which the WeakPtr was created?(I realize that in the scenario I outlined, where an object is only accessed on one thread, and WeakPtrs are all vended on that thread, the distinction is immaterial.)PKThe WeakPtr must only be accessed on the thread on which the WeakPtr was created.This is enforced by a ThreadChecker, so definitely true in practice.Generally, this will/should be the same thread on which the object-pointed-to will also be deleted.Better to do all of these on 1 thread. But if it wasn't for the use of ThreadChecker, the thread it's deleted on would matter more. You don't want a race where the pointer appears to be valid just before you use it.The distinction is that, with a WeakPtrFactory does not necessarily have to be a member of the object-being-pointed-to - it COULD be that ClassA owns-a ClassB* (class_b_) and ClassA has-a WeakPtrFactory<ClassB> (weak_ptr_factory_ , initialized with WeakPtrFactory(class_b_) )I would advise against doing the above. SupportsWeakPtr is a safer way. It's critical that the lifetime of the WeakPtrFactory is scoped to the lifetime of its pointee, or all bets are off. If you separate the two, you're opening yourself up to make a mistake about their order of destruction.No, it is not at all critical that these two be related. Providing this disconnect is one of the key points of InvalidateWeakPtrs.
Since InvalidateWeakPtrs can only be called on the thread that the WeakPtrs run/will be dereferenced, you have a 100% guarantee that "some other thread" will not be in the pointee's code - because they're in your code. So it is totally valid (and totally safe) to do something like:// Runs on IO threadvoid ClassA::CalledOnIOThread() {file_thread_->PostTask(FROM_HERE, base::Bind(&ClassA::CleanupOnFileThread, base::Unretrained(this));}// Runs on FILE threadvoid ClassA::CleanupOnFileThread() {// weak_ptr_factory_ holds-a ClassB// weak_ptr_factory_ and ClassB are only ever used on the FILE threadweak_ptr_factory_->InvalidateWeakPtrs();// Not strictly necessary - but this pattern here is a sanity check and reflects the general problem with// objects living on multiple threads. The assumption here is that some other method on ClassA may be// pending on the FILE thread that will attempt to vend a WeakPtr.weak_ptr_factory_.reset();io_thread_->PostTask(FROM_HERE, base::Bind(&ClassA::CleanupComplete, base::Unretained(this));}// Called on the IO threadvoid ClassA::CleanupComplete() {// Totally safe to do. All of the WeakPtrs have been invalidated, so nobody on the FILE thread can or// will be using |class_b_| at this point.class_b_.release();}The discussion here is the normal discussion of inheritance vs composition, and is similar of the discussion of external reference counting vs intrusive reference counting. In Chromium code, the style guide strongly encourages composition, for which the above is totally safe to do.SupportsWeakPtr<> can be as dangerous as reference counting, and requires care and forethought. Using SupportsWeakPtr<> publicly exposes the ability to vend WeakPtrs. This can lead to callers having a Foo* and trying to vend a WeakPtr<Foo> from it - which they may be on the wrong thread and this may not be safe to do!
Or more general: What is the desired pattern to call yourself on another thread?