@@ -11,47 +11,48 @@ namespace Babylon
1111 // handling garbage collection and shutdown scenarios. This class provides and manages the schedulers intended
1212 // for a N-API object to use with arcana tasks. It is different than the typical scheduler as this class itself
1313 // is not a scheduler directly, but instead hands out scheduler via its `Get()` function. It provides special
14- // handling for when the JsRuntime begins shutting down, i.e., when JsRuntime::NotifyDisposing is called:
15- // 1. The destructor blocks if there are outstanding schedulers not yet invoked on the JavaScript thread.
14+ // handling for when the JsRuntime begins shutting down, i.e., when JsRuntime::NotifyDisposing is called.
15+ // 1. Calling `Rundown` blocks execution until all outstanding schedulers are invoked on the JavaScript thread.
1616 // 2. Once the JsRuntime begins shutting down, all schedulers will reroute its dispatch calls from the
1717 // JsRuntime to a separate dispatcher owned by the JsRuntimeScheduler itself. This class will then be able
1818 // to pump this dispatcher in its destructor to prevent deadlocks.
1919 //
2020 // The typical pattern for an arcana task will look something like this:
21- //
2221 // class MyClass
2322 // {
2423 // public:
25- // void MyFunction(const Napi::CallbackInfo& info);
24+ // ~MyClass()
25+ // {
26+ // m_cancellationSource.cancel();
27+ //
28+ // // Wait for asynchronous operations to complete.
29+ // m_runtimeScheduler.Rundown();
30+ // }
31+ //
32+ // void MyFunction(const Napi::CallbackInfo& info)
33+ // {
34+ // const auto callback{info[0].As<Napi::Function>()};
35+ //
36+ // arcana::make_task(arcana::threadpool_scheduler, m_cancellationSource, []() {
37+ // // do some asynchronous work
38+ // }).then(m_runtimeScheduler.Get(), m_cancellationSource, [thisRef = Napi::Persistent(info.This()), callback = Napi::Persistent(callback)]() {
39+ // callback.Call({});
40+ // });
41+ // }
2642 //
2743 // private:
2844 // arcana::cancellation_source m_cancellationSource;
29- //
30- // // Put this last so that it gets destructed first.
3145 // JsRuntimeScheduler m_runtimeScheduler;
3246 // };
3347 //
34- // void MyClass::MyFunction(const Napi::CallbackInfo& info)
35- // {
36- // const auto callback{info[0].As<Napi::Function>()};
37- //
38- // arcana::make_task(arcana::threadpool_scheduler, m_cancellationSource, []()
39- // {
40- // // do some asynchronous work
41- // }).then(m_runtimeScheduler.Get(), m_cancelSource, [thisRef = Napi::Persistent(info.This()), callback = Napi::Persistent(callback)]() {
42- // {
43- // callback.Call({});
44- // });
45- // }
46- //
4748 // **IMPORTANT**:
48- // 1. To prevent continuations from accessing destructed objects, declare the JsRuntimeScheduler at the end of
49- // the N-API class. The destructor of the JsRuntimeScheduler will call `Rundown()` which will block until
50- // all of its schedulers are invoked. If this is not possible, call `Rundown()` manually in the destructor
51- // of the N-API class.
49+ // 1. To prevent continuations from accessing freed memory, the destructor of the N-API class is expected to call
50+ // `Rundown()` which blocks execution until all of its schedulers are invoked. Failing to do so will result in
51+ // an exception if there are outstanding schedulers not yet invoked.
5252 // 2. The last continuation that accesses members of the N-API object, including the cancellation associated with
53- // the continuation must capture a persistent reference to the N-API object itself to prevent GC from collecting
54- // the N-API object during the asynchronous operation.
53+ // the continuation, must capture a persistent reference to the N-API object itself to prevent the GC from
54+ // collecting the N-API object during the asynchronous operation. Failing to do so will result in a hang
55+ // when `Rundown()` is called in the N-API class destructor.
5556 class JsRuntimeScheduler
5657 {
5758 public:
@@ -67,7 +68,10 @@ namespace Babylon
6768
6869 ~JsRuntimeScheduler ()
6970 {
70- Rundown ();
71+ if (m_count > 0 )
72+ {
73+ throw std::runtime_error{" Schedulers for the JavaScript thread are not yet invoked" };
74+ }
7175 }
7276
7377 // Wait until all of the schedulers are invoked.
0 commit comments