mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-14 04:01:35 +00:00
165 lines
5.6 KiB
C
165 lines
5.6 KiB
C
/*
|
|
* Node-API v8 binding for asynchronous flock(LOCK_EX | LOCK_NB).
|
|
* The caller owns fd through completion; this module never opens, duplicates,
|
|
* closes, or explicitly unlocks it. The callback receives zero or a positive
|
|
* errno; JavaScript owns the promise and syscall error construction.
|
|
*/
|
|
|
|
#include <node_api.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <sys/file.h>
|
|
|
|
typedef struct {
|
|
napi_env env;
|
|
napi_ref callback;
|
|
napi_async_work work;
|
|
napi_async_cleanup_hook_handle cleanup;
|
|
int fd;
|
|
int error;
|
|
bool closing;
|
|
} lock_request;
|
|
|
|
static void check_status(napi_status status, const char *message) {
|
|
if (status != napi_ok) {
|
|
napi_fatal_error("flock", NAPI_AUTO_LENGTH, message, NAPI_AUTO_LENGTH);
|
|
}
|
|
}
|
|
|
|
static void release_request(lock_request *request) {
|
|
if (request->callback != NULL) {
|
|
(void)napi_delete_reference(request->env, request->callback);
|
|
}
|
|
if (request->work != NULL) {
|
|
(void)napi_delete_async_work(request->env, request->work);
|
|
}
|
|
if (request->cleanup != NULL) {
|
|
(void)napi_remove_async_cleanup_hook(request->cleanup);
|
|
}
|
|
free(request);
|
|
}
|
|
|
|
static napi_value throw_setup_error(napi_env env, napi_status status,
|
|
const char *message) {
|
|
if (status != napi_pending_exception) {
|
|
status = napi_throw_error(env, "ERR_FLOCK_ASYNC_WORK", message);
|
|
bool pending;
|
|
/* Error construction can fail with both generic_failure and a JS exception. */
|
|
check_status(napi_is_exception_pending(env, &pending), "Cannot inspect flock setup exception");
|
|
if (!pending && status != napi_pending_exception) check_status(status, message);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void execute_lock(napi_env env, void *data) {
|
|
(void)env;
|
|
lock_request *request = data;
|
|
request->error = flock(request->fd, LOCK_EX | LOCK_NB) == 0 ? 0 : errno;
|
|
}
|
|
|
|
static void complete_lock(napi_env env, napi_status status, void *data) {
|
|
lock_request *request = data;
|
|
if (env != NULL && !request->closing) {
|
|
napi_value callback;
|
|
napi_value receiver;
|
|
napi_value result;
|
|
check_status(status, "flock async work did not complete");
|
|
check_status(napi_get_reference_value(env, request->callback, &callback),
|
|
"Cannot retrieve flock callback");
|
|
check_status(napi_get_undefined(env, &receiver), "Cannot create flock receiver");
|
|
check_status(napi_create_int32(env, request->error, &result),
|
|
"Cannot create flock result");
|
|
status = napi_call_function(env, receiver, callback, 1, &result, NULL);
|
|
} else {
|
|
status = napi_ok;
|
|
}
|
|
release_request(request);
|
|
/* Node's async-work dispatcher reports callback exceptions and handles termination. */
|
|
if (status != napi_pending_exception) {
|
|
check_status(status, "Cannot invoke flock callback");
|
|
}
|
|
}
|
|
|
|
static void cleanup_lock(napi_async_cleanup_hook_handle handle, void *data) {
|
|
(void)handle;
|
|
lock_request *request = data;
|
|
request->closing = true;
|
|
/*
|
|
* Running work cannot be cancelled. The hook keeps the environment alive
|
|
* until completion releases both the work and this hook; it never frees
|
|
* memory that the execution thread can still access.
|
|
*/
|
|
(void)napi_cancel_async_work(request->env, request->work);
|
|
}
|
|
|
|
static napi_value try_lock(napi_env env, napi_callback_info info) {
|
|
size_t argc = 2;
|
|
napi_value argv[2];
|
|
double fd;
|
|
napi_status status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
if (status != napi_ok) {
|
|
return throw_setup_error(env, status, "Cannot read flock arguments");
|
|
}
|
|
if (argc < 1 || napi_get_value_double(env, argv[0], &fd) != napi_ok) {
|
|
(void)napi_throw_type_error(env, NULL, "fd must be a number");
|
|
return NULL;
|
|
}
|
|
if (!(fd >= INT_MIN && fd <= INT_MAX) || fd != (int)fd) {
|
|
(void)napi_throw_range_error(env, NULL, "fd must be a signed C int");
|
|
return NULL;
|
|
}
|
|
napi_valuetype callback_type;
|
|
if (argc < 2 || napi_typeof(env, argv[1], &callback_type) != napi_ok ||
|
|
callback_type != napi_function) {
|
|
(void)napi_throw_type_error(env, NULL, "callback must be a function");
|
|
return NULL;
|
|
}
|
|
|
|
lock_request *request = calloc(1, sizeof(*request));
|
|
if (request == NULL) {
|
|
(void)napi_throw_error(env, "ENOMEM", "Cannot allocate flock async work");
|
|
return NULL;
|
|
}
|
|
request->env = env;
|
|
request->fd = (int)fd;
|
|
|
|
status = napi_create_reference(env, argv[1], 1, &request->callback);
|
|
if (status != napi_ok) {
|
|
release_request(request);
|
|
return throw_setup_error(env, status, "Cannot retain flock callback");
|
|
}
|
|
napi_value name;
|
|
status = napi_create_string_utf8(env, "flock", NAPI_AUTO_LENGTH, &name);
|
|
if (status == napi_ok) {
|
|
status = napi_create_async_work(env, NULL, name, execute_lock, complete_lock,
|
|
request, &request->work);
|
|
}
|
|
if (status != napi_ok) {
|
|
release_request(request);
|
|
return throw_setup_error(env, status, "Cannot create flock async work");
|
|
}
|
|
status = napi_add_async_cleanup_hook(env, cleanup_lock, request, &request->cleanup);
|
|
if (status != napi_ok) {
|
|
release_request(request);
|
|
return throw_setup_error(env, status, "Cannot register flock environment cleanup");
|
|
}
|
|
status = napi_queue_async_work(env, request->work);
|
|
if (status != napi_ok) {
|
|
/* Queue failure schedules no completion callback. */
|
|
release_request(request);
|
|
return throw_setup_error(env, status, "Cannot queue flock async work");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
NAPI_MODULE_INIT() {
|
|
napi_value function;
|
|
if (napi_create_function(env, "tryLock", NAPI_AUTO_LENGTH, try_lock,
|
|
NULL, &function) != napi_ok ||
|
|
napi_set_named_property(env, exports, "tryLock", function) != napi_ok) {
|
|
return NULL;
|
|
}
|
|
return exports;
|
|
}
|