current commit
231103.1 · Reorganize examples into categorized folders (#341)
#include <stdio.h>
#include <stdlib.h>
#include <node_api.h>

// Limit ourselves to this many primes, starting at 2
#define PRIME_COUNT 10
#define CHECK(expr) \
  { \
    if ((expr) == 0) { \
      fprintf(stderr, "%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, #expr); \
      fflush(stderr); \
      abort(); \
    } \
  }

typedef struct linked_box_s linked_box_t;
struct linked_box_s {
  linked_box_t* prev;
  int the_prime;
};

typedef struct {
  napi_async_work work;
  napi_deferred deferred;
  int data[PRIME_COUNT];
} AddonData;

// This function runs on a worker thread. It has no access to the JavaScript.
static void ExecuteWork(napi_env env, void* data) {
  AddonData* addon_data = (AddonData*)data;
  int idx_inner, idx_outer;
  int prime_count = 0;

  // Find the first 1000 prime numbers using an extremely inefficient algorithm.
  for (idx_outer = 2; prime_count < PRIME_COUNT; idx_outer++) {
    for (idx_inner = 2; idx_inner < idx_outer; idx_inner++) {
      if (idx_outer % idx_inner == 0) {
        break;
      }
    }
    if (idx_inner < idx_outer) {
      continue;
    }

    // Save the prime number to the heap. The JavaScript marshaller (CallJs)
    // will free this item after having sent it to JavaScript.
    addon_data->data[prime_count] = idx_outer;
    ++prime_count;
  }
}

// This function runs on the main thread after `ExecuteWork` exits.
static void WorkComplete(napi_env env, napi_status status, void* data) {
  if (status != napi_ok) {
    return;
  }

  AddonData* addon_data = (AddonData*)data;
  napi_value array;
  CHECK(napi_create_array(env, &array) == napi_ok);

  for (uint32_t idx = 0; idx < PRIME_COUNT; idx++) {
    napi_value js_the_prime;

    // Convert the integer to a napi_value.
    CHECK(napi_create_int32(env, addon_data->data[idx], &js_the_prime) == napi_ok);

    // Store the js value in the array.
    CHECK(napi_set_element(env, array, idx, js_the_prime) == napi_ok);
  }
  CHECK(napi_resolve_deferred(env, addon_data->deferred, array) == napi_ok);

  // Clean up the work item associated with this run.
  CHECK(napi_delete_async_work(env, addon_data->work) == napi_ok);

  // Set both values to NULL so JavaScript can order a new run of the thread.
  addon_data->work = NULL;
  addon_data->deferred = NULL;
}

// Create a deferred promise and an async queue work item.
static napi_value StartWork(napi_env env, napi_callback_info info) {
  napi_value work_name, promise;
  AddonData* addon_data;

  // Retrieve the per-addon data.
  CHECK(napi_get_cb_info(env,
                          info,
                          NULL,
                          NULL,
                          NULL,
                          (void**)(&addon_data)) == napi_ok);

  // Ensure that no work is currently in progress.
  CHECK(addon_data->work == NULL && "Only one work item must exist at a time");

  // Create a string to describe this asynchronous operation.
  CHECK(napi_create_string_utf8(env,
                                 "Node-API Deferred Promise from Async Work Item",
                                 NAPI_AUTO_LENGTH,
                                 &work_name) == napi_ok);

  // Create a deferred promise which we will resolve at the completion of the work.
  CHECK(napi_create_promise(env,
                             &(addon_data->deferred),
                             &promise) == napi_ok);

  // Create an async work item, passing in the addon data, which will give the
  // worker thread access to the above-created deferred promise.
  CHECK(napi_create_async_work(env,
                                NULL,
                                work_name,
                                ExecuteWork,
                                WorkComplete,
                                addon_data,
                                &(addon_data->work)) == napi_ok);

  // Queue the work item for execution.
  CHECK(napi_queue_async_work(env, addon_data->work) == napi_ok);

  // This causes created `promise` to be returned to JavaScript.
  return promise;
}

// Free the per-addon-instance data.
static void addon_getting_unloaded(napi_env env, void* data, void* hint) {
  AddonData* addon_data = (AddonData*)data;
  CHECK(addon_data->work == NULL &&
      "No work item in progress at module unload");
  free(addon_data);
}

// The commented-out return type and the commented out formal function
// parameters below help us keep in mind the signature of the addon
// initialization function. We write the body as though the return value were as
// commented below and as though there were parameters passed in as commented
// below.
/*napi_value*/ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {

  // Define addon-level data associated with this instance of the addon.
  AddonData* addon_data = (AddonData*)malloc(sizeof(*addon_data));
  addon_data->work = NULL;

  // Define the properties that will be set on exports.
  napi_property_descriptor start_work = {
    "startWork",
    NULL,
    StartWork,
    NULL,
    NULL,
    NULL,
    napi_default,
    addon_data
  };

  // Decorate exports with the above-defined properties.
  CHECK(napi_define_properties(env, exports, 1, &start_work) == napi_ok);

  // Associate the addon data with the exports object, to make sure that when
  // the addon gets unloaded our data gets freed.
  CHECK(napi_wrap(env,
                   exports,
                   addon_data,
                   addon_getting_unloaded,
                   NULL,
                   NULL) == napi_ok);

  // Return the decorated exports object.
  return exports;
}