LCOV - code coverage report
Current view: top level - toolkit/components/finalizationwitness - FinalizationWitnessService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 18 66 27.3 %
Date: 2017-07-14 16:53:18 Functions: 6 15 40.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       3             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "FinalizationWitnessService.h"
       6             : 
       7             : #include "nsString.h"
       8             : #include "jsapi.h"
       9             : #include "js/CallNonGenericMethod.h"
      10             : #include "mozJSComponentLoader.h"
      11             : #include "nsZipArchive.h"
      12             : 
      13             : #include "mozilla/Scoped.h"
      14             : #include "mozilla/Services.h"
      15             : #include "nsIObserverService.h"
      16             : #include "nsThreadUtils.h"
      17             : 
      18             : 
      19             : // Implementation of nsIFinalizationWitnessService
      20             : 
      21             : static bool gShuttingDown = false;
      22             : 
      23             : namespace mozilla {
      24             : 
      25             : namespace {
      26             : 
      27             : /**
      28             :  * An event meant to be dispatched to the main thread upon finalization
      29             :  * of a FinalizationWitness, unless method |forget()| has been called.
      30             :  *
      31             :  * Held as private data by each instance of FinalizationWitness.
      32             :  * Important note: we maintain the invariant that these private data
      33             :  * slots are already addrefed.
      34             :  */
      35           0 : class FinalizationEvent final: public Runnable
      36             : {
      37             : public:
      38           2 :   FinalizationEvent(const char* aTopic,
      39             :                   const char16_t* aValue)
      40           2 :     : Runnable("FinalizationEvent")
      41             :     , mTopic(aTopic)
      42           2 :     , mValue(aValue)
      43           2 :   { }
      44             : 
      45           0 :   NS_IMETHOD Run() override {
      46             :     nsCOMPtr<nsIObserverService> observerService =
      47           0 :       mozilla::services::GetObserverService();
      48           0 :     if (!observerService) {
      49             :       // This is either too early or, more likely, too late for notifications.
      50             :       // Bail out.
      51           0 :       return NS_ERROR_NOT_AVAILABLE;
      52             :     }
      53           0 :     (void)observerService->
      54           0 :       NotifyObservers(nullptr, mTopic.get(), mValue.get());
      55           0 :     return NS_OK;
      56             :   }
      57             : private:
      58             :   /**
      59             :    * The topic on which to broadcast the notification of finalization.
      60             :    *
      61             :    * Deallocated on the main thread.
      62             :    */
      63             :   const nsCString mTopic;
      64             : 
      65             :   /**
      66             :    * The result of converting the exception to a string.
      67             :    *
      68             :    * Deallocated on the main thread.
      69             :    */
      70             :   const nsString mValue;
      71             : };
      72             : 
      73             : enum {
      74             :   WITNESS_SLOT_EVENT,
      75             :   WITNESS_INSTANCES_SLOTS
      76             : };
      77             : 
      78             : /**
      79             :  * Extract the FinalizationEvent from an instance of FinalizationWitness
      80             :  * and clear the slot containing the FinalizationEvent.
      81             :  */
      82             : already_AddRefed<FinalizationEvent>
      83           0 : ExtractFinalizationEvent(JSObject *objSelf)
      84             : {
      85           0 :   JS::Value slotEvent = JS_GetReservedSlot(objSelf, WITNESS_SLOT_EVENT);
      86           0 :   if (slotEvent.isUndefined()) {
      87             :     // Forget() has been called
      88           0 :     return nullptr;
      89             :   }
      90             : 
      91           0 :   JS_SetReservedSlot(objSelf, WITNESS_SLOT_EVENT, JS::UndefinedValue());
      92             : 
      93           0 :   return dont_AddRef(static_cast<FinalizationEvent*>(slotEvent.toPrivate()));
      94             : }
      95             : 
      96             : /**
      97             :  * Finalizer for instances of FinalizationWitness.
      98             :  *
      99             :  * Unless method Forget() has been called, the finalizer displays an error
     100             :  * message.
     101             :  */
     102           0 : void Finalize(JSFreeOp *fop, JSObject *objSelf)
     103             : {
     104           0 :   RefPtr<FinalizationEvent> event = ExtractFinalizationEvent(objSelf);
     105           0 :   if (event == nullptr || gShuttingDown) {
     106             :     // NB: event will be null if Forget() has been called
     107           0 :     return;
     108             :   }
     109             : 
     110             :   // Notify observers. Since we are executed during garbage-collection,
     111             :   // we need to dispatch the notification to the main thread.
     112           0 :   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
     113           0 :   if (mainThread) {
     114           0 :     mainThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
     115             :   }
     116             :   // We may fail at dispatching to the main thread if we arrive too late
     117             :   // during shutdown. In that case, there is not much we can do.
     118             : }
     119             : 
     120             : static const JSClassOps sWitnessClassOps = {
     121             :   nullptr /* addProperty */,
     122             :   nullptr /* delProperty */,
     123             :   nullptr /* getProperty */,
     124             :   nullptr /* setProperty */,
     125             :   nullptr /* enumerate */,
     126             :   nullptr /* newEnumerate */,
     127             :   nullptr /* resolve */,
     128             :   nullptr /* mayResolve */,
     129             :   Finalize /* finalize */
     130             : };
     131             : 
     132             : static const JSClass sWitnessClass = {
     133             :   "FinalizationWitness",
     134             :   JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS) |
     135             :   JSCLASS_FOREGROUND_FINALIZE,
     136             :   &sWitnessClassOps
     137             : };
     138             : 
     139           0 : bool IsWitness(JS::Handle<JS::Value> v)
     140             : {
     141           0 :   return v.isObject() && JS_GetClass(&v.toObject()) == &sWitnessClass;
     142             : }
     143             : 
     144             : 
     145             : /**
     146             :  * JS method |forget()|
     147             :  *
     148             :  * === JS documentation
     149             :  *
     150             :  *  Neutralize the witness. Once this method is called, the witness will
     151             :  *  never report any error.
     152             :  */
     153           0 : bool ForgetImpl(JSContext* cx, const JS::CallArgs& args)
     154             : {
     155           0 :   if (args.length() != 0) {
     156           0 :     JS_ReportErrorASCII(cx, "forget() takes no arguments");
     157           0 :     return false;
     158             :   }
     159           0 :   JS::Rooted<JS::Value> valSelf(cx, args.thisv());
     160           0 :   JS::Rooted<JSObject*> objSelf(cx, &valSelf.toObject());
     161             : 
     162           0 :   RefPtr<FinalizationEvent> event = ExtractFinalizationEvent(objSelf);
     163           0 :   if (event == nullptr) {
     164           0 :     JS_ReportErrorASCII(cx, "forget() called twice");
     165           0 :     return false;
     166             :   }
     167             : 
     168           0 :   args.rval().setUndefined();
     169           0 :   return true;
     170             : }
     171             : 
     172           0 : bool Forget(JSContext *cx, unsigned argc, JS::Value *vp)
     173             : {
     174           0 :   JS::CallArgs args = CallArgsFromVp(argc, vp);
     175           0 :   return JS::CallNonGenericMethod<IsWitness, ForgetImpl>(cx, args);
     176             : }
     177             : 
     178             : static const JSFunctionSpec sWitnessClassFunctions[] = {
     179             :   JS_FN("forget", Forget, 0, JSPROP_READONLY | JSPROP_PERMANENT),
     180             :   JS_FS_END
     181             : };
     182             : 
     183             : } // namespace
     184             : 
     185          43 : NS_IMPL_ISUPPORTS(FinalizationWitnessService, nsIFinalizationWitnessService, nsIObserver)
     186             : 
     187             : /**
     188             :  * Create a new Finalization Witness.
     189             :  *
     190             :  * A finalization witness is an object whose sole role is to notify
     191             :  * observers when it is gc-ed. Once the witness is created, call its
     192             :  * method |forget()| to prevent the observers from being notified.
     193             :  *
     194             :  * @param aTopic The notification topic.
     195             :  * @param aValue The notification value. Converted to a string.
     196             :  *
     197             :  * @constructor
     198             :  */
     199             : NS_IMETHODIMP
     200           2 : FinalizationWitnessService::Make(const char* aTopic,
     201             :                                  const char16_t* aValue,
     202             :                                  JSContext* aCx,
     203             :                                  JS::MutableHandle<JS::Value> aRetval)
     204             : {
     205           4 :   JS::Rooted<JSObject*> objResult(aCx, JS_NewObject(aCx, &sWitnessClass));
     206           2 :   if (!objResult) {
     207           0 :     return NS_ERROR_OUT_OF_MEMORY;
     208             :   }
     209           2 :   if (!JS_DefineFunctions(aCx, objResult, sWitnessClassFunctions)) {
     210           0 :     return NS_ERROR_FAILURE;
     211             :   }
     212             : 
     213           4 :   RefPtr<FinalizationEvent> event = new FinalizationEvent(aTopic, aValue);
     214             : 
     215             :   // Transfer ownership of the addrefed |event| to |objResult|.
     216           2 :   JS_SetReservedSlot(objResult, WITNESS_SLOT_EVENT,
     217           4 :                      JS::PrivateValue(event.forget().take()));
     218             : 
     219           2 :   aRetval.setObject(*objResult);
     220           2 :   return NS_OK;
     221             : }
     222             : 
     223             : NS_IMETHODIMP
     224           0 : FinalizationWitnessService::Observe(nsISupports* aSubject,
     225             :                                     const char* aTopic,
     226             :                                     const char16_t* aValue)
     227             : {
     228           0 :   MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
     229           0 :   gShuttingDown = true;
     230           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     231           0 :   if (obs) {
     232           0 :     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     233             :   }
     234             : 
     235           0 :   return NS_OK;
     236             : }
     237             : 
     238             : nsresult
     239           1 : FinalizationWitnessService::Init()
     240             : {
     241           2 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     242           1 :   if (!obs) {
     243           0 :     return NS_ERROR_FAILURE;
     244             :   }
     245             : 
     246           1 :   return obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     247             : }
     248             : 
     249             : } // namespace mozilla

Generated by: LCOV version 1.13