| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d4f33f1 commit 4afcd55
1 file changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -314,16 +314,16 @@ class RefBase : protected Finalizer, RefTracker { | |||
| 314 | 314 | }; | |
| 315 | 315 | ||
| 316 | 316 | class Reference : public RefBase { | |
| 317 | + using SecondPassCallParameterRef = Reference*; | ||
| 318 | + | ||
| 317 | 319 | protected: | |
| 318 | 320 | template <typename... Args> | |
| 319 | - Reference(napi_env env, | ||
| 320 | - v8::Local<v8::Value> value, | ||
| 321 | - Args&&... args) | ||
| 321 | + Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args) | ||
| 322 | 322 | : RefBase(env, std::forward<Args>(args)...), | |
| 323 | - _persistent(env->isolate, value) { | ||
| 323 | + _persistent(env->isolate, value), | ||
| 324 | + _secondPassParameter(new SecondPassCallParameterRef(this)) { | ||
| 324 | 325 | if (RefCount() == 0) { | |
| 325 | - _persistent.SetWeak( | ||
| 326 | - this, FinalizeCallback, v8::WeakCallbackType::kParameter); | ||
| 326 | + SetWeak(); | ||
| 327 | 327 | } | |
| 328 | 328 | } | |
| 329 | 329 | ||
@@ -344,10 +344,19 @@ class Reference : public RefBase { | |||
| 344 | 344 | finalize_hint); | |
| 345 | 345 | } | |
| 346 | 346 | ||
| 347 | + virtual ~Reference() { | ||
| 348 | + // If the second pass callback is scheduled, it will delete the | ||
| 349 | + // parameter passed to it, otherwise it will never be scheduled | ||
| 350 | + // and we need to delete it here. | ||
| 351 | + if (_secondPassParameter != nullptr) { | ||
| 352 | + delete _secondPassParameter; | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + | ||
| 347 | 356 | inline uint32_t Ref() { | |
| 348 | 357 | uint32_t refcount = RefBase::Ref(); | |
| 349 | 358 | if (refcount == 1) { | |
| 350 | - _persistent.ClearWeak(); | ||
| 359 | + ClearWeak(); | ||
| 351 | 360 | } | |
| 352 | 361 | return refcount; | |
| 353 | 362 | } | |
@@ -356,8 +365,7 @@ class Reference : public RefBase { | |||
| 356 | 365 | uint32_t old_refcount = RefCount(); | |
| 357 | 366 | uint32_t refcount = RefBase::Unref(); | |
| 358 | 367 | if (old_refcount == 1 && refcount == 0) { | |
| 359 | - _persistent.SetWeak( | ||
| 360 | - this, FinalizeCallback, v8::WeakCallbackType::kParameter); | ||
| 368 | + SetWeak(); | ||
| 361 | 369 | } | |
| 362 | 370 | return refcount; | |
| 363 | 371 | } | |
@@ -377,39 +385,95 @@ class Reference : public RefBase { | |||
| 377 | 385 | ||
| 378 | 386 | // During env teardown, `~napi_env()` alone is responsible for finalizing. | |
| 379 | 387 | // Thus, we don't want any stray gc passes to trigger a second call to | |
| 380 | - // `Finalize()`, so let's reset the persistent here if nothing is | ||
| 381 | - // keeping it alive. | ||
| 382 | - if (is_env_teardown && _persistent.IsWeak()) { | ||
| 383 | - _persistent.ClearWeak(); | ||
| 388 | + // `RefBase::Finalize()`. ClearWeak will ensure that even if the | ||
| 389 | + // gc is in progress no Finalization will be run for this Reference | ||
| 390 | + // by the gc. | ||
| 391 | + if (is_env_teardown) { | ||
| 392 | + ClearWeak(); | ||
| 384 | 393 | } | |
| 385 | 394 | ||
| 386 | 395 | // Chain up to perform the rest of the finalization. | |
| 387 | 396 | RefBase::Finalize(is_env_teardown); | |
| 388 | 397 | } | |
| 389 | 398 | ||
| 390 | 399 | private: | |
| 400 | + // ClearWeak is marking the Reference so that the gc should not | ||
| 401 | + // collect it, but it is possible that a second pass callback | ||
| 402 | + // may have been scheduled already if we are in shutdown. We clear | ||
| 403 | + // the secondPassParameter so that even if it has been | ||
| 404 | + // secheduled no Finalization will be run. | ||
| 405 | + inline void ClearWeak() { | ||
| 406 | + if (!_persistent.IsEmpty()) { | ||
| 407 | + _persistent.ClearWeak(); | ||
| 408 | + } | ||
| 409 | + if (_secondPassParameter != nullptr) { | ||
| 410 | + *_secondPassParameter = nullptr; | ||
| 411 | + } | ||
| 412 | + } | ||
| 413 | + | ||
| 414 | + // Mark the reference as weak and eligible for collection | ||
| 415 | + // by the gc. | ||
| 416 | + inline void SetWeak() { | ||
| 417 | + if (_secondPassParameter == nullptr) { | ||
| 418 | + // This means that the Reference has already been processed | ||
| 419 | + // by the second pass calback, so its already been Finalized, do | ||
| 420 | + // nothing | ||
| 421 | + return; | ||
| 422 | + } | ||
| 423 | + _persistent.SetWeak( | ||
| 424 | + _secondPassParameter, FinalizeCallback, | ||
| 425 | + v8::WeakCallbackType::kParameter); | ||
| 426 | + *_secondPassParameter = this; | ||
| 427 | + } | ||
| 428 | + | ||
| 391 | 429 | // The N-API finalizer callback may make calls into the engine. V8's heap is | |
| 392 | 430 | // not in a consistent state during the weak callback, and therefore it does | |
| 393 | 431 | // not support calls back into it. However, it provides a mechanism for adding | |
| 394 | 432 | // a finalizer which may make calls back into the engine by allowing us to | |
| 395 | 433 | // attach such a second-pass finalizer from the first pass finalizer. Thus, | |
| 396 | 434 | // we do that here to ensure that the N-API finalizer callback is free to call | |
| 397 | 435 | // into the engine. | |
| 398 | - static void FinalizeCallback(const v8::WeakCallbackInfo<Reference>& data) { | ||
| 399 | - Reference* reference = data.GetParameter(); | ||
| 436 | + static void FinalizeCallback( | ||
| 437 | + const v8::WeakCallbackInfo<SecondPassCallParameterRef>& data) { | ||
| 438 | + SecondPassCallParameterRef* parameter = data.GetParameter(); | ||
| 439 | + Reference* reference = *parameter; | ||
| 440 | + if (reference == nullptr) { | ||
| 441 | + return; | ||
| 442 | + } | ||
| 400 | 443 | ||
| 401 | 444 | // The reference must be reset during the first pass. | |
| 402 | 445 | reference->_persistent.Reset(); | |
| 446 | + // Mark the parameter not delete-able until the second pass callback is | ||
| 447 | + // invoked. | ||
| 448 | + reference->_secondPassParameter = nullptr; | ||
| 403 | 449 | ||
| 404 | 450 | data.SetSecondPassCallback(SecondPassCallback); | |
| 405 | 451 | } | |
| 406 | 452 | ||
| 407 | - static void SecondPassCallback(const v8::WeakCallbackInfo<Reference>& data) { | ||
| 408 | - data.GetParameter()->Finalize(); | ||
| 453 | + // Second pass callbacks are scheduled with platform tasks. At env teardown, | ||
| 454 | + // the tasks may have already be scheduled and we are unable to cancel the | ||
| 455 | + // second pass callback task. We have to make sure that parameter is kept | ||
| 456 | + // alive until the second pass callback is been invoked. In order to do | ||
| 457 | + // this and still allow our code to Finalize/delete the Reference during | ||
| 458 | + // shutdown we have to use a seperately allocated parameter instead | ||
| 459 | + // of a parameter within the Reference object itself. This is what | ||
| 460 | + // is stored in _secondPassParameter and it is alocated in the | ||
| 461 | + // constructor for the Reference. | ||
| 462 | + static void SecondPassCallback( | ||
| 463 | + const v8::WeakCallbackInfo<SecondPassCallParameterRef>& data) { | ||
| 464 | + SecondPassCallParameterRef* parameter = data.GetParameter(); | ||
| 465 | + Reference* reference = *parameter; | ||
| 466 | + delete parameter; | ||
| 467 | + if (reference == nullptr) { | ||
| 468 | + // the reference itself has already been deleted so nothing to do | ||
| 469 | + return; | ||
| 470 | + } | ||
| 471 | + reference->Finalize(); | ||
| 409 | 472 | } | |
| 410 | 473 | ||
| 411 | 474 | bool env_teardown_finalize_started_ = false; | |
| 412 | 475 | v8impl::Persistent<v8::Value> _persistent; | |
| 476 | + SecondPassCallParameterRef* _secondPassParameter; | ||
| 413 | 477 | }; | |
| 414 | 478 | ||
| 415 | 479 | enum UnwrapAction { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments