Bug 830704: Delete UnixSocketImpl instance after SocketReceiveTasks completed [r=qdot]

The Bluetooth system internally uses UnixSocketImpl when transfering
files. When Bluetooth gets disabled during a file transfer, the IPC code
deletes any related instance of UnixSocketImpl. This can happen before all
pending SocketReceiveTasks have been processed by the main thread. The
implementation of SocketReceiveTask uses a reference to the instance of
UnixSocketImpl that has just deen disabled. This results in a segmantation
fault.

This patch fixes the problem by scheduling the delete operation for
UnixSocketImpl to be executed after any pending SocketReceiveTasks.
This commit is contained in:
Thomas Zimmermann 2013-01-16 10:21:49 +08:00
parent 079048695a
commit dfe0efbdcd

View File

@ -253,12 +253,24 @@ private:
}; };
static void template<class T>
DestroyImpl(UnixSocketImpl* impl) class DeleteInstanceRunnable : public nsRunnable
{ {
MOZ_ASSERT(impl); public:
delete impl; DeleteInstanceRunnable(T* aInstance)
} : mInstance(aInstance)
{ }
NS_IMETHOD Run()
{
delete mInstance;
return NS_OK;
}
private:
T* mInstance;
};
class OnSocketEventTask : public nsRunnable class OnSocketEventTask : public nsRunnable
{ {
@ -612,9 +624,18 @@ UnixSocketConsumer::CloseSocket()
// Line it up to be destructed on the IO Thread // Line it up to be destructed on the IO Thread
impl->mConsumer.forget(); impl->mConsumer.forget();
impl->StopTask(); impl->StopTask();
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
NewRunnableFunction(DestroyImpl, // The receiver task should have been stopped at this point, but
impl)); // SocketReceiverTask runnables might still be pending the main
// thread. We enqueue the DeleteInstanceRunnable _after_ any pending
// SocketReceiverTask. Otherwise we might free 'impl' before those
// runnables have been executed.
nsRefPtr<nsIRunnable> t(new DeleteInstanceRunnable<UnixSocketImpl>(impl));
NS_ENSURE_TRUE_VOID(t);
nsresult rv = NS_DispatchToMainThread(t);
NS_ENSURE_SUCCESS_VOID(rv);
t.forget();
NotifyDisconnect(); NotifyDisconnect();
} }