@@ -323,7 +323,7 @@ void SignalHandler::HandleProfilerSignal(int sig,
323323 auto time_from = Now ();
324324 old_handler (sig, info, context);
325325 auto time_to = Now ();
326- prof->PushContext (time_from, time_to, cpu_time);
326+ prof->PushContext (time_from, time_to, cpu_time, isolate );
327327}
328328#else
329329class SignalHandler {
@@ -509,6 +509,8 @@ WallProfiler::WallProfiler(std::chrono::microseconds samplingPeriod,
509509#endif
510510 jsArray_ = v8::Global<v8::Uint32Array>(isolate, jsArray);
511511 std::fill (fields_, fields_ + kFieldCount , 0 );
512+
513+ cpedSymbol_ = v8::Global<v8::Symbol>(isolate, v8::Symbol::New (isolate));
512514}
513515
514516WallProfiler::~WallProfiler () {
@@ -953,23 +955,65 @@ v8::CpuProfiler* WallProfiler::CreateV8CpuProfiler() {
953955}
954956
955957v8::Local<v8::Value> WallProfiler::GetContext (Isolate* isolate) {
956- auto context = GetContextPtr ();
958+ auto context = GetContextPtr (isolate );
957959 if (!context) return v8::Undefined (isolate);
958960 return context->Get (isolate);
959961}
960962
961963void WallProfiler::SetContext (Isolate* isolate, Local<Value> value) {
964+ auto cped = isolate->GetContinuationPreservedEmbedderData ();
965+ // No Node AsyncContextFrame in this continuation yet
966+ if (!cped->IsObject ()) return ;
967+
968+ auto cpedObj = cped.As <Object>();
969+ auto localSymbol = cpedSymbol_.Get (isolate);
970+ auto v8Ctx = isolate->GetCurrentContext ();
971+ auto maybeProfData = cpedObj->Get (v8Ctx, localSymbol);
972+ if (maybeProfData.IsEmpty ()) return ;
973+ auto profData = maybeProfData.ToLocalChecked ();
974+
975+ ContextPtr* contextPtr = nullptr ;
976+ if (profData->IsUndefined ()) {
977+ contextPtr = new ContextPtr ();
978+ auto maybeSetResult = cpedObj->Set (v8Ctx, localSymbol, External::New (isolate, contextPtr));
979+ if (maybeSetResult.IsNothing ()) {
980+ delete contextPtr;
981+ return ;
982+ }
983+
984+ // Register a callback to delete contextPtr when the CPED object is GCed
985+ Persistent<Object>(isolate, cpedObj).SetWeak (contextPtr, [](const WeakCallbackInfo<ContextPtr>& data) {
986+ // Using SetSecondPassCallback as shared_ptr can trigger ~Global and any V8 API use needs to be in the second pass
987+ data.SetSecondPassCallback ([](const WeakCallbackInfo<ContextPtr>& data) {
988+ delete data.GetParameter ();
989+ });
990+ }, WeakCallbackType::kParameter );
991+ } else {
992+ contextPtr = reinterpret_cast <ContextPtr*>(profData.As <External>()->Value ());
993+ }
994+
962995 std::atomic_signal_fence (std::memory_order_release);
963996 std::atomic_store_explicit (
964- &curContext_ ,
997+ contextPtr ,
965998 value->IsNullOrUndefined ()
966999 ? std::shared_ptr<Global<Value>>()
9671000 : std::make_shared<Global<Value>>(isolate, value),
9681001 std::memory_order_relaxed);
9691002}
9701003
971- ContextPtr WallProfiler::GetContextPtr () {
972- auto contextPtr = atomic_load_explicit (&curContext_, std::memory_order_relaxed);
1004+ ContextPtr WallProfiler::GetContextPtr (Isolate* isolate) {
1005+ auto cped = isolate->GetContinuationPreservedEmbedderData (); // signal safe?
1006+ if (!cped->IsObject ()) return std::shared_ptr<Global<Value>>();
1007+
1008+ auto cpedObj = cped.As <Object>();
1009+ auto localSymbol = cpedSymbol_.Get (isolate); // signal safe?
1010+ auto maybeProfData = cpedObj->Get (isolate->GetCurrentContext (), localSymbol); // signal safe?
1011+ if (maybeProfData.IsEmpty ()) return std::shared_ptr<Global<Value>>();
1012+ auto profData = maybeProfData.ToLocalChecked ();
1013+
1014+ if (profData->IsUndefined ()) return std::shared_ptr<Global<Value>>();
1015+
1016+ auto contextPtr = std::atomic_load_explicit (reinterpret_cast <ContextPtr*>(profData.As <External>()->Value ()), std::memory_order_relaxed);
9731017 std::atomic_signal_fence (std::memory_order_acquire);
9741018 return contextPtr;
9751019}
@@ -1001,12 +1045,14 @@ NAN_METHOD(WallProfiler::Dispose) {
10011045
10021046void WallProfiler::PushContext (int64_t time_from,
10031047 int64_t time_to,
1004- int64_t cpu_time) {
1048+ int64_t cpu_time,
1049+ Isolate* isolate) {
10051050 // Be careful this is called in a signal handler context therefore all
10061051 // operations must be async signal safe (in particular no allocations).
10071052 // Our ring buffer avoids allocations.
10081053 if (contexts_.size () < contexts_.capacity ()) {
1009- contexts_.push_back ({GetContextPtr (), time_from, time_to, cpu_time});
1054+ HandleScope handle_scope (isolate); // signal safe?
1055+ contexts_.push_back ({GetContextPtr (isolate), time_from, time_to, cpu_time});
10101056 std::atomic_fetch_add_explicit (
10111057 reinterpret_cast <std::atomic<uint32_t >*>(&fields_[kSampleCount ]),
10121058 1U ,
0 commit comments