Skip to content

(Typed)ThreadSafeFunction.Release() crashes renderer process on reload (& abort?) #1109

Closed as not planned
@robinchrist

Description

@robinchrist

We are using a native addon with Electron (Both v14 and v16 show the same behaviour)

There is a class, which roughly looks like this:

class Foo : public Napi::ObjectWrap<Foo> {
public:
    Foo(
        const Napi::CallbackInfo& info
    ):
        Napi::ObjectWrap<Foo>(info)
    {
        tsfn1_ = ...
        tsfn2_ = ...
    }

    ~Foo() {
        tsfn1_.Release();
        tsfn2_.Release();
        ...
    }
private:
    TSFNT1 tsfn1_;
    TSFNT2 tsfn2_;
    ...
}

Everything works fine - Except when Electron reloads.
There are some situation when we need to reload the Electron application via getCurrentWebContents().reloadIgnoringCache() - Which fails spectacularly with a renderer crash.
This is an issue both for our users AND when in development when using hot reload.

I have tracked down the issue to the .Release() calls
If I remove the .Release() calls, everything seems to work fine, including reload.

I have tried to debug the issue further with a debug build of Electron, but at least v14 (have not tested v16) triggers another assertion when reloading which can't be fixed (I stripped the addon to not contain any exports and just the register call and it still caused a renderer crash on reload with Debug Electron)

My theory: Node destroys the TSFNs on Node's side before it frees / destroys the objects of the addon. I can't say whether this is just a wild theory or a real explanation, but I think a couple of Node devs here could know?

Our fix for now: Don't free the TSNFs in the destructor. The Foo object is long-living and usually only created once and destroyed once in the lifecycle of the addon.

FWIW, that's the stacktrace:

 thread #1, name = 'electron', stop reason = signal SIGABRT
  * frame #0: 0x00007f70aad7218b libc.so.6`__GI_raise(sig=<unavailable>) at raise.c:51:1
    frame #1: 0x00007f70aad51859 libc.so.6`__GI_abort at abort.c:79:7
    frame #2: 0x00005565c007d794 electron`uv_mutex_lock + 20
    frame #3: 0x00005565c63f7266 electron`napi_release_threadsafe_function + 38
    frame #4: 0x00007f709c498c9a addon.node`Napi::TypedThreadSafeFunction<std::nullptr_t, std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::unique_ptr<SecretType, std::default_delete<SecretType> > >, &(Foo::SecretJSGlueFunction(Napi::Env, Napi::Function, std::nullptr_t*, std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::unique_ptr<SecretType, std::default_delete<SecretType> > >*))>::Release(this=0x000013900794e100) at napi-inl.h:5163:10
    frame #5: 0x00007f709c4905d8 addon.node`Foo::~Foo(this=0x000013900794e000) at Foo.cc:166:25
    frame #6: 0x00007f709c490839 addon.node`Foo::~Foo(this=0x000013900794e000) at Foo.cc:164:48
    frame #7: 0x00007f709c4a2632 addon.node`Napi::ObjectWrap<Foo>::FinalizeCallback(env=0x000013900078ae20, data=0x000013900794e000, (null)=0x0000000000000000) at napi-inl.h:4387:3
    frame #8: 0x00005565c63f765f electron
    frame #9: 0x00005565c63f8adf electron
    frame #10: 0x00005565c63f74a6 electron
    frame #11: 0x00005565c63f7ea5 electron
    frame #12: 0x00005565c63f7f0e electron
    frame #13: 0x00005565c63f8218 electron
    frame #14: 0x00005565c0072729 electron`uv_run + 521
    frame #15: 0x00005565c63d00e1 electron
    frame #16: 0x00005565c63d0638 electron
    frame #17: 0x00005565c6398264 electron`node::FreeEnvironment(node::Environment*) + 180
    frame #18: 0x00005565c01d6698 electron
    frame #19: 0x00005565c56662d5 electron
    frame #20: 0x00005565c43085a5 electron
    frame #21: 0x00005565c43072a6 electron
    frame #22: 0x00005565c4684c0f electron
    frame #23: 0x00005565c4ba1468 electron
    frame #24: 0x00005565c4ba24f4 electron
    frame #25: 0x00005565c4bb69e2 electron
    frame #26: 0x00005565c4bb97bd electron
    frame #27: 0x00005565c470b503 electron
    frame #28: 0x00005565c5657761 electron
    frame #29: 0x00005565c5671db3 electron
    frame #30: 0x00005565c5671bfb electron
    frame #31: 0x00005565c56561cc electron
    frame #32: 0x00005565c5ae4805 electron
    frame #33: 0x00005565c0f493c9 electron
    frame #34: 0x00005565c5ae4d2b electron
    frame #35: 0x00005565c2fa29e8 electron
    frame #36: 0x00005565c2fa771d electron
    frame #37: 0x00005565c2fa3f4a electron
    frame #38: 0x00005565c31c0b29 electron
    frame #39: 0x00005565c2fa505f electron
    frame #40: 0x00005565c2c6e2f5 electron
    frame #41: 0x00005565c2c84316 electron
    frame #42: 0x00005565c2c854af electron
    frame #43: 0x00005565c2c3901e electron
    frame #44: 0x00005565c2c85914 electron
    frame #45: 0x00005565c2c575f2 electron
    frame #46: 0x00005565c61e682b electron
    frame #47: 0x00005565c104fe53 electron
    frame #48: 0x00005565c104d77f electron
    frame #49: 0x00005565c104e169 electron
    frame #50: 0x00005565c008335c electron
    frame #51: 0x00007f70aad530b3 libc.so.6`__libc_start_main(main=0x00005565c0083150, argc=15, argv=0x00007fff22fedd28, init=<unavailable>, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fff22fedd18) at libc-start.c:308:16
    frame #52: 0x00005565bfd18daa electron`_start + 42

Electron versions: v14 & v16
Compiler: Clang 10+
OS: definitely Linux, but seems to happen on MacOS and Windows
Arch: AMD64, but seems to happen on ARM64 too

Any ideas?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions