toolkit/components/finalizationwitness/FinalizationWitnessService.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,213 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "FinalizationWitnessService.h"
     1.9 +
    1.10 +#include "nsString.h"
    1.11 +#include "jsapi.h"
    1.12 +#include "js/CallNonGenericMethod.h"
    1.13 +#include "mozJSComponentLoader.h"
    1.14 +#include "nsZipArchive.h"
    1.15 +
    1.16 +#include "mozilla/Scoped.h"
    1.17 +#include "mozilla/Services.h"
    1.18 +#include "mozilla/NullPtr.h"
    1.19 +#include "nsIObserverService.h"
    1.20 +#include "nsThreadUtils.h"
    1.21 +
    1.22 +
    1.23 +// Implementation of nsIFinalizationWitnessService
    1.24 +
    1.25 +namespace mozilla {
    1.26 +
    1.27 +namespace {
    1.28 +
    1.29 +/**
    1.30 + * An event meant to be dispatched to the main thread upon finalization
    1.31 + * of a FinalizationWitness, unless method |forget()| has been called.
    1.32 + *
    1.33 + * Held as private data by each instance of FinalizationWitness.
    1.34 + * Important note: we maintain the invariant that these private data
    1.35 + * slots are already addrefed.
    1.36 + */
    1.37 +class FinalizationEvent MOZ_FINAL: public nsRunnable
    1.38 +{
    1.39 +public:
    1.40 +  FinalizationEvent(const char* aTopic,
    1.41 +                  const jschar* aValue)
    1.42 +    : mTopic(aTopic)
    1.43 +    , mValue(aValue)
    1.44 +  { }
    1.45 +
    1.46 +  NS_METHOD Run() {
    1.47 +    nsCOMPtr<nsIObserverService> observerService =
    1.48 +      mozilla::services::GetObserverService();
    1.49 +    if (!observerService) {
    1.50 +      // This is either too early or, more likely, too late for notifications.
    1.51 +      // Bail out.
    1.52 +      return NS_ERROR_NOT_AVAILABLE;
    1.53 +    }
    1.54 +    (void)observerService->
    1.55 +      NotifyObservers(nullptr, mTopic.get(), mValue.get());
    1.56 +    return NS_OK;
    1.57 +  }
    1.58 +private:
    1.59 +  /**
    1.60 +   * The topic on which to broadcast the notification of finalization.
    1.61 +   *
    1.62 +   * Deallocated on the main thread.
    1.63 +   */
    1.64 +  const nsCString mTopic;
    1.65 +
    1.66 +  /**
    1.67 +   * The result of converting the exception to a string.
    1.68 +   *
    1.69 +   * Deallocated on the main thread.
    1.70 +   */
    1.71 +  const nsString mValue;
    1.72 +};
    1.73 +
    1.74 +enum {
    1.75 +  WITNESS_SLOT_EVENT,
    1.76 +  WITNESS_INSTANCES_SLOTS
    1.77 +};
    1.78 +
    1.79 +/**
    1.80 + * Extract the FinalizationEvent from an instance of FinalizationWitness
    1.81 + * and clear the slot containing the FinalizationEvent.
    1.82 + */
    1.83 +already_AddRefed<FinalizationEvent>
    1.84 +ExtractFinalizationEvent(JSObject *objSelf)
    1.85 +{
    1.86 +  JS::Value slotEvent = JS_GetReservedSlot(objSelf, WITNESS_SLOT_EVENT);
    1.87 +  if (slotEvent.isUndefined()) {
    1.88 +    // Forget() has been called
    1.89 +    return nullptr;
    1.90 +  }
    1.91 +
    1.92 +  JS_SetReservedSlot(objSelf, WITNESS_SLOT_EVENT, JS::UndefinedValue());
    1.93 +
    1.94 +  return dont_AddRef(static_cast<FinalizationEvent*>(slotEvent.toPrivate()));
    1.95 +}
    1.96 +
    1.97 +/**
    1.98 + * Finalizer for instances of FinalizationWitness.
    1.99 + *
   1.100 + * Unless method Forget() has been called, the finalizer displays an error
   1.101 + * message.
   1.102 + */
   1.103 +void Finalize(JSFreeOp *fop, JSObject *objSelf)
   1.104 +{
   1.105 +  nsRefPtr<FinalizationEvent> event = ExtractFinalizationEvent(objSelf);
   1.106 +  if (event == nullptr) {
   1.107 +    // Forget() has been called
   1.108 +    return;
   1.109 +  }
   1.110 +
   1.111 +  // Notify observers. Since we are executed during garbage-collection,
   1.112 +  // we need to dispatch the notification to the main thread.
   1.113 +  (void)NS_DispatchToMainThread(event);
   1.114 +  // We may fail at dispatching to the main thread if we arrive too late
   1.115 +  // during shutdown. In that case, there is not much we can do.
   1.116 +}
   1.117 +
   1.118 +static const JSClass sWitnessClass = {
   1.119 +  "FinalizationWitness",
   1.120 +  JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS),
   1.121 +  JS_PropertyStub /* addProperty */,
   1.122 +  JS_DeletePropertyStub /* delProperty */,
   1.123 +  JS_PropertyStub /* getProperty */,
   1.124 +  JS_StrictPropertyStub /* setProperty */,
   1.125 +  JS_EnumerateStub /* enumerate */,
   1.126 +  JS_ResolveStub /* resolve */,
   1.127 +  JS_ConvertStub /* convert */,
   1.128 +  Finalize /* finalize */
   1.129 +};
   1.130 +
   1.131 +bool IsWitness(JS::Handle<JS::Value> v)
   1.132 +{
   1.133 +  return v.isObject() && JS_GetClass(&v.toObject()) == &sWitnessClass;
   1.134 +}
   1.135 +
   1.136 +
   1.137 +/**
   1.138 + * JS method |forget()|
   1.139 + *
   1.140 + * === JS documentation
   1.141 + *
   1.142 + *  Neutralize the witness. Once this method is called, the witness will
   1.143 + *  never report any error.
   1.144 + */
   1.145 +bool ForgetImpl(JSContext* cx, JS::CallArgs args)
   1.146 +{
   1.147 +  if (args.length() != 0) {
   1.148 +    JS_ReportError(cx, "forget() takes no arguments");
   1.149 +    return false;
   1.150 +  }
   1.151 +  JS::Rooted<JS::Value> valSelf(cx, args.thisv());
   1.152 +  JS::Rooted<JSObject*> objSelf(cx, &valSelf.toObject());
   1.153 +
   1.154 +  nsRefPtr<FinalizationEvent> event = ExtractFinalizationEvent(objSelf);
   1.155 +  if (event == nullptr) {
   1.156 +    JS_ReportError(cx, "forget() called twice");
   1.157 +    return false;
   1.158 +  }
   1.159 +
   1.160 +  args.rval().setUndefined();
   1.161 +  return true;
   1.162 +}
   1.163 +
   1.164 +bool Forget(JSContext *cx, unsigned argc, JS::Value *vp)
   1.165 +{
   1.166 +  JS::CallArgs args = CallArgsFromVp(argc, vp);
   1.167 +  return JS::CallNonGenericMethod<IsWitness, ForgetImpl>(cx, args);
   1.168 +}
   1.169 +
   1.170 +static const JSFunctionSpec sWitnessClassFunctions[] = {
   1.171 +  JS_FN("forget", Forget, 0, JSPROP_READONLY | JSPROP_PERMANENT),
   1.172 +  JS_FS_END
   1.173 +};
   1.174 +
   1.175 +}
   1.176 +
   1.177 +NS_IMPL_ISUPPORTS(FinalizationWitnessService, nsIFinalizationWitnessService)
   1.178 +
   1.179 +/**
   1.180 + * Create a new Finalization Witness.
   1.181 + *
   1.182 + * A finalization witness is an object whose sole role is to notify
   1.183 + * observers when it is gc-ed. Once the witness is created, call its
   1.184 + * method |forget()| to prevent the observers from being notified.
   1.185 + *
   1.186 + * @param aTopic The notification topic.
   1.187 + * @param aValue The notification value. Converted to a string.
   1.188 + *
   1.189 + * @constructor
   1.190 + */
   1.191 +NS_IMETHODIMP
   1.192 +FinalizationWitnessService::Make(const char* aTopic,
   1.193 +                                 const char16_t* aValue,
   1.194 +                                 JSContext* aCx,
   1.195 +                                 JS::MutableHandle<JS::Value> aRetval)
   1.196 +{
   1.197 +  JS::Rooted<JSObject*> objResult(aCx, JS_NewObject(aCx, &sWitnessClass, JS::NullPtr(),
   1.198 +                                                    JS::NullPtr()));
   1.199 +  if (!objResult) {
   1.200 +    return NS_ERROR_OUT_OF_MEMORY;
   1.201 +  }
   1.202 +  if (!JS_DefineFunctions(aCx, objResult, sWitnessClassFunctions)) {
   1.203 +    return NS_ERROR_FAILURE;
   1.204 +  }
   1.205 +
   1.206 +  nsRefPtr<FinalizationEvent> event = new FinalizationEvent(aTopic, aValue);
   1.207 +
   1.208 +  // Transfer ownership of the addrefed |event| to |objResult|.
   1.209 +  JS_SetReservedSlot(objResult, WITNESS_SLOT_EVENT,
   1.210 +                     JS::PrivateValue(event.forget().take()));
   1.211 +
   1.212 +  aRetval.setObject(*objResult);
   1.213 +  return NS_OK;
   1.214 +}
   1.215 +
   1.216 +} // namespace mozilla

mercurial