Skip to content
This repository has been archived by the owner on Oct 16, 2021. It is now read-only.

How to migrate from eio_custom to uv_queue_work

ry edited this page Jun 15, 2012 · 2 revisions

For native modules that wanted to utilize the thread pool, the original function to do this was eio_custom(), exposed by libeio. You would have code that looked something like this:

/** THIS IS THE OLD API, DON'T COPY THIS, SEE BELOW!! **/

/* the "do work" callback; called on the thread pool */
int doing_work (eio_req *req) {
  /* Something computationally expensive here */
  req->rtn = 1 + 1;
  return 0;
}

/* the "after work" callback; called on the main thread */
int after_doing_work (eio_req *req) {
  HandleScope scope;

  my_struct *m = (my_struct *)req->data;

  Handle<Value> argv[1];
  argv[0] = Integer::New(r->rtn);

  TryCatch try_catch;
  m->callback->Call(Context::GetCurrent()->Global(), 1, argv);

  // cleanup
  m->callback.Dispose();
  delete m;

  if (try_catch.HasCaught())
    FatalException(try_catch);
  return 0;
}

/* the JS entry point */
Handle<Value> start_doing_work (const Arguments& args) {
  HandleScope scope;

  my_struct *m = new my_struct;
  m->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));

  eio_custom(doing_work, EIO_PRI_DEFAULT, after_doing_work, m);

  return Undefined();
}

Now, starting with node v0.5.6, there is a new preferred method of utilizing the thread pool for CPU-intensive tasks: uv_queue_work, exposed by libuv. The API is similar but slightly different:

/** THIS IS THE CURRENT API. COPY THIS!!! **/

/* the "do work" callback; called on the thread pool */
void doing_work (uv_work_t *req) {
  /* Something computationally expensive here */
  req->rtn = 1 + 1;
}

/* the "after work" callback; called on the main thread */
void after_doing_work (uv_work_t *req) {
  HandleScope scope;

  my_struct *m = (my_struct *)req->data;

  Handle<Value> argv[1];
  argv[0] = Integer::New(r->rtn);

  TryCatch try_catch;
  m->callback->Call(Context::GetCurrent()->Global(), 1, argv);

  // cleanup
  m->callback.Dispose();
  delete m;
  delete req;

  if (try_catch.HasCaught())
    FatalException(try_catch);
}

/* the JS entry point */
Handle<Value> start_doing_work (const Arguments& args) {
  HandleScope scope;

  uv_work_t *req = new uv_work_t;
  my_struct *m = new my_struct;
  req->data = m;
  m->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));

  uv_queue_work(uv_default_loop(), req, doing_work, after_doing_work);

  return Undefined();
}

Rundown:

  • Call uv_queue_work() instead of eio_custom() to initiate async work on the thread pool.
  • The "do work" and "after work" callback functions now accept a uv_work_t * pointer as their arguments, instead of a eio_req * pointer.
  • You must manually create the uv_work_t instance, and pass it to uv_queue_work(). It's not done for you like with libeio.
  • You can attach a custom void * to the data field of the uv_work_t struct.
  • Don't forget to free() or delete the uv_work_t struct at the end of the "after work" callback function.

If you desire backwards-compatibility with node <= v0.4.x, then check out this shim header file, which will conditionally use uv_queue_work() when available or fall back to using eio_custom() when it's not.

uv_queue_work() works on Windows, eio_custom() does not.