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

Commit

Permalink
add $sendSync and $recvSync
Browse files Browse the repository at this point in the history
Fixes #24
Fixes #25
  • Loading branch information
emicklei authored and ry committed Dec 7, 2015
1 parent c37b852 commit 24a262f
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 22 deletions.
96 changes: 91 additions & 5 deletions binding.cc
Expand Up @@ -13,10 +13,12 @@ struct worker_s {
int x;
void* data;
worker_recv_cb cb;
worker_recvSync_cb req_cb;
Isolate* isolate;
std::string last_exception;
Persistent<Function> recv;
Persistent<Context> context;
Persistent<Function> recv_sync_handler;
};

// Extracts a C string from a V8 Utf8Value.
Expand Down Expand Up @@ -44,7 +46,7 @@ std::string ExceptionString(Isolate* isolate, TryCatch* try_catch) {
HandleScope handle_scope(isolate);
String::Utf8Value exception(try_catch->Exception());
const char* exception_string = ToCString(exception);

Handle<Message> message = try_catch->Message();

if (message.IsEmpty()) {
Expand Down Expand Up @@ -102,6 +104,9 @@ void go_recv_cb(const char* msg, void* data) {
recvCb((char*)msg, data);
}

const char* go_recvSync_cb(const char* msg, void* data) {
return recvSyncCb((char*)msg, data);
}

const char* worker_version() {
return V8::GetVersion();
Expand Down Expand Up @@ -165,7 +170,7 @@ void Print(const FunctionCallbackInfo<Value>& args) {
// sets the recv callback.
void Recv(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
worker* w = (worker*)isolate->GetData(0);
worker* w = (worker*)isolate->GetData(0);
assert(w->isolate == isolate);

HandleScope handle_scope(isolate);
Expand All @@ -180,6 +185,23 @@ void Recv(const FunctionCallbackInfo<Value>& args) {
w->recv.Reset(isolate, func);
}

void RecvSync(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
worker* w = (worker*)isolate->GetData(0);
assert(w->isolate == isolate);

HandleScope handle_scope(isolate);

Local<Context> context = Local<Context>::New(w->isolate, w->context);
Context::Scope context_scope(context);

Local<Value> v = args[0];
assert(v->IsFunction());
Local<Function> func = Local<Function>::Cast(v);

w->recv_sync_handler.Reset(isolate, func);
}

// Called from javascript. Must route message to golang.
void Send(const FunctionCallbackInfo<Value>& args) {
std::string msg;
Expand All @@ -202,10 +224,37 @@ void Send(const FunctionCallbackInfo<Value>& args) {
msg = ToCString(str);
}

// XXX should we use Unlocker?
// XXX should we use Unlocker?
w->cb(msg.c_str(), w->data);
}

// Called from javascript using $request.
// Must route message (string) to golang and send back message (string) as return value.
void SendSync(const FunctionCallbackInfo<Value>& args) {
std::string msg;
worker* w = NULL;
{
Isolate* isolate = args.GetIsolate();
w = static_cast<worker*>(isolate->GetData(0));
assert(w->isolate == isolate);

Locker locker(w->isolate);
HandleScope handle_scope(isolate);

Local<Context> context = Local<Context>::New(w->isolate, w->context);
Context::Scope context_scope(context);

Local<Value> v = args[0];
assert(v->IsString());

String::Utf8Value str(v);
msg = ToCString(str);
}
const char* returnMsg = w->req_cb(msg.c_str(), w->data);
Local<String> returnV = String::NewFromUtf8(w->isolate, returnMsg);
args.GetReturnValue().Set(returnV);
}

// Called from golang. Must route message to javascript lang.
// non-zero return value indicates error. check worker_last_exception().
int worker_send(worker* w, const char* msg) {
Expand Down Expand Up @@ -239,6 +288,36 @@ int worker_send(worker* w, const char* msg) {
return 0;
}

// Called from golang. Must route message to javascript lang.
// It will call the $recv_sync_handler callback function and return its string value.
const char* worker_sendSync(worker* w, const char* msg) {
std::string out;
Locker locker(w->isolate);
Isolate::Scope isolate_scope(w->isolate);
HandleScope handle_scope(w->isolate);

Local<Context> context = Local<Context>::New(w->isolate, w->context);
Context::Scope context_scope(context);

Local<Function> recv_sync_handler = Local<Function>::New(w->isolate, w->recv_sync_handler);
if (recv_sync_handler.IsEmpty()) {
out.append("err: $recvSync not called");
return out.c_str();
}

Local<Value> args[1];
args[0] = String::NewFromUtf8(w->isolate, msg);
Local<Value> response_value = recv_sync_handler->Call(context->Global(), 1, args);

if (response_value->IsString()) {
String::Utf8Value response(response_value->ToString());
out.append(*response);
} else {
out.append("err: non-string return value");
}
return out.c_str();
}

static ArrayBufferAllocator array_buffer_allocator;

void v8_init() {
Expand All @@ -250,18 +329,19 @@ void v8_init() {
V8::SetArrayBufferAllocator(&array_buffer_allocator);
}

worker* worker_new(worker_recv_cb cb, void* data) {
worker* worker_new(worker_recv_cb cb, worker_recvSync_cb recvSync_cb, void* data) {
Isolate* isolate = Isolate::New();
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);

worker* w = new(worker);
w->isolate = isolate;
w->isolate->SetCaptureStackTraceForUncaughtExceptions(true);
w->isolate->SetCaptureStackTraceForUncaughtExceptions(true);
w->isolate->SetData(0, w);
w->data = data;
w->cb = cb;
w->req_cb = recvSync_cb;

Local<ObjectTemplate> global = ObjectTemplate::New(w->isolate);

Expand All @@ -274,6 +354,12 @@ worker* worker_new(worker_recv_cb cb, void* data) {
global->Set(String::NewFromUtf8(w->isolate, "$send"),
FunctionTemplate::New(w->isolate, Send));

global->Set(String::NewFromUtf8(w->isolate, "$sendSync"),
FunctionTemplate::New(w->isolate, SendSync));

global->Set(String::NewFromUtf8(w->isolate, "$recvSync"),
FunctionTemplate::New(w->isolate, RecvSync));

Local<Context> context = Context::New(w->isolate, NULL, global);
w->context.Reset(w->isolate, context);
//context->Enter();
Expand Down
5 changes: 4 additions & 1 deletion binding.h
Expand Up @@ -4,16 +4,18 @@ extern "C" {


void go_recv_cb(const char* msg, void* data);
const char* go_recvSync_cb(const char* msg, void* data);

struct worker_s;
typedef struct worker_s worker;
typedef void (*worker_recv_cb)(const char* msg, void* data);
typedef const char* (*worker_recvSync_cb)(const char* msg, void* data);

const char* worker_version();

void v8_init();

worker* worker_new(worker_recv_cb cb, void* data);
worker* worker_new(worker_recv_cb cb, worker_recvSync_cb recvSync_cb, void* data);

// returns nonzero on error
// get error from worker_last_exception
Expand All @@ -22,6 +24,7 @@ int worker_load(worker* w, char* name_s, char* source_s);
const char* worker_last_exception(worker* w);

int worker_send(worker* w, const char* msg);
const char* worker_sendSync(worker* w, const char* msg);

#ifdef __cplusplus
} // extern "C"
Expand Down
38 changes: 33 additions & 5 deletions worker.go
Expand Up @@ -8,19 +8,27 @@ package v8worker
*/
import "C"
import "errors"

import "unsafe"
import "sync"

// To receive messages from javascript...
type ReceiveMessageCallback func(msg string)

// To send a message from javascript and synchronously return a string.
type ReceiveSyncMessageCallback func(msg string) string

// DiscardSendSync can be used in the worker constructor when you don't use the builtin $sendSync.
func DiscardSendSync(msg string) string { return "" }

// Don't init V8 more than once.
var initV8Once sync.Once

// This is a golang wrapper around a single V8 Isolate.
type Worker struct {
cWorker *C.worker
cb ReceiveMessageCallback
cWorker *C.worker
cb ReceiveMessageCallback
recvSync_cb ReceiveSyncMessageCallback
}

// Return the V8 version E.G. "4.3.59"
Expand All @@ -35,20 +43,30 @@ func recvCb(msg_s *C.char, ptr unsafe.Pointer) {
worker.cb(msg)
}

//export recvSyncCb
func recvSyncCb(msg_s *C.char, ptr unsafe.Pointer) *C.char {
msg := C.GoString(msg_s)
worker := (*Worker)(ptr)
return_s := C.CString(worker.recvSync_cb(msg))
return return_s
}

// Creates a new worker, which corresponds to a V8 isolate. A single threaded
// standalone execution context.
func New(cb ReceiveMessageCallback) *Worker {
func New(cb ReceiveMessageCallback, recvSync_cb ReceiveSyncMessageCallback) *Worker {
worker := &Worker{
cb: cb,
cb: cb,
recvSync_cb: recvSync_cb,
}

initV8Once.Do(func() {
C.v8_init()
})

callback := C.worker_recv_cb(C.go_recv_cb)
receiveSync_callback := C.worker_recvSync_cb(C.go_recvSync_cb)

worker.cWorker = C.worker_new(callback, unsafe.Pointer(worker))
worker.cWorker = C.worker_new(callback, receiveSync_callback, unsafe.Pointer(worker))
return worker
}

Expand Down Expand Up @@ -81,3 +99,13 @@ func (w *Worker) Send(msg string) error {

return nil
}

// SendSync sends a message to a worker. The $recvSync callback in js will be called.
// That callback will return a string which is passed to golang and used as the return value of SendSync.
func (w *Worker) SendSync(msg string) string {
msg_s := C.CString(string(msg))
defer C.free(unsafe.Pointer(msg_s))

svalue := C.worker_sendSync(w.cWorker, msg_s)
return C.GoString(svalue)
}

0 comments on commit 24a262f

Please sign in to comment.