From 19f182a39f90ec7ef16648cf6bc7dee213b74118 Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 25 Aug 2009 04:10:20 +0200 Subject: [PATCH] Experimental support for Promise.block() --- src/events.cc | 121 +++++++++++++++++++++-------- src/events.h | 31 +++++--- src/events.js | 8 -- src/file.cc | 34 ++++---- src/node.h | 6 +- test/mjsunit/test-promise-block.js | 46 +++++++++++ website/api.txt | 5 ++ 7 files changed, 180 insertions(+), 71 deletions(-) create mode 100644 test/mjsunit/test-promise-block.js diff --git a/src/events.cc b/src/events.cc index 8ef50c7def..d9daed37b5 100644 --- a/src/events.cc +++ b/src/events.cc @@ -1,4 +1,6 @@ #include "events.h" +#include +#include #include #include @@ -21,7 +23,7 @@ using namespace node; Persistent EventEmitter::constructor_template; void -EventEmitter::Initialize (v8::Handle target) +EventEmitter::Initialize (Handle target) { HandleScope scope; @@ -108,79 +110,136 @@ Promise::Initialize (v8::Handle target) { HandleScope scope; - Local t = FunctionTemplate::New(); + Local t = FunctionTemplate::New(New); constructor_template = Persistent::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 +Promise::New (const v8::Arguments& args) { HandleScope scope; - Local 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 argv[]) +Handle +Promise::Block (const Arguments& args) { - bool r = Emit("success", argc, argv); + HandleScope scope; + Promise *promise = ObjectWrap::Unwrap(args.Holder()); + promise->Block(); + return Undefined(); +} - Detach(); +v8::Handle +Promise::EmitSuccess (const v8::Arguments& args) +{ + HandleScope scope; + Promise *promise = ObjectWrap::Unwrap(args.Holder()); - return r; + int argc = 0; + Local emit_args; + if (args[0]->IsArray()) { + emit_args = Local::Cast(args[0]); + argc = emit_args->Length(); + } + Local 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 argv[]) +v8::Handle +Promise::EmitError (const v8::Arguments& args) { - bool r = Emit("error", argc, argv); + HandleScope scope; + Promise *promise = ObjectWrap::Unwrap(args.Holder()); - Detach(); + int argc = 0; + Local emit_args; + if (args[0]->IsArray()) { + emit_args = Local::Cast(args[0]); + argc = emit_args->Length(); + } + Local 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) { + if (blocking_) { + ev_unloop(EV_DEFAULT_ EVUNLOOP_ONE); + blocking_ = false; + } + if (ref_) { + ev_unref(EV_DEFAULT_UC); + } ObjectWrap::Detach(); - ev_unref(EV_DEFAULT_UC); } -EIOPromise* -EIOPromise::Create (void) +Promise* +Promise::Create (bool ref) { HandleScope scope; Local handle = Promise::constructor_template->GetFunction()->NewInstance(); - EIOPromise *promise = new EIOPromise(); - promise->Wrap(handle); + Promise *promise = ObjectWrap::Unwrap(handle); - promise->Attach(); + promise->ref_ = ref; + if (ref) ev_ref(EV_DEFAULT_UC); return promise; } + +bool +Promise::EmitSuccess (int argc, v8::Handle argv[]) +{ + bool r = Emit("success", argc, argv); + + Detach(); + + return r; +} + +bool +Promise::EmitError (int argc, v8::Handle argv[]) +{ + bool r = Emit("error", argc, argv); + + Detach(); + + return r; +} diff --git a/src/events.h b/src/events.h index f6e6588b7a..115717255b 100644 --- a/src/events.h +++ b/src/events.h @@ -23,28 +23,35 @@ class Promise : public EventEmitter { public: static void Initialize (v8::Handle target); static v8::Persistent constructor_template; + + static Promise* Create (bool ref = false); bool EmitSuccess (int argc, v8::Handle argv[]); bool EmitError (int argc, v8::Handle argv[]); + void Block (); - static Promise* Create (void); - v8::Handle Handle(void) { return handle_; } + v8::Handle Handle () + { + return handle_; + } protected: + static v8::Handle New (const v8::Arguments& args); + static v8::Handle Block (const v8::Arguments& args); + static v8::Handle EmitSuccess (const v8::Arguments& args); + static v8::Handle EmitError (const v8::Arguments& args); - Promise () : EventEmitter() { } -}; - -class EIOPromise : public Promise { - public: - static EIOPromise* Create (void); + virtual void Detach (void); - protected: - void Attach (void); - void Detach (void); + bool blocking_; + bool ref_; - EIOPromise () : Promise() { } + Promise () : EventEmitter() + { + blocking_ = false; + ref_ = false; + } }; } // namespace node diff --git a/src/events.js b/src/events.js index 80421a305d..ef4dc39337 100644 --- a/src/events.js +++ b/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 diff --git a/src/file.cc b/src/file.cc index cfabaa9278..c20b93d5b3 100644 --- a/src/file.cc +++ b/src/file.cc @@ -31,7 +31,7 @@ using namespace node; static int AfterClose (eio_req *req) { - EIOPromise *promise = reinterpret_cast(req->data); + Promise *promise = reinterpret_cast(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(req->data); + Promise *promise = reinterpret_cast(req->data); if (req->result == 0) { promise->EmitSuccess(0, NULL); } else { @@ -74,7 +74,7 @@ static Handle 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 Rename (const Arguments& args) static int AfterUnlink (eio_req *req) { - EIOPromise *promise = reinterpret_cast(req->data); + Promise *promise = reinterpret_cast(req->data); if (req->result == 0) { promise->EmitSuccess(0, NULL); } else { @@ -98,7 +98,7 @@ static Handle 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 Unlink (const Arguments& args) static int AfterRMDir (eio_req *req) { - EIOPromise *promise = reinterpret_cast(req->data); + Promise *promise = reinterpret_cast(req->data); if (req->result == 0) { promise->EmitSuccess(0, NULL); } else { @@ -121,7 +121,7 @@ static Handle 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 RMDir (const Arguments& args) static int AfterOpen (eio_req *req) { - EIOPromise *promise = reinterpret_cast(req->data); + Promise *promise = reinterpret_cast(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(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(req->data); + Promise *promise = reinterpret_cast(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(req->data); + Promise *promise = reinterpret_cast(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(req->data); + Promise *promise = reinterpret_cast(req->data); if (req->result < 0) { promise->EmitError(0, NULL); @@ -326,7 +326,7 @@ Read (const Arguments& args) encoding = static_cast(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(req->data); + Promise *promise = reinterpret_cast(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); diff --git a/src/node.h b/src/node.h index c2e466610d..e91241a89d 100644 --- a/src/node.h +++ b/src/node.h @@ -22,9 +22,9 @@ namespace node { #define NODE_SET_PROTOTYPE_METHOD(templ, name, callback) \ do { \ - Local __callback##_SIG = Signature::New(templ); \ - Local __callback##_TEM = \ - FunctionTemplate::New(callback, Handle() , __callback##_SIG ); \ + v8::Local __callback##_SIG = v8::Signature::New(templ); \ + v8::Local __callback##_TEM = \ + FunctionTemplate::New(callback, v8::Handle() , __callback##_SIG ); \ templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name), \ __callback##_TEM); \ } while(0) diff --git a/test/mjsunit/test-promise-block.js b/test/mjsunit/test-promise-block.js new file mode 100644 index 0000000000..8773f3ab2c --- /dev/null +++ b/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); +} diff --git a/website/api.txt b/website/api.txt index 8ee96739ce..4c6d0f9c59 100644 --- a/website/api.txt +++ b/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