Bug 1596756 - Support FinalizationGroup objects in the browser r=mccr8

Add browser support for FinalizationGroup by setting the HostCleanupFinalizationGroupCallback in CycleCollectedJSContext.  The callback adds groups pending cleanup to a vector stored in a PersistentRooted.  A runnable is dispatched to call back into the JS engine and perform cleanup at a later time as a separate task.  Using AutoEntryScript reports errors to the console.

Differential Revision: https://phabricator.services.mozilla.com/D53248

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jon Coppeard 2020-01-15 14:22:04 +00:00
parent 565c221ae9
commit 5af1cef3ff
2 changed files with 72 additions and 0 deletions

View File

@ -13,6 +13,7 @@
#include "mozilla/Move.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Sprintf.h"
#include "mozilla/SystemGroup.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/TimelineMarker.h"
@ -77,6 +78,10 @@ CycleCollectedJSContext::~CycleCollectedJSContext() {
return;
}
JS::SetHostCleanupFinalizationGroupCallback(
mJSContext, nullptr, nullptr);
mFinalizationGroupsToCleanUp.reset();
JS_SetContextPrivate(mJSContext, nullptr);
mRuntime->SetContext(nullptr);
@ -144,6 +149,10 @@ nsresult CycleCollectedJSContext::Initialize(JSRuntime* aParentRuntime,
JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(
js::SystemAllocPolicy()));
mFinalizationGroupsToCleanUp.init(mJSContext);
JS::SetHostCleanupFinalizationGroupCallback(
mJSContext, CleanupFinalizationGroupCallback, this);
// Cast to PerThreadAtomCache for dom::GetAtomCache(JSContext*).
JS_SetContextPrivate(mJSContext, static_cast<PerThreadAtomCache*>(this));
@ -722,4 +731,54 @@ nsresult CycleCollectedJSContext::NotifyUnhandledRejections::Cancel() {
}
return NS_OK;
}
class CleanupFinalizationGroupsRunnable : public CancelableRunnable {
public:
explicit CleanupFinalizationGroupsRunnable(CycleCollectedJSContext* aContext)
: CancelableRunnable("CleanupFinalizationGroupsRunnable"), mContext(aContext) {}
NS_DECL_NSIRUNNABLE
private:
CycleCollectedJSContext* mContext;
};
NS_IMETHODIMP
CleanupFinalizationGroupsRunnable::Run() {
if (mContext->mFinalizationGroupsToCleanUp.empty()) {
return NS_OK;
}
JS::RootingContext* cx = mContext->RootingCx();
JS::Rooted<CycleCollectedJSContext::ObjectVector> groups(cx);
std::swap(groups.get(), mContext->mFinalizationGroupsToCleanUp.get());
JS::Rooted<JSObject*> group(cx);
for (const auto& g : groups) {
group = g;
AutoEntryScript aes(group, "cleanupFinalizationGroup");
mozilla::Unused << JS::CleanupQueuedFinalizationGroup(aes.cx(), group);
}
return NS_OK;
}
/* static */
void CycleCollectedJSContext::CleanupFinalizationGroupCallback(JSObject* aGroup,
void* aData) {
CycleCollectedJSContext* ccjs = static_cast<CycleCollectedJSContext*>(aData);
ccjs->QueueFinalizationGroupForCleanup(aGroup);
}
void CycleCollectedJSContext::QueueFinalizationGroupForCleanup(
JSObject* aGroup) {
bool firstGroup = mFinalizationGroupsToCleanUp.empty();
MOZ_ALWAYS_TRUE(mFinalizationGroupsToCleanUp.append(aGroup));
if (firstGroup) {
RefPtr<CleanupFinalizationGroupsRunnable> cleanup =
new CleanupFinalizationGroupsRunnable(this);
NS_DispatchToCurrentThread(cleanup.forget());
}
}
} // namespace mozilla

View File

@ -17,6 +17,7 @@
#include "mozilla/dom/AtomList.h"
#include "mozilla/dom/Promise.h"
#include "jsapi.h"
#include "js/GCVector.h"
#include "js/Promise.h"
#include "nsCOMPtr.h"
@ -263,6 +264,9 @@ class CycleCollectedJSContext
class SavedMicroTaskQueue;
js::UniquePtr<SavedJobQueue> saveJobQueue(JSContext*) override;
static void CleanupFinalizationGroupCallback(JSObject* aGroup, void* aData);
void QueueFinalizationGroupForCleanup(JSObject* aGroup);
private:
CycleCollectedJSRuntime* mRuntime;
@ -336,6 +340,15 @@ class CycleCollectedJSContext
CycleCollectedJSContext* mCx;
PromiseArray mUnhandledRejections;
};
// Support for JS FinalizationGroup objects.
//
// These allow a JS callback to be registered that is called when an object
// dies. The browser part of the implementation keeps a vector of
// FinalizationGroups with pending callbacks here.
friend class CleanupFinalizationGroupsRunnable;
using ObjectVector = JS::GCVector<JSObject*, 0, InfallibleAllocPolicy>;
JS::PersistentRooted<ObjectVector> mFinalizationGroupsToCleanUp;
};
class MOZ_STACK_CLASS nsAutoMicroTask {