-
Notifications
You must be signed in to change notification settings - Fork 287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Task API for running background tasks in the libuv thread pool #214
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,3 +16,4 @@ pub mod mem; | |
pub mod fun; | ||
pub mod convert; | ||
pub mod class; | ||
pub mod task; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
#ifndef NEON_TASK_H_ | ||
#define NEON_TASK_H_ | ||
|
||
#include <nan.h> | ||
#include <uv.h> | ||
#include "neon.h" | ||
#include "v8.h" | ||
|
||
namespace neon { | ||
|
||
class Task { | ||
public: | ||
Task(v8::Isolate *isolate, | ||
void *rust_task, | ||
Neon_TaskPerformCallback perform, | ||
Neon_TaskCompleteCallback complete, | ||
v8::Local<v8::Function> callback) | ||
: isolate_(isolate), | ||
rust_task_(rust_task), | ||
perform_(perform), | ||
complete_(complete) | ||
{ | ||
request_.data = this; | ||
result_ = nullptr; | ||
callback_.Reset(isolate, callback); | ||
} | ||
|
||
~Task() { | ||
callback_.Reset(); | ||
} | ||
|
||
void execute() { | ||
result_ = perform_(rust_task_); | ||
} | ||
|
||
void complete() { | ||
Nan::HandleScope scope; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should enter an Isolate::Scope and Context::Scope here: v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope(isolate_); // Or Nan::HandleScope
v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate_, context_);
v8::Context::Scope context_scope(context);
// ... Store the context when the task is created because it can change. Using |
||
|
||
v8::TryCatch trycatch(isolate_); | ||
|
||
v8::Local<v8::Value> argv[2]; | ||
v8::Local<v8::Value> completion; | ||
|
||
complete_(rust_task_, result_, &completion); | ||
|
||
if (trycatch.HasCaught()) { | ||
argv[0] = trycatch.Exception(); | ||
argv[1] = v8::Undefined(isolate_); | ||
} else { | ||
argv[0] = v8::Null(isolate_); | ||
argv[1] = completion; | ||
} | ||
|
||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(isolate_, callback_); | ||
callback->Call(isolate_->GetCurrentContext(), v8::Null(isolate_), 2, argv); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If Whether it's a good idea to catch exceptions and continue is up for debate, though. |
||
} | ||
|
||
void *get_result() { | ||
return result_; | ||
} | ||
|
||
uv_work_t request_; | ||
|
||
private: | ||
v8::Isolate *isolate_; | ||
void *rust_task_; | ||
Neon_TaskPerformCallback perform_; | ||
Neon_TaskCompleteCallback complete_; | ||
void *result_; | ||
v8::Persistent<v8::Function> callback_; | ||
}; | ||
|
||
void execute_task(uv_work_t *request) { | ||
Task *task = static_cast<Task*>(request->data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, we use a container_of-like macro for that in libuv and node.js. Saves one level of pointer indirection. |
||
task->execute(); | ||
} | ||
|
||
void complete_task(uv_work_t *request) { | ||
Task *task = static_cast<Task*>(request->data); | ||
task->complete(); | ||
delete task; | ||
} | ||
|
||
void queue_task(Task *task) { | ||
uv_queue_work(uv_default_loop(), | ||
&task->request_, | ||
execute_task, | ||
(uv_after_work_cb)complete_task); | ||
} | ||
|
||
} | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//! Facilities for running background tasks in the libuv thread pool. | ||
|
||
use raw::Local; | ||
use std::os::raw::c_void; | ||
|
||
extern "C" { | ||
|
||
/// Schedules a background task. | ||
#[link_name = "Neon_Task_Schedule"] | ||
pub fn schedule(task: *mut c_void, | ||
perform: unsafe extern fn(*mut c_void) -> *mut c_void, | ||
complete: unsafe extern fn(*mut c_void, *mut c_void, &mut Local), | ||
callback: Local); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use std::marker::{Send, Sized}; | ||
use std::mem; | ||
use std::os::raw::c_void; | ||
|
||
use js::{Value, JsFunction}; | ||
use mem::Handle; | ||
use internal::mem::Managed; | ||
use internal::scope::{Scope, RootScope, RootScopeInternal}; | ||
use internal::vm::{JsResult, Isolate, IsolateInternal}; | ||
use neon_runtime; | ||
use neon_runtime::raw; | ||
|
||
pub trait Task: Send + Sized { | ||
type Output: Send; | ||
type Error: Send; | ||
type JsEvent: Value; | ||
|
||
fn perform(&self) -> Result<Self::Output, Self::Error>; | ||
|
||
fn complete<'a, T: Scope<'a>>(self, scope: &'a mut T, result: Result<Self::Output, Self::Error>) -> JsResult<Self::JsEvent>; | ||
|
||
fn schedule(self, callback: Handle<JsFunction>) { | ||
let boxed_self = Box::new(self); | ||
let self_raw = Box::into_raw(boxed_self); | ||
let callback_raw = callback.to_raw(); | ||
unsafe { | ||
neon_runtime::task::schedule(mem::transmute(self_raw), | ||
perform_task::<Self>, | ||
complete_task::<Self>, | ||
callback_raw); | ||
} | ||
} | ||
} | ||
|
||
unsafe extern "C" fn perform_task<T: Task>(task: *mut c_void) -> *mut c_void { | ||
let task: Box<T> = Box::from_raw(mem::transmute(task)); | ||
let result = task.perform(); | ||
Box::into_raw(task); | ||
mem::transmute(Box::into_raw(Box::new(result))) | ||
} | ||
|
||
unsafe extern "C" fn complete_task<T: Task>(task: *mut c_void, result: *mut c_void, out: &mut raw::Local) { | ||
let result: Result<T::Output, T::Error> = *Box::from_raw(mem::transmute(result)); | ||
let task: Box<T> = Box::from_raw(mem::transmute(task)); | ||
|
||
// The neon::Task::complete() method installs an outer v8::HandleScope | ||
// that is responsible for managing the out pointer, so it's safe to | ||
// create the RootScope here without creating a local v8::HandleScope. | ||
let mut scope = RootScope::new(Isolate::current()); | ||
if let Ok(result) = task.complete(&mut scope, result) { | ||
*out = result.to_raw(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not strictly necessary,
~Persistent()
does that by default.