Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crashes from template finalizer #182

Merged
merged 2 commits into from
Sep 27, 2021
Merged

Conversation

dylanahsmith
Copy link
Collaborator

Problem

We ran into fairly reproducible crashes from benchmarking code that was creating (and initializing) new isolates frequently, often in C.TemplateSetTemplate, where the value being set was a v8go.FunctionTemplate. At first, I thought this might have been from a race condition from the finalizer running in a separate goroutine, which I thought was the reason other uses of finalizers was removed from v8go, but it happened even when the finalizer only freed the wrapper struct (m_template). It looks like there was also a problem with the template becoming garbage after template.ptr is obtained from it, even before template.ptr is passed into the C function (e.g. C.TemplateSetTemplate).

Solution

To avoid the finalizer goroutine race condition, I changed the finalizer to only free the template wrapper struct. v8::Template resides in the V8 heap (i.e. it is v8::Data), so it should get freed when the isolate is disposed. In the future, the solution for #105 should allow this v8::Data to be released earlier.

The second commit in this PR uses runtime.KeepAlive to avoid the template Go struct from being garbage collected before the template.ptr is finished being used.

I tested this in our application benchmark that was crashing and this seems to have fixed the crashes.

@codecov
Copy link

codecov bot commented Sep 27, 2021

Codecov Report

Merging #182 (608e79d) into master (7e0f8ff) will increase coverage by 0.05%.
The diff coverage is 100.00%.

❗ Current head 608e79d differs from pull request most recent head c7c6d8f. Consider uploading reports for the commit c7c6d8f to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master     #182      +/-   ##
==========================================
+ Coverage   94.91%   94.96%   +0.05%     
==========================================
  Files          12       12              
  Lines         492      497       +5     
==========================================
+ Hits          467      472       +5     
  Misses         15       15              
  Partials       10       10              
Impacted Files Coverage Δ
context.go 96.77% <100.00%> (+0.05%) ⬆️
function_template.go 93.75% <100.00%> (+0.20%) ⬆️
object_template.go 100.00% <100.00%> (ø)
template.go 76.92% <100.00%> (+1.92%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7e0f8ff...c7c6d8f. Read the comment docs.

@dylanahsmith dylanahsmith force-pushed the template-safe-finalizer branch from ebac6bf to cf200ca Compare September 27, 2021 15:58
t.ptr = nil
runtime.SetFinalizer(t, nil)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From https://pkg.go.dev/runtime#SetFinalizer

When the garbage collector finds an unreachable block with an associated finalizer, it clears the association and runs finalizer(obj) in a separate goroutine. This makes obj reachable again, but now without an associated finalizer.

so Go should will clear the finalizer for us, thus we don't need to do that here.

CHANGELOG.md Outdated Show resolved Hide resolved
…apper

v8::Persistent::Reset() shouldn't be used without synchronization
from the finalizer goroutine to avoid race conditions. Instead, we can
just free the wrapper and leave the V8 data on the isolate heap to be
freed when the isolate is disposed.
@dylanahsmith dylanahsmith force-pushed the template-safe-finalizer branch from 608e79d to 461353d Compare September 27, 2021 17:45
Otherwise, the garbage collector can collect the template and the
finalizer can free the C++ m_template struct before it is finished
being used.
@dylanahsmith dylanahsmith force-pushed the template-safe-finalizer branch from 461353d to c7c6d8f Compare September 27, 2021 17:46
@dylanahsmith dylanahsmith merged commit fd090e9 into master Sep 27, 2021
@dylanahsmith dylanahsmith deleted the template-safe-finalizer branch September 27, 2021 17:48
GustavoCaso pushed a commit to GustavoCaso/v8go that referenced this pull request Oct 14, 2021
* Fix crash from template finalizer releasing V8 data, just free the wrapper

v8::Persistent::Reset() shouldn't be used without synchronization
from the finalizer goroutine to avoid race conditions. Instead, we can
just free the wrapper and leave the V8 data on the isolate heap to be
freed when the isolate is disposed.

* Keep alive the template while its C++ pointer is still being used

Otherwise, the garbage collector can collect the template and the
finalizer can free the C++ m_template struct before it is finished
being used.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants