Search code examples
c++multithreadingv8

Access a pointer from a child thread


I have a struct that looks like this:

struct Work {
  uv_work_t requst;
  Local<Promise::Resolver> resolver;
};

I then have this method which crops an image with imagemagick:

void MagickNode::Crop(const FunctionCallbackInfo<Value> &args) {
  // Create a promise
  Local<Promise::Resolver> resolver = v8::Promise::Resolver::New(i.isolate);
  args.GetReturnValue().Set(resolver->GetPromise());

  // Create a worker
  Work *work = new Work();
  work->requst.data = work;
  work->resolver = resolver;

  // Do work
  uv_queue_work(uv_default_loop(), &work->requst, [](uv_work_t *req) {
    // Do the heavy lifting
  }, WorkVoidComplete);
}

When the process is complete, I want to resolve the promise. Which I do here:

void MagickNode::WorkVoidComplete(uv_work_t *req, int status) {
  // Setup the required items
  Isolate *isolate = Isolate::GetCurrent();
  v8::HandleScope handleScope(isolate);
  Work *work = static_cast<Work *>(req->data);

  // Resolve the promise
  work->resolver->Resolve(Undefined(isolate));
  delete work;
}

The problem, is that when work->resolver->Resolve runs it gives Segmentation fault. I think that this is because it is executed from a thread when it was created on the parent thread giving me no access.

How can I get access and run that line?

If I move that line to the end of the method MagickNode::Crop, it runs without error and the promise gets called.


Solution

  • Your WorkVoidComplete will actually be called in the loop thread. The problem here is that you're using a Local<T> handle instead of a Persistent<T> handle.

    When the scope of MagickNode::Crop closes the local handle is released.

    Changing your struct to use v8::Persistent<Promise::Resolver> will fix the problem. Note that when you're done with a persistent reference handle it must be manually released by calling Reset().

    class Work {
     public:
      Work(Isolate* i, Local<Promise::Resolver> r)
        : isolate_(i) {
          resolver_.Reset(i, r);
        }
    
      ~Work() {
        resolver_.Reset();
      }
    
      inline uv_work_t* request() { return &request_; }
      inline Isolate* isolate() { return isolate_; }
      inline Local<Promise::Resolver> resolver() {
        return resolver_.Get(isolate_);
      }
    
     private:
      Isolate* isolate_;
      Persistent<Promise::Resolver> resolver_;
      uv_work_t request_;
    };
    
    void Something(const FunctionCallbackInfo<Value>& args) {
      Isolate* isolate = args.GetIsolate();
      Local<Context> context = isolate->GetCurrentContext();
    
      Local<Promise::Resolver> resolver =
        v8::Promise::Resolver::New(context).ToLocalChecked();
    
      Work* work = new Work(isolate, resolver);
      work->request()->data = work;
      uv_queue_work(uv_default_loop(), work->request(), DoWork, WorkComplete);
    
      args.GetReturnValue().Set(resolver->GetPromise());
    }