Browse Source

Experimental support for Promise.block()

v0.7.4-release
Ryan 15 years ago
parent
commit
19f182a39f
  1. 121
      src/events.cc
  2. 31
      src/events.h
  3. 8
      src/events.js
  4. 34
      src/file.cc
  5. 6
      src/node.h
  6. 46
      test/mjsunit/test-promise-block.js
  7. 5
      website/api.txt

121
src/events.cc

@ -1,4 +1,6 @@
#include "events.h"
#include <ev.h>
#include <v8.h>
#include <assert.h>
#include <stdlib.h>
@ -21,7 +23,7 @@ using namespace node;
Persistent<FunctionTemplate> EventEmitter::constructor_template;
void
EventEmitter::Initialize (v8::Handle<v8::Object> target)
EventEmitter::Initialize (Handle<Object> target)
{
HandleScope scope;
@ -108,79 +110,136 @@ Promise::Initialize (v8::Handle<v8::Object> target)
{
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New();
Local<FunctionTemplate> t = FunctionTemplate::New(New);
constructor_template = Persistent<FunctionTemplate>::New(t);
constructor_template->Inherit(EventEmitter::constructor_template);
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
// All prototype methods are defined in events.js
NODE_SET_PROTOTYPE_METHOD(constructor_template, "block", Block);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "emitSuccess", EmitSuccess);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "emitError", EmitError);
target->Set(String::NewSymbol("Promise"),
constructor_template->GetFunction());
}
Promise*
Promise::Create (void)
v8::Handle<v8::Value>
Promise::New (const v8::Arguments& args)
{
HandleScope scope;
Local<Object> handle =
Promise::constructor_template->GetFunction()->NewInstance();
Promise *promise = new Promise();
promise->Wrap(handle);
promise->Wrap(args.This());
promise->Attach();
return promise;
return args.This();
}
bool
Promise::EmitSuccess (int argc, v8::Handle<v8::Value> argv[])
Handle<Value>
Promise::Block (const Arguments& args)
{
bool r = Emit("success", argc, argv);
HandleScope scope;
Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
promise->Block();
return Undefined();
}
Detach();
v8::Handle<v8::Value>
Promise::EmitSuccess (const v8::Arguments& args)
{
HandleScope scope;
Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
return r;
int argc = 0;
Local<Array> emit_args;
if (args[0]->IsArray()) {
emit_args = Local<Array>::Cast(args[0]);
argc = emit_args->Length();
}
Local<Value> argv[argc];
for (int i = 0; i < argc; i++) {
argv[i] = emit_args->Get(Integer::New(i));
}
bool r = promise->EmitSuccess(argc, argv);
return r ? True() : False();
}
bool
Promise::EmitError (int argc, v8::Handle<v8::Value> argv[])
v8::Handle<v8::Value>
Promise::EmitError (const v8::Arguments& args)
{
bool r = Emit("error", argc, argv);
HandleScope scope;
Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
Detach();
int argc = 0;
Local<Array> emit_args;
if (args[0]->IsArray()) {
emit_args = Local<Array>::Cast(args[0]);
argc = emit_args->Length();
}
Local<Value> argv[argc];
for (int i = 0; i < argc; i++) {
argv[i] = emit_args->Get(Integer::New(i));
}
return r;
bool r = promise->EmitError(argc, argv);
return r ? True() : False();
}
void
EIOPromise::Attach (void)
Promise::Block (void)
{
ObjectWrap::Attach();
ev_ref(EV_DEFAULT_UC);
blocking_ = true;
ev_loop(EV_DEFAULT_UC_ 0);
}
void
EIOPromise::Detach (void)
Promise::Detach (void)
{
ObjectWrap::Detach();
if (blocking_) {
ev_unloop(EV_DEFAULT_ EVUNLOOP_ONE);
blocking_ = false;
}
if (ref_) {
ev_unref(EV_DEFAULT_UC);
}
ObjectWrap::Detach();
}
EIOPromise*
EIOPromise::Create (void)
Promise*
Promise::Create (bool ref)
{
HandleScope scope;
Local<Object> handle =
Promise::constructor_template->GetFunction()->NewInstance();
EIOPromise *promise = new EIOPromise();
promise->Wrap(handle);
Promise *promise = ObjectWrap::Unwrap<Promise>(handle);
promise->Attach();
promise->ref_ = ref;
if (ref) ev_ref(EV_DEFAULT_UC);
return promise;
}
bool
Promise::EmitSuccess (int argc, v8::Handle<v8::Value> argv[])
{
bool r = Emit("success", argc, argv);
Detach();
return r;
}
bool
Promise::EmitError (int argc, v8::Handle<v8::Value> argv[])
{
bool r = Emit("error", argc, argv);
Detach();
return r;
}

31
src/events.h

@ -24,27 +24,34 @@ class Promise : public EventEmitter {
static void Initialize (v8::Handle<v8::Object> target);
static v8::Persistent<v8::FunctionTemplate> constructor_template;
static Promise* Create (bool ref = false);
bool EmitSuccess (int argc, v8::Handle<v8::Value> argv[]);
bool EmitError (int argc, v8::Handle<v8::Value> argv[]);
void Block ();
static Promise* Create (void);
v8::Handle<v8::Object> Handle(void) { return handle_; }
v8::Handle<v8::Object> Handle ()
{
return handle_;
}
protected:
static v8::Handle<v8::Value> New (const v8::Arguments& args);
static v8::Handle<v8::Value> Block (const v8::Arguments& args);
static v8::Handle<v8::Value> EmitSuccess (const v8::Arguments& args);
static v8::Handle<v8::Value> EmitError (const v8::Arguments& args);
Promise () : EventEmitter() { }
};
virtual void Detach (void);
class EIOPromise : public Promise {
public:
static EIOPromise* Create (void);
protected:
void Attach (void);
void Detach (void);
bool blocking_;
bool ref_;
EIOPromise () : Promise() { }
Promise () : EventEmitter()
{
blocking_ = false;
ref_ = false;
}
};
} // namespace node

8
src/events.js

@ -30,12 +30,4 @@ promise.addErrback = function (listener) {
return this;
};
promise.emitSuccess = function (args) {
this.emit("success", args);
};
promise.emitError = function (args) {
this.emit("error", args);
};
})(); // end anonymous namespace

34
src/file.cc

@ -31,7 +31,7 @@ using namespace node;
static int
AfterClose (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result == 0) {
promise->EmitSuccess(0, NULL);
} else {
@ -48,7 +48,7 @@ Close (const Arguments& args)
HandleScope scope;
int fd = args[0]->Int32Value();
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
eio_close(fd, EIO_PRI_DEFAULT, AfterClose, promise);
return scope.Close(promise->Handle());
@ -57,7 +57,7 @@ Close (const Arguments& args)
static int
AfterRename (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result == 0) {
promise->EmitSuccess(0, NULL);
} else {
@ -74,7 +74,7 @@ static Handle<Value> Rename (const Arguments& args)
String::Utf8Value path(args[0]->ToString());
String::Utf8Value new_path(args[1]->ToString());
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, promise);
return scope.Close(promise->Handle());
@ -83,7 +83,7 @@ static Handle<Value> Rename (const Arguments& args)
static int
AfterUnlink (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result == 0) {
promise->EmitSuccess(0, NULL);
} else {
@ -98,7 +98,7 @@ static Handle<Value> Unlink (const Arguments& args)
return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
String::Utf8Value path(args[0]->ToString());
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
eio_unlink(*path, EIO_PRI_DEFAULT, AfterUnlink, promise);
return scope.Close(promise->Handle());
}
@ -106,7 +106,7 @@ static Handle<Value> Unlink (const Arguments& args)
static int
AfterRMDir (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result == 0) {
promise->EmitSuccess(0, NULL);
} else {
@ -121,7 +121,7 @@ static Handle<Value> RMDir (const Arguments& args)
return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
String::Utf8Value path(args[0]->ToString());
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
eio_rmdir(*path, EIO_PRI_DEFAULT, AfterRMDir, promise);
return scope.Close(promise->Handle());
}
@ -129,7 +129,7 @@ static Handle<Value> RMDir (const Arguments& args)
static int
AfterOpen (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
@ -156,7 +156,7 @@ Open (const Arguments& args)
int flags = args[1]->Int32Value();
mode_t mode = static_cast<mode_t>(args[2]->Int32Value());
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, promise);
return scope.Close(promise->Handle());
@ -165,7 +165,7 @@ Open (const Arguments& args)
static int
AfterWrite (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
@ -231,7 +231,7 @@ Write (const Arguments& args)
return ThrowException(BAD_ARGUMENTS);
}
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, promise);
return scope.Close(promise->Handle());
}
@ -239,7 +239,7 @@ Write (const Arguments& args)
static int
AfterUtf8Read (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
@ -267,7 +267,7 @@ AfterUtf8Read (eio_req *req)
static int
AfterRawRead(eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
@ -326,7 +326,7 @@ Read (const Arguments& args)
encoding = static_cast<enum encoding>(args[3]->Int32Value());
}
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
// NOTE: 2nd param: NULL pointer tells eio to allocate it itself
eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT,
@ -338,7 +338,7 @@ Read (const Arguments& args)
static int
AfterStat (eio_req *req)
{
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
@ -394,7 +394,7 @@ Stat (const Arguments& args)
String::Utf8Value path(args[0]->ToString());
EIOPromise *promise = EIOPromise::Create();
Promise *promise = Promise::Create(true);
eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, promise);

6
src/node.h

@ -22,9 +22,9 @@ namespace node {
#define NODE_SET_PROTOTYPE_METHOD(templ, name, callback) \
do { \
Local<Signature> __callback##_SIG = Signature::New(templ); \
Local<FunctionTemplate> __callback##_TEM = \
FunctionTemplate::New(callback, Handle<Value>() , __callback##_SIG ); \
v8::Local<v8::Signature> __callback##_SIG = v8::Signature::New(templ); \
v8::Local<v8::FunctionTemplate> __callback##_TEM = \
FunctionTemplate::New(callback, v8::Handle<v8::Value>() , __callback##_SIG ); \
templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name), \
__callback##_TEM); \
} while(0)

46
test/mjsunit/test-promise-block.js

@ -0,0 +1,46 @@
include("mjsunit.js");
var p1_done = false;
var p1 = new node.Promise();
p1.addCallback(function () {
p1_done = true;
});
var p2_done = false;
var p2 = new node.Promise();
p2.addCallback(function () {
p2_done = true;
setTimeout(function () {
p1.emitSuccess();
}, 100);
});
var p3_done = false;
var p3 = new node.Promise();
p3.addCallback(function () {
p3_done = true;
});
function onLoad () {
p2.emitSuccess();
assertFalse(p1_done);
assertTrue(p2_done);
assertFalse(p3_done);
p1.block()
assertTrue(p1_done);
assertTrue(p2_done);
assertFalse(p3_done);
p3.emitSuccess();
}
function onExit() {
assertTrue(p1_done);
assertTrue(p2_done);
assertTrue(p3_done);
}

5
website/api.txt

@ -189,6 +189,11 @@ Adds a listener for the +"success"+ event. Returns the same promise object.
+promise.addErrback(listener)+ ::
Adds a listener for the +"error"+ event. Returns the same promise object.
+promise.block()+ ::
Blocks futher execution until the promise emits a success or error event.
Events setup before the call to +promise.block()+ was made may still be
emitted and executed while +promise.block()+ is blocking.
=== Standard I/O

Loading…
Cancel
Save