diff --git a/netwerk/base/src/Dashboard.cpp b/netwerk/base/src/Dashboard.cpp index ff930194c7a..c4232b07133 100644 --- a/netwerk/base/src/Dashboard.cpp +++ b/netwerk/base/src/Dashboard.cpp @@ -25,25 +25,6 @@ struct ConnStatus nsString creationSts; }; -class DashConnStatusRunnable: public nsRunnable -{ -public: - DashConnStatusRunnable(Dashboard * aDashboard, ConnStatus aStatus) - : mDashboard(aDashboard) - { - mStatus.creationSts = aStatus.creationSts; - } - - NS_IMETHODIMP Run() - { - return mDashboard->GetConnectionStatus(mStatus); - } - -private: - ConnStatus mStatus; - Dashboard * mDashboard; -}; - Dashboard::Dashboard() { mEnableLogging = false; @@ -604,7 +585,8 @@ Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort, if (NS_FAILED(rv)) { ConnStatus status; CopyASCIItoUTF16(GetErrorString(rv), status.creationSts); - nsCOMPtr event = new DashConnStatusRunnable(this, status); + nsCOMPtr event = + NS_NewRunnableMethodWithArg(this, &Dashboard::GetConnectionStatus, status); mConn.thread->Dispatch(event, NS_DISPATCH_NORMAL); return rv; } @@ -674,7 +656,7 @@ Dashboard::OnTransportStatus(nsITransport *aTransport, nsresult aStatus, ConnStatus status; CopyASCIItoUTF16(GetErrorString(aStatus), status.creationSts); - nsCOMPtr event = new DashConnStatusRunnable(this, status); + nsCOMPtr event = NS_NewRunnableMethodWithArg(this, &Dashboard::GetConnectionStatus, status); mConn.thread->Dispatch(event, NS_DISPATCH_NORMAL); return NS_OK; @@ -693,7 +675,7 @@ Dashboard::Notify(nsITimer *timer) ConnStatus status; status.creationSts.Assign(NS_LITERAL_STRING("NS_ERROR_NET_TIMEOUT")); - nsCOMPtr event = new DashConnStatusRunnable(this, status); + nsCOMPtr event = NS_NewRunnableMethodWithArg(this, &Dashboard::GetConnectionStatus, status); mConn.thread->Dispatch(event, NS_DISPATCH_NORMAL); return NS_OK; diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h index 0e2d1798d97..82a555766c5 100644 --- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -310,16 +310,29 @@ public: typedef typename ReturnTypeEnforcer::ReturnTypeIsSafe check; }; -template +template struct nsRunnableMethodReceiver { ClassType *mObj; - nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) { NS_IF_ADDREF(mObj); } - ~nsRunnableMethodReceiver() { Revoke(); } + Arg mArg; + nsRunnableMethodReceiver(ClassType *obj, Arg arg) + : mObj(obj) + , mArg(arg) + { NS_IF_ADDREF(mObj); } + ~nsRunnableMethodReceiver() { Revoke(); } + void Revoke() { NS_IF_RELEASE(mObj); } +}; + +template +struct nsRunnableMethodReceiver { + ClassType *mObj; + nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) + { NS_IF_ADDREF(mObj); } + ~nsRunnableMethodReceiver() { Revoke(); } void Revoke() { NS_IF_RELEASE(mObj); } }; template -struct nsRunnableMethodReceiver { +struct nsRunnableMethodReceiver { ClassType *mObj; nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) {} void Revoke() { mObj = nullptr; } @@ -327,28 +340,70 @@ struct nsRunnableMethodReceiver { template struct nsRunnableMethodTraits; +template +struct nsRunnableMethodTraits { + typedef C class_type; + typedef R return_type; + typedef A arg_type; + typedef nsRunnableMethod base_type; +}; + template struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; + typedef void arg_type; typedef nsRunnableMethod base_type; }; #ifdef NS_HAVE_STDCALL +template +struct nsRunnableMethodTraits { + typedef C class_type; + typedef R return_type; + typedef A arg_type; + typedef nsRunnableMethod base_type; +}; + template struct nsRunnableMethodTraits { typedef C class_type; typedef R return_type; + typedef void arg_type; typedef nsRunnableMethod base_type; }; #endif -template +template class nsRunnableMethodImpl : public nsRunnableMethodTraits::base_type { typedef typename nsRunnableMethodTraits::class_type ClassType; - nsRunnableMethodReceiver mReceiver; + nsRunnableMethodReceiver mReceiver; + Method mMethod; +public: + nsRunnableMethodImpl(ClassType *obj, + Method method, + Arg arg) + : mReceiver(obj, arg) + , mMethod(method) + {} + NS_IMETHOD Run() { + if (MOZ_LIKELY(mReceiver.mObj)) + ((*mReceiver.mObj).*mMethod)(mReceiver.mArg); + return NS_OK; + } + void Revoke() { + mReceiver.Revoke(); + } +}; + +template +class nsRunnableMethodImpl + : public nsRunnableMethodTraits::base_type +{ + typedef typename nsRunnableMethodTraits::class_type ClassType; + nsRunnableMethodReceiver mReceiver; Method mMethod; public: @@ -383,14 +438,32 @@ template typename nsRunnableMethodTraits::base_type* NS_NewRunnableMethod(PtrType ptr, Method method) { - return new nsRunnableMethodImpl(ptr, method); + return new nsRunnableMethodImpl(ptr, method); +} + +template +struct dependent_type +{ + typedef T type; +}; + + +// Similar to NS_NewRunnableMethod. Call like so: +// Type myArg; +// nsCOMPtr event = +// NS_NewRunnableMethodWithArg(myObject, &MyClass::HandleEvent, myArg); +template +typename nsRunnableMethodTraits::base_type* +NS_NewRunnableMethodWithArg(PtrType ptr, Method method, typename dependent_type::type arg) +{ + return new nsRunnableMethodImpl(ptr, method, arg); } template typename nsRunnableMethodTraits::base_type* NS_NewNonOwningRunnableMethod(PtrType ptr, Method method) { - return new nsRunnableMethodImpl(ptr, method); + return new nsRunnableMethodImpl(ptr, method); } #endif // XPCOM_GLUE_AVOID_NSPR diff --git a/xpcom/tests/TestThreadUtils.cpp b/xpcom/tests/TestThreadUtils.cpp new file mode 100644 index 00000000000..6ff0b3c330d --- /dev/null +++ b/xpcom/tests/TestThreadUtils.cpp @@ -0,0 +1,146 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http:mozilla.org/MPL/2.0/. */ + +#include "TestHarness.h" +#include "nsThreadUtils.h" + +enum { + TEST_CALL_VOID_ARG_VOID_RETURN, + TEST_CALL_VOID_ARG_NONVOID_RETURN, + TEST_CALL_NONVOID_ARG_VOID_RETURN, + TEST_CALL_NONVOID_ARG_NONVOID_RETURN, + TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT, + TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT, +#ifdef HAVE_STDCALL + TEST_STDCALL_VOID_ARG_VOID_RETURN, + TEST_STDCALL_VOID_ARG_NONVOID_RETURN, + TEST_STDCALL_NONVOID_ARG_VOID_RETURN, + TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN, + TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT, +#endif + MAX_TESTS +}; + +bool gRunnableExecuted[MAX_TESTS]; + +class nsFoo : public nsISupports { + NS_DECL_ISUPPORTS + nsresult DoFoo(bool* aBool) { + *aBool = true; + return NS_OK; + } + virtual ~nsFoo() {} +}; + +NS_IMPL_ISUPPORTS0(nsFoo) + +class nsBar : public nsISupports { + NS_DECL_ISUPPORTS + virtual ~nsBar() {} + void DoBar1(void) { + gRunnableExecuted[TEST_CALL_VOID_ARG_VOID_RETURN] = true; + } + nsresult DoBar2(void) { + gRunnableExecuted[TEST_CALL_VOID_ARG_NONVOID_RETURN] = true; + return NS_OK; + } + void DoBar3(nsFoo* aFoo) { + aFoo->DoFoo(&gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN]); + } + nsresult DoBar4(nsFoo* aFoo) { + return aFoo->DoFoo(&gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN]); + } + void DoBar5(nsFoo* aFoo) { + if (aFoo) + gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true; + } + nsresult DoBar6(char* aFoo) { + if (strlen(aFoo)) + gRunnableExecuted[TEST_CALL_NONVOID_ARG_NONVOID_RETURN_EXPLICIT] = true; + return NS_OK; + } +#ifdef HAVE_STDCALL + void __stdcall DoBar1std(void) { + gRunnableExecuted[TEST_STDCALL_VOID_ARG_VOID_RETURN] = true; + } + nsresult __stdcall DoBar2std(void) { + gRunnableExecuted[TEST_STDCALL_VOID_ARG_NONVOID_RETURN] = true; + return NS_OK; + } + void __stdcall DoBar3std(nsFoo* aFoo) { + aFoo->DoFoo(&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN]); + } + nsresult __stdcall DoBar4std(nsFoo* aFoo) { + return aFoo->DoFoo(&gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_NONVOID_RETURN]); + } + void __stdcall DoBar5std(nsFoo* aFoo) { + if (aFoo) + gRunnableExecuted[TEST_STDCALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true; + } + nsresult __stdcall DoBar6std(char* aFoo) { + if (strlen(aFoo)) + gRunnableExecuted[TEST_CALL_NONVOID_ARG_VOID_RETURN_EXPLICIT] = true; + return NS_OK; + } +#endif +}; + +NS_IMPL_ISUPPORTS0(nsBar) + +int main(int argc, char** argv) +{ + ScopedXPCOM xpcom("ThreadUtils"); + NS_ENSURE_FALSE(xpcom.failed(), 1); + + memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool)); + // Scope the smart ptrs so that the runnables need to hold on to whatever they need + { + nsRefPtr foo = new nsFoo(); + nsRefPtr bar = new nsBar(); + + // This pointer will be freed at the end of the block + // Do not dereference this pointer in the runnable method! + nsFoo * rawFoo = new nsFoo(); + + // Read only string. Dereferencing in runnable method to check this works. + char* message = (char*)"Test message"; + + NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar1)); + NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar2)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< nsRefPtr > + (bar, &nsBar::DoBar3, foo)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< nsRefPtr > + (bar, &nsBar::DoBar4, foo)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar5, rawFoo)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar6, message)); +#ifdef HAVE_STDCALL + NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar1std)); + NS_DispatchToMainThread(NS_NewRunnableMethod(bar, &nsBar::DoBar2std)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< nsRefPtr > + (bar, &nsBar::DoBar3std, foo)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg< nsRefPtr > + (bar, &nsBar::DoBar4std, foo)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar5std, rawFoo)); + NS_DispatchToMainThread(NS_NewRunnableMethodWithArg(bar, &nsBar::DoBar6std, message)); +#endif + + delete rawFoo; + } + + // Spin the event loop + NS_ProcessPendingEvents(nullptr); + + int result = 0; + + for (uint32_t i = 0; i < MAX_TESTS; i++) { + if (gRunnableExecuted[i]) { + passed("Test %d passed",i); + } else { + fail("Error in test %d", i); + result = 1; + } + } + + return result; +} diff --git a/xpcom/tests/moz.build b/xpcom/tests/moz.build index 79d3f9a338b..ea9825639d7 100644 --- a/xpcom/tests/moz.build +++ b/xpcom/tests/moz.build @@ -78,6 +78,7 @@ CPP_UNIT_TESTS += [ 'TestRefPtr.cpp', 'TestTArray.cpp', 'TestTextFormatter.cpp', + 'TestThreadUtils.cpp' ] if CONFIG['MOZ_MEMORY']: