Skip to content

Commit aa2faf0

Browse files
committed
feat: added support to three async functions
1 parent a23af5a commit aa2faf0

File tree

3 files changed

+108
-67
lines changed

3 files changed

+108
-67
lines changed

packages/host/cpp/RuntimeNodeApiAsync.cpp

Lines changed: 89 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,87 +2,74 @@
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

8369
static 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

8774
namespace 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

118101
napi_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

152135
napi_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

168152
napi_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

packages/host/cpp/RuntimeNodeApiAsync.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,20 @@ napi_status napi_delete_async_work(
2323

2424
napi_status napi_cancel_async_work(
2525
node_api_basic_env env, napi_async_work work);
26+
27+
napi_status napi_async_init(napi_env env,
28+
napi_value async_resource,
29+
napi_value async_resource_name,
30+
napi_async_context* result);
31+
32+
napi_status napi_async_destroy(napi_env env, napi_async_context async_context);
33+
34+
napi_status napi_make_callback(napi_env env,
35+
napi_async_context async_context,
36+
napi_value recv,
37+
napi_value func,
38+
size_t argc,
39+
const napi_value* argv,
40+
napi_value* result);
41+
2642
} // namespace callstack::nodeapihost

packages/host/scripts/generate-weak-node-api-injector.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const IMPLEMENTED_RUNTIME_FUNCTIONS = [
2020
"napi_fatal_error",
2121
"napi_get_node_version",
2222
"napi_get_version",
23+
"napi_async_init",
24+
"napi_async_destroy",
25+
"napi_make_callback",
2326
];
2427

2528
/**

0 commit comments

Comments
 (0)