22#include < ReactCommon/CallInvoker.h>
33#include " Logger.hpp"
44
5- struct AsyncJob {
6- using IdType = uint64_t ;
7- enum State { Created, Queued, Completed, Cancelled, Deleted };
5+ using IdType = uint64_t ;
86
7+ struct AsyncContext {
98 IdType id{};
10- State state{};
119 napi_env env;
1210 napi_value async_resource;
1311 napi_value async_resource_name;
14- napi_async_execute_callback execute;
15- napi_async_complete_callback complete;
16- void * data{nullptr };
1712
18- static AsyncJob* fromWork (napi_async_work work) {
19- return reinterpret_cast <AsyncJob*>(work);
20- }
21- static napi_async_work toWork (AsyncJob* job) {
22- return reinterpret_cast <napi_async_work>(job);
23- }
13+ AsyncContext (
14+ napi_env env, napi_value async_resource, napi_value async_resource_name)
15+ : env{env},
16+ async_resource{async_resource},
17+ async_resource_name{async_resource_name} {}
2418};
2519
26- class AsyncWorkRegistry {
27- public:
28- using IdType = AsyncJob::IdType;
20+ struct AsyncJob : AsyncContext {
21+ enum State { Created, Queued, Completed, Cancelled, Deleted };
2922
30- std::shared_ptr<AsyncJob> create (napi_env env,
23+ State state{State::Created};
24+ napi_async_execute_callback execute;
25+ napi_async_complete_callback complete;
26+ void * data{nullptr };
27+
28+ AsyncJob (napi_env env,
3129 napi_value async_resource,
3230 napi_value async_resource_name,
3331 napi_async_execute_callback execute,
3432 napi_async_complete_callback complete,
35- void * data) {
36- const auto job = std::shared_ptr<AsyncJob>(new AsyncJob{
37- .id = next_id (),
38- .state = AsyncJob::State::Created,
39- .env = env,
40- .async_resource = async_resource,
41- .async_resource_name = async_resource_name,
42- .execute = execute,
43- .complete = complete,
44- .data = data,
45- });
46-
47- jobs_[job->id ] = job;
48- return job;
49- }
33+ void * data)
34+ : AsyncContext{env, async_resource, async_resource_name},
35+ execute{execute},
36+ complete{complete},
37+ data{data} {}
38+ };
5039
51- std::shared_ptr<AsyncJob> get (napi_async_work work) const {
52- const auto job = AsyncJob::fromWork (work);
53- if (!job) {
54- return {};
55- }
56- if (const auto it = jobs_.find (job->id ); it != jobs_.end ()) {
57- return it->second ;
58- }
59- return {};
40+ template <class T , class U >
41+ class Container {
42+ public:
43+ void push (std::shared_ptr<T>&& obj) {
44+ const auto id = nextId ();
45+ obj->id = id;
46+ map_[id] = std::move (obj);
6047 }
61-
62- bool release (IdType id) {
63- if (const auto it = jobs_.find (id); it != jobs_.end ()) {
64- it->second ->state = AsyncJob::State::Deleted;
65- jobs_.erase (it);
66- return true ;
67- }
68- return false ;
48+ std::shared_ptr<T> get (const U obj) {
49+ return map_.contains (id (obj)) ? map_[id (obj)] : nullptr ;
6950 }
51+ bool release (const U obj) { return map_.erase (id (obj)) > 0 ; }
7052
7153 private:
72- IdType next_id () {
73- if (current_id_ == std::numeric_limits<IdType>::max ()) [[unlikely]] {
74- current_id_ = 0 ;
54+ IdType id (const U obj) const {
55+ auto casted = reinterpret_cast <T*>(obj);
56+ return casted ? casted->id : 0 ;
57+ }
58+ IdType nextId () {
59+ if (currentId_ == std::numeric_limits<IdType>::max ()) [[unlikely]] {
60+ currentId_ = 0 ;
7561 }
76- return ++current_id_ ;
62+ return ++currentId_ ;
7763 }
7864
79- IdType current_id_ {0 };
80- std::unordered_map<IdType, std::shared_ptr<AsyncJob >> jobs_ ;
65+ IdType currentId_ {0 };
66+ std::unordered_map<IdType, std::shared_ptr<T >> map_ ;
8167};
8268
8369static std::unordered_map<napi_env, std::weak_ptr<facebook::react::CallInvoker>>
8470 callInvokers;
85- static AsyncWorkRegistry asyncWorkRegistry;
71+ static Container<AsyncJob, napi_async_work> jobs_;
72+ static Container<AsyncContext, napi_async_context> contexts_;
8673
8774namespace callstack ::nodeapihost {
8875
@@ -104,20 +91,16 @@ napi_status napi_create_async_work(napi_env env,
10491 napi_async_complete_callback complete,
10592 void * data,
10693 napi_async_work* result) {
107- const auto job = asyncWorkRegistry. create (
94+ auto job = std::make_shared<AsyncJob> (
10895 env, async_resource, async_resource_name, execute, complete, data);
109- if (!job) {
110- log_debug (" Error: Failed to create async work job" );
111- return napi_generic_failure;
112- }
113-
114- *result = AsyncJob::toWork (job.get ());
96+ *result = reinterpret_cast <napi_async_work>(job.get ());
97+ jobs_.push (std::move (job));
11598 return napi_ok;
11699}
117100
118101napi_status napi_queue_async_work (
119102 node_api_basic_env env, napi_async_work work) {
120- const auto job = asyncWorkRegistry .get (work);
103+ const auto job = jobs_ .get (work);
121104 if (!job) {
122105 log_debug (" Error: Received null job in napi_queue_async_work" );
123106 return napi_invalid_arg;
@@ -151,13 +134,14 @@ napi_status napi_queue_async_work(
151134
152135napi_status napi_delete_async_work (
153136 node_api_basic_env env, napi_async_work work) {
154- const auto job = asyncWorkRegistry .get (work);
137+ const auto job = jobs_ .get (work);
155138 if (!job) {
156139 log_debug (" Error: Received non-existent job in napi_delete_async_work" );
157140 return napi_invalid_arg;
158141 }
159142
160- if (!asyncWorkRegistry.release (job->id )) {
143+ job->state = AsyncJob::State::Deleted;
144+ if (!jobs_.release (work)) {
161145 log_debug (" Error: Failed to release async work job" );
162146 return napi_generic_failure;
163147 }
@@ -167,7 +151,7 @@ napi_status napi_delete_async_work(
167151
168152napi_status napi_cancel_async_work (
169153 node_api_basic_env env, napi_async_work work) {
170- const auto job = asyncWorkRegistry .get (work);
154+ const auto job = jobs_ .get (work);
171155 if (!job) {
172156 log_debug (" Error: Received null job in napi_cancel_async_work" );
173157 return napi_invalid_arg;
@@ -187,4 +171,42 @@ napi_status napi_cancel_async_work(
187171 job->state = AsyncJob::State::Cancelled;
188172 return napi_ok;
189173}
174+
175+ napi_status napi_async_init (napi_env env,
176+ napi_value async_resource,
177+ napi_value async_resource_name,
178+ napi_async_context* result) {
179+ if (!result) {
180+ return napi_invalid_arg;
181+ }
182+ auto context =
183+ std::make_shared<AsyncContext>(env, async_resource, async_resource_name);
184+ *result = reinterpret_cast <napi_async_context>(context.get ());
185+ contexts_.push (std::move (context));
186+ return napi_ok;
187+ }
188+
189+ napi_status napi_async_destroy (napi_env env, napi_async_context async_context) {
190+ if (!async_context) {
191+ return napi_invalid_arg;
192+ }
193+ if (!contexts_.release (async_context)) {
194+ return napi_generic_failure;
195+ }
196+ return napi_ok;
197+ }
198+
199+ napi_status napi_make_callback (napi_env env,
200+ napi_async_context async_context,
201+ napi_value recv,
202+ napi_value func,
203+ size_t argc,
204+ const napi_value* argv,
205+ napi_value* result) {
206+ const auto status = napi_call_function (env, recv, func, argc, argv, result);
207+ if (status == napi_pending_exception && async_context) {
208+ contexts_.release (async_context);
209+ }
210+ return status;
211+ }
190212} // namespace callstack::nodeapihost
0 commit comments