Using RAII classes in Mozilla

RAII classes are useful when two operations (e.g., Lock/Unlock, AddRef/Release, PushState/PopState) must be paired.

Ensuring RAII classes are not used as temporaries

A common mistake when using RAII classes is to accidentally forget to name object, which causes its scope to be different from what is intended. For example, instead of writing:

  AutoLock lock(mMutex);

which causes the lock to be held until the end of the block, one might write:

  AutoLock(mMutex);

which erroneously causes the lock to be released at the end of the statement.

We have two techniques for avoiding this problem: runtime assertions and static analysis.

Assertions

Runtime assertions are often better at catching bugs in code that a developer is currently working on than static analysis, which he might not think to run. We have a set of macros that will cause a class to assert when it is instantiated at a temporary. They cause no extra complexity for users of the class, but they do slightly complicate the implementation of the class.

These macros are provided in GuardObjects.h.

In the common case, using these macros involves these three additions to a class:

class NS_STACK_CLASS nsAutoScriptBlocker {
public:
  nsAutoScriptBlocker(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {//Note: No ',' before macro
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    nsContentUtils::AddScriptBlocker(cx);
  }
  ~nsAutoScriptBlocker() {
    nsContentUtils::RemoveScriptBlocker();
  }
private:
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };

MOZ_GUARD_OBJECT_NOTIFIER_PARAM is added to the end of the constructor's parameter list. MOZ_GUARD_OBJECT_NOTIFIER_INIT is added as a statement in the constructor. MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER is added where private members should be declared.

However, there are some variations for special cases:

First, if the constructor (otherwise) takes no arguments, then you have to use the MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM macro instead. (This is needed because the macro adds a parameter only when DEBUG is defined, and in this case, it can't add the leading comma.)

class NS_STACK_CLASS nsAutoScriptBlocker {
public:
  nsAutoScriptBlocker(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    nsContentUtils::AddScriptBlocker();
  }
  ~nsAutoScriptBlocker() {
    nsContentUtils::RemoveScriptBlocker();
  }
private:
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };

Second, if the constructor is not inline, it needs the parameter added in its implementation as well. In this case, the implementation must use the MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL macro to add to the implementation:

nsAutoScriptBlocker::nsAutoScriptBlocker(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
{
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    nsContentUtils::AddScriptBlocker(cx);
}

or, if it is the only parameter:

nsAutoScriptBlocker::nsAutoScriptBlocker(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
{
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    nsContentUtils::AddScriptBlocker();
}

Finally, if an RAII class that uses these macros has derived classes, the derived classes must also use some of the macros in order to get the benefit of the assertions. In particular, a class derived from a class using these macros does not use MOZ_GUARD_OBJECT_NOTIFIER_INIT or MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER, but instead uses either MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT or MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT:

nsSpecialAutoScriptBlocker::nsSpecialAutoScriptBlocker(MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
  :  nsAutoScriptBlocker(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT)
{
}

Note that a few of these macros aren't actually implemented yet, but they can be implemented when needed.

Static Analysis

Static analysis is good at ensuring there are no occurrences of a problem, even in rarely-executed code. Once bug 502775 lands, we'll have a way to document that RAII classes should never be used as temporaries.

Document Tags and Contributors

Contributors to this page: DBaron, Yoric, Dholbert
Last updated by: Dholbert,