Bug 1634846 - P3. Get around NS_INLINE_DECL_REFCOUNTING not working with TaskQueue. r=nika,froydnj

NS_INLINE_DECL_REFCOUNTING macro doesn't properly work when the object is used on a thread that isn't backed by a single PRThread (such as TaskQueue). See bug 1648031.

The resolution of this issue is rather complex, and outside the scope of this series of change.

So for now, we create a new macro NS_INLINE_DECL_REFCOUNTING_ONEVENTTHREAD which will use a different mechanism to ensure the thread-safe usage of a class.

Differential Revision: https://phabricator.services.mozilla.com/D81269
This commit is contained in:
Jean-Yves Avenard 2020-07-02 00:26:43 +00:00
parent b2cf09ec3e
commit afea3c617d
3 changed files with 91 additions and 30 deletions

View File

@ -860,7 +860,7 @@ class ManagedEndpoint {
// references!
class ActorLifecycleProxy {
public:
NS_INLINE_DECL_REFCOUNTING(ActorLifecycleProxy)
NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(ActorLifecycleProxy)
IProtocol* Get() { return mActor; }

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupportsImpl.h"
#include "mozilla/Assertions.h"
#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
# include "nsThreadUtils.h"
@ -44,4 +45,26 @@ void nsAutoOwningThread::AssertCurrentThreadOwnsMe(const char* msg) const {
bool nsAutoOwningThread::IsCurrentThread() const {
return mThread == PR_GetCurrentThread();
}
nsAutoOwningEventTarget::nsAutoOwningEventTarget()
: mTarget(GetCurrentSerialEventTarget()) {
mTarget->AddRef();
}
nsAutoOwningEventTarget::~nsAutoOwningEventTarget() {
nsCOMPtr<nsISerialEventTarget> target = dont_AddRef(mTarget);
}
void nsAutoOwningEventTarget ::AssertCurrentThreadOwnsMe(
const char* msg) const {
if (MOZ_UNLIKELY(!IsCurrentThread())) {
// `msg` is a string literal by construction.
MOZ_CRASH_UNSAFE(msg);
}
}
bool nsAutoOwningEventTarget::IsCurrentThread() const {
return mTarget->IsOnCurrentThread();
}
#endif

View File

@ -68,12 +68,38 @@ class nsAutoOwningThread {
void* mThread;
};
class nsISerialEventTarget;
class nsAutoOwningEventTarget {
public:
nsAutoOwningEventTarget();
~nsAutoOwningEventTarget();
// We move the actual assertion checks out-of-line to minimize code bloat,
// but that means we have to pass a non-literal string to MOZ_CRASH_UNSAFE.
// To make that more safe, the public interface requires a literal string
// and passes that to the private interface; we can then be assured that we
// effectively are passing a literal string to MOZ_CRASH_UNSAFE.
template <int N>
void AssertOwnership(const char (&aMsg)[N]) const {
AssertCurrentThreadOwnsMe(aMsg);
}
bool IsCurrentThread() const;
private:
void AssertCurrentThreadOwnsMe(const char* aMsg) const;
nsISerialEventTarget* mTarget;
};
# define NS_DECL_OWNINGTHREAD nsAutoOwningThread _mOwningThread;
# define NS_DECL_OWNINGEVENTTARGET nsAutoOwningEventTarget _mOwningThread;
# define NS_ASSERT_OWNINGTHREAD(_class) \
_mOwningThread.AssertOwnership(#_class " not thread-safe")
#else // !MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
# define NS_DECL_OWNINGTHREAD /* nothing */
# define NS_DECL_OWNINGEVENTTARGET /* nothing */
# define NS_ASSERT_OWNINGTHREAD(_class) ((void)0)
#endif // MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
@ -567,33 +593,33 @@ class ThreadSafeAutoRefCnt {
* AddRef & Release methods (typically NS_IMETHOD_ or NS_METHOD_).
* @param optional override Mark the AddRef & Release methods as overrides.
*/
#define NS_INLINE_DECL_REFCOUNTING_META(_class, _decl, _destroy, ...) \
public: \
_decl(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD(_class); \
++mRefCnt; \
NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \
return mRefCnt; \
} \
_decl(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
NS_ASSERT_OWNINGTHREAD(_class); \
--mRefCnt; \
NS_LOG_RELEASE(this, mRefCnt, #_class); \
if (mRefCnt == 0) { \
mRefCnt = 1; /* stabilize */ \
_destroy; \
return 0; \
} \
return mRefCnt; \
} \
using HasThreadSafeRefCnt = std::false_type; \
\
protected: \
nsAutoRefCnt mRefCnt; \
NS_DECL_OWNINGTHREAD \
#define NS_INLINE_DECL_REFCOUNTING_META(_class, _decl, _destroy, _owning, ...) \
public: \
_decl(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD(_class); \
++mRefCnt; \
NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \
return mRefCnt; \
} \
_decl(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
NS_ASSERT_OWNINGTHREAD(_class); \
--mRefCnt; \
NS_LOG_RELEASE(this, mRefCnt, #_class); \
if (mRefCnt == 0) { \
mRefCnt = 1; /* stabilize */ \
_destroy; \
return 0; \
} \
return mRefCnt; \
} \
using HasThreadSafeRefCnt = std::false_type; \
\
protected: \
nsAutoRefCnt mRefCnt; \
_owning \
public:
/**
@ -606,14 +632,16 @@ class ThreadSafeAutoRefCnt {
* @param optional override Mark the AddRef & Release methods as overrides.
*/
#define NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, _destroy, ...) \
NS_INLINE_DECL_REFCOUNTING_META(_class, NS_METHOD_, _destroy, __VA_ARGS__)
NS_INLINE_DECL_REFCOUNTING_META(_class, NS_METHOD_, _destroy, \
NS_DECL_OWNINGTHREAD, __VA_ARGS__)
/**
* Like NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY with AddRef & Release declared
* virtual.
*/
#define NS_INLINE_DECL_VIRTUAL_REFCOUNTING_WITH_DESTROY(_class, _destroy, ...) \
NS_INLINE_DECL_REFCOUNTING_META(_class, NS_IMETHOD_, _destroy, __VA_ARGS__)
NS_INLINE_DECL_REFCOUNTING_META(_class, NS_IMETHOD_, _destroy, \
NS_DECL_OWNINGTHREAD, __VA_ARGS__)
/**
* Use this macro to declare and implement the AddRef & Release methods for a
@ -625,6 +653,16 @@ class ThreadSafeAutoRefCnt {
#define NS_INLINE_DECL_REFCOUNTING(_class, ...) \
NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, delete (this), __VA_ARGS__)
/**
* Like NS_INLINE_DECL_REFCOUNTING, however the thread safety check will work
* with any nsISerialEventTarget. This is a workaround until bug 1648031 is
* properly resolved. Once this is done, it will be possible to use
* NS_INLINE_DECL_REFCOUNTING under all circumstances.
*/
#define NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(_class, ...) \
NS_INLINE_DECL_REFCOUNTING_META(_class, NS_METHOD_, delete (this), \
NS_DECL_OWNINGEVENTTARGET, __VA_ARGS__)
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, _decl, _destroy, \
...) \
public: \