current commit
231103.1 · Reorganize examples into categorized folders (#341)
#include <chrono>
#include <thread>
#include "napi.h"

constexpr size_t ARRAY_LENGTH = 10;

// Data structure representing our thread-safe function context.
struct TsfnContext {
  TsfnContext(Napi::Env env) : deferred(Napi::Promise::Deferred::New(env)) {
    for (size_t i = 0; i < ARRAY_LENGTH; ++i) ints[i] = i;
  };

  // Native Promise returned to JavaScript
  Napi::Promise::Deferred deferred;

  // Native thread
  std::thread nativeThread;

  // Some data to pass around
  int ints[ARRAY_LENGTH];

  Napi::ThreadSafeFunction tsfn;
};

// The thread entry point. This takes as its arguments the specific
// threadsafe-function context created inside the main thread.
void threadEntry(TsfnContext* context);

// The thread-safe function finalizer callback. This callback executes
// at destruction of thread-safe function, taking as arguments the finalizer
// data and threadsafe-function context.
void FinalizerCallback(Napi::Env env, void* finalizeData, TsfnContext* context);

// Exported JavaScript function. Creates the thread-safe function and native
// thread. Promise is resolved in the thread-safe function's finalizer.
Napi::Value CreateTSFN(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  // Construct context data
  auto testData = new TsfnContext(env);

  // Create a new ThreadSafeFunction.
  testData->tsfn = Napi::ThreadSafeFunction::New(
      env,                           // Environment
      info[0].As<Napi::Function>(),  // JS function from caller
      "TSFN",                        // Resource name
      0,                             // Max queue size (0 = unlimited).
      1,                             // Initial thread count
      testData,                      // Context,
      FinalizerCallback,             // Finalizer
      (void*)nullptr                 // Finalizer data
  );
  testData->nativeThread = std::thread(threadEntry, testData);

  // Return the deferred's Promise. This Promise is resolved in the thread-safe
  // function's finalizer callback.
  return testData->deferred.Promise();
}

// The thread entry point. This takes as its arguments the specific
// threadsafe-function context created inside the main thread.
void threadEntry(TsfnContext* context) {
  // This callback transforms the native addon data (int *data) to JavaScript
  // values. It also receives the treadsafe-function's registered callback, and
  // may choose to call it.
  auto callback = [](Napi::Env env, Napi::Function jsCallback, int* data) {
    jsCallback.Call({Napi::Number::New(env, *data)});
  };

  for (size_t index = 0; index < ARRAY_LENGTH; ++index) {
    // Perform a call into JavaScript.
    napi_status status =
        context->tsfn.BlockingCall(&context->ints[index], callback);

    if (status != napi_ok) {
      Napi::Error::Fatal(
          "ThreadEntry",
          "Napi::ThreadSafeNapi::Function.BlockingCall() failed");
    }
    // Sleep for some time.
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
  }

  // Release the thread-safe function. This decrements the internal thread
  // count, and will perform finalization since the count will reach 0.
  context->tsfn.Release();
}

void FinalizerCallback(Napi::Env env,
                       void* finalizeData,
                       TsfnContext* context) {
  // Join the thread
  context->nativeThread.join();

  // Resolve the Promise previously returned to JS via the CreateTSFN method.
  context->deferred.Resolve(Napi::Boolean::New(env, true));
  delete context;
}

// Addon entry point
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports["createTSFN"] = Napi::Function::New(env, CreateTSFN);
  return exports;
}

NODE_API_MODULE(addon, Init)