>> In bug 1218297 we saw a case where dispatch to a thread (the socket
>> transport service thread in this case) fails because the thread has
>> already shut down.  In our brave new world, nsThread simply leaks the
>> runnable.  It can't release the reference it holds, because that would
>> reintroduce the race condition we wanted to avoid, and it can't
>> release the reference on the correct thread so it's already gone away.
>>
>
>I am a bit confused with this, if the executing thread has already shut
>down, why would releasing the reference dispatching thread holds
>reintroduce the race condition? Who is racing with dispatching thread?

It doesn't re-introduce a race.  What it may do is cause the same effect
as a lost race, whereby an object meant to be released on the target
thread gets released on the sending thread.  If the object is a
non-threadsafe object (say, for example, a JS success or failure
callback object), then releasing it on the sending thread is Bad.  This
is the same thing that can happen in a race where you have:

What used to happen:
  {
    RefPtr<Foo> bar = new FooRunnable(nonthreadsafe_object);
    target_thread->Dispatch(bar, ...);
    // Note: Dispatch in this (old-style) case takes a new reference to
    // bar.  And the runnable may run and release that reference before
    // Dispatch() returns!  And if Dispatch() fails, you always 'lose'
    // the race.
  }
  // bar drops the reference.  If the runnable ran already, this is
  // the last reference and it gets destroyed here.  If it hasn't run,
  // it gets destroyed on the target thread.

New way (already_AddRefed<>):
  {
    RefPtr<Foo> bar = new FooRunnable(nonthreadsafe_object);
    target_thread->Dispatch(bar.forget(), ...);
    // bar is always destroyed on the target thread - unless
    // Dispatch() fails, in which case it leaks (for safety).
  }

or:
  other_thread->Dispatch(do_AddRef(new FooRunnable(nonthreadsafe_object)),
                         ...);

If you use the old-style (raw ptr Dispatch) as a lot of the tree still
does, it will now leak on Dispatch() failure instead of possibly
wrong-thread releasing:

nsThread.h:
  nsresult Dispatch(nsIRunnable* aEvent, uint32_t aFlags) {
    return Dispatch(nsCOMPtr<nsIRunnable>(aEvent).forget(), aFlags);
  }

Since Dispatch(already_AddRefed<>,...) leaks on failure, this means that
Dispatch(raw_ptr,...) now leaks on failure (which is what engendered
this discussion).  You can override this behavior with a check of the
result of Dispatch() and a manual Release if it fails (and you know
the object is entirely threadsafe).

We could add DispatchFallible() (DispatchNeverLeaks()?) if that's a
wide-enough pattern; part of my question in that case is "did the caller
really expect Dispatch to possibly fail, and what implications does such
a failure have to the rest of the system?"

-- 
Randell Jesup, Mozilla Corp
remove "news" for personal email
_______________________________________________
dev-platform mailing list
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform

Reply via email to