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