diff --git a/src/events.cc b/src/events.cc index c1af9b62fc..8d00d24af2 100644 --- a/src/events.cc +++ b/src/events.cc @@ -220,29 +220,9 @@ Promise::Detach (void) coroutine_top->Destack(); } - if (ref_) { - ev_unref(EV_DEFAULT_UC); - } - ObjectWrap::Detach(); } -Promise* -Promise::Create (bool ref) -{ - HandleScope scope; - - Local handle = - Promise::constructor_template->GetFunction()->NewInstance(); - - Promise *promise = ObjectWrap::Unwrap(handle); - - promise->ref_ = ref; - if (ref) ev_ref(EV_DEFAULT_UC); - - return promise; -} - bool Promise::EmitSuccess (int argc, v8::Handle argv[]) { @@ -262,3 +242,18 @@ Promise::EmitError (int argc, v8::Handle argv[]) return r; } + +Promise* +Promise::Create (void) +{ + HandleScope scope; + + Local handle = + Promise::constructor_template->GetFunction()->NewInstance(); + + Promise *promise = new Promise(); + promise->Wrap(handle); + promise->Attach(); + + return promise; +} diff --git a/src/events.h b/src/events.h index 91a8f804ec..0e44c8f69d 100644 --- a/src/events.h +++ b/src/events.h @@ -23,14 +23,12 @@ class Promise : public EventEmitter { public: static void Initialize (v8::Handle target); static v8::Persistent constructor_template; - - static Promise* Create (bool ref = false); + static Promise* Create (void); bool EmitSuccess (int argc, v8::Handle argv[]); bool EmitError (int argc, v8::Handle argv[]); void Block (); - v8::Handle Handle () { return handle_; @@ -45,7 +43,6 @@ class Promise : public EventEmitter { virtual void Detach (void); bool blocking_; - bool ref_; Promise *prev_; /* for the prev in the Poor Man's coroutine stack */ void Destack (); @@ -53,10 +50,8 @@ class Promise : public EventEmitter { Promise () : EventEmitter() { blocking_ = false; - ref_ = false; prev_ = NULL; } }; - } // namespace node #endif diff --git a/src/file.cc b/src/file.cc index 6528faee61..a79e59d699 100644 --- a/src/file.cc +++ b/src/file.cc @@ -28,184 +28,242 @@ using namespace node; #define CTIME_SYMBOL String::NewSymbol("ctime") #define BAD_ARGUMENTS Exception::TypeError(String::New("Bad argument")) -static int -AfterClose (eio_req *req) +void +EIOPromise::Attach (void) { - Promise *promise = reinterpret_cast(req->data); - if (req->result == 0) { - promise->EmitSuccess(0, NULL); - } else { - promise->EmitError(0, NULL); - } - return 0; + ev_ref(EV_DEFAULT_UC); + Promise::Attach(); } -static Handle -Close (const Arguments& args) +void +EIOPromise::Detach (void) +{ + Promise::Detach(); + ev_unref(EV_DEFAULT_UC); +} + +EIOPromise* +EIOPromise::Create (void) { - if (args.Length() < 1 || !args[0]->IsInt32()) - return ThrowException(BAD_ARGUMENTS); HandleScope scope; - int fd = args[0]->Int32Value(); - Promise *promise = Promise::Create(true); + Local handle = + Promise::constructor_template->GetFunction()->NewInstance(); - eio_close(fd, EIO_PRI_DEFAULT, AfterClose, promise); - return scope.Close(promise->Handle()); + EIOPromise *promise = new EIOPromise(); + promise->Wrap(handle); + + promise->Attach(); + + return promise; } -static int -AfterRename (eio_req *req) +#define NODE_UNIXTIME(t) v8::Date::New(1000*static_cast(t)) +int +EIOPromise::After (eio_req *req) { - Promise *promise = reinterpret_cast(req->data); - if (req->result == 0) { - promise->EmitSuccess(0, NULL); - } else { - promise->EmitError(0, NULL); + HandleScope scope; + + EIOPromise *promise = reinterpret_cast(req->data); + assert(req == promise->req_); + + if (req->errorno != 0) { + Local exception = Exception::Error( + String::NewSymbol(strerror(req->errorno))); + promise->EmitError(1, &exception); + return 0; + } + + int argc = 0; + Local argv[5]; // 5 is the maximum number of args + + switch (req->type) { + case EIO_CLOSE: + case EIO_RENAME: + case EIO_UNLINK: + case EIO_RMDIR: + argc = 0; + break; + + case EIO_OPEN: + case EIO_WRITE: + argc = 1; + argv[0] = Integer::New(req->result); + break; + + case EIO_STAT: + { + Local stats = Object::New(); + struct stat *s = reinterpret_cast(req->ptr2); + stats->Set(DEV_SYMBOL, Integer::New(s->st_dev)); /* ID of device containing file */ + stats->Set(INO_SYMBOL, Integer::New(s->st_ino)); /* inode number */ + stats->Set(MODE_SYMBOL, Integer::New(s->st_mode)); /* protection */ + stats->Set(NLINK_SYMBOL, Integer::New(s->st_nlink)); /* number of hard links */ + stats->Set(UID_SYMBOL, Integer::New(s->st_uid)); /* user ID of owner */ + stats->Set(GID_SYMBOL, Integer::New(s->st_gid)); /* group ID of owner */ + stats->Set(RDEV_SYMBOL, Integer::New(s->st_rdev)); /* device ID (if special file) */ + stats->Set(SIZE_SYMBOL, Integer::New(s->st_size)); /* total size, in bytes */ + stats->Set(BLKSIZE_SYMBOL, Integer::New(s->st_blksize)); /* blocksize for filesystem I/O */ + stats->Set(BLOCKS_SYMBOL, Integer::New(s->st_blocks)); /* number of blocks allocated */ + stats->Set(ATIME_SYMBOL, NODE_UNIXTIME(s->st_atime)); /* time of last access */ + stats->Set(MTIME_SYMBOL, NODE_UNIXTIME(s->st_mtime)); /* time of last modification */ + stats->Set(CTIME_SYMBOL, NODE_UNIXTIME(s->st_ctime)); /* time of last status change */ + argc = 1; + argv[0] = stats; + break; + } + + case EIO_READ: + { + argc = 2; + // FIXME the following is really ugly! + if (promise->encoding_ == RAW) { + if (req->result == 0) { + argv[0] = Local::New(Null()); + argv[1] = Integer::New(0); + } else { + char *buf = reinterpret_cast(req->ptr2); + size_t len = req->result; + Local array = Array::New(len); + for (unsigned int i = 0; i < len; i++) { + unsigned char val = reinterpret_cast(buf)[i]; + array->Set(Integer::New(i), Integer::New(val)); + } + argv[0] = array; + argv[1] = Integer::New(req->result); + } + } else { + // UTF8 + if (req->result == 0) { + // eof + argv[0] = Local::New(Null()); + argv[1] = Integer::New(0); + } else { + char *buf = reinterpret_cast(req->ptr2); + argv[0] = String::New(buf, req->result); + argv[1] = Integer::New(req->result); + } + } + break; + } + + default: + assert(0 && "Unhandled eio response"); } + + promise->EmitSuccess(argc, argv); + return 0; } -static Handle Rename (const Arguments& args) +static Handle +Close (const Arguments& args) { - if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) - return ThrowException(BAD_ARGUMENTS); HandleScope scope; - String::Utf8Value path(args[0]->ToString()); - String::Utf8Value new_path(args[1]->ToString()); - Promise *promise = Promise::Create(true); + if (args.Length() < 1 || !args[0]->IsInt32()) { + return ThrowException(BAD_ARGUMENTS); + } - eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, promise); - return scope.Close(promise->Handle()); + int fd = args[0]->Int32Value(); + + return scope.Close(EIOPromise::Close(fd)); } -static int -AfterUnlink (eio_req *req) +static Handle +Stat (const Arguments& args) { - Promise *promise = reinterpret_cast(req->data); - if (req->result == 0) { - promise->EmitSuccess(0, NULL); - } else { - promise->EmitError(0, NULL); + HandleScope scope; + + if (args.Length() < 1 || !args[0]->IsString()) { + return ThrowException(BAD_ARGUMENTS); } - return 0; + + String::Utf8Value path(args[0]->ToString()); + + return scope.Close(EIOPromise::Stat(*path)); } -static Handle Unlink (const Arguments& args) +static Handle +Rename (const Arguments& args) { - if (args.Length() < 1 || !args[0]->IsString()) - return ThrowException(BAD_ARGUMENTS); HandleScope scope; + + if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { + return ThrowException(BAD_ARGUMENTS); + } + String::Utf8Value path(args[0]->ToString()); - Promise *promise = Promise::Create(true); - eio_unlink(*path, EIO_PRI_DEFAULT, AfterUnlink, promise); + String::Utf8Value new_path(args[1]->ToString()); + + return scope.Close(EIOPromise::Rename(*path, *new_path)); + Promise *promise = EIOPromise::Create(); + return scope.Close(promise->Handle()); } -static int -AfterRMDir (eio_req *req) +static Handle Unlink (const Arguments& args) { - Promise *promise = reinterpret_cast(req->data); - if (req->result == 0) { - promise->EmitSuccess(0, NULL); - } else { - promise->EmitError(0, NULL); - } - return 0; -} + HandleScope scope; -static Handle RMDir (const Arguments& args) -{ - if (args.Length() < 1 || !args[0]->IsString()) + if (args.Length() < 1 || !args[0]->IsString()) { return ThrowException(BAD_ARGUMENTS); - HandleScope scope; + } + String::Utf8Value path(args[0]->ToString()); - Promise *promise = Promise::Create(true); - eio_rmdir(*path, EIO_PRI_DEFAULT, AfterRMDir, promise); - return scope.Close(promise->Handle()); + return scope.Close(EIOPromise::Unlink(*path)); } -static int -AfterOpen (eio_req *req) +static Handle +RMDir (const Arguments& args) { - Promise *promise = reinterpret_cast(req->data); + HandleScope scope; - if (req->result < 0) { - promise->EmitError(0, NULL); - return 0; + if (args.Length() < 1 || !args[0]->IsString()) { + return ThrowException(BAD_ARGUMENTS); } - HandleScope scope; - Local argv[1] = { Integer::New(req->result) }; - promise->EmitSuccess(1, argv); - return 0; + String::Utf8Value path(args[0]->ToString()); + return scope.Close(EIOPromise::RMDir(*path)); } static Handle Open (const Arguments& args) { + HandleScope scope; + if ( args.Length() < 3 || !args[0]->IsString() || !args[1]->IsInt32() || !args[2]->IsInt32() ) return ThrowException(BAD_ARGUMENTS); - HandleScope scope; String::Utf8Value path(args[0]->ToString()); int flags = args[1]->Int32Value(); mode_t mode = static_cast(args[2]->Int32Value()); - Promise *promise = Promise::Create(true); - - eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, promise); - return scope.Close(promise->Handle()); -} - -static int -AfterWrite (eio_req *req) -{ - Promise *promise = reinterpret_cast(req->data); - - if (req->result < 0) { - promise->EmitError(0, NULL); - return 0; - } - - HandleScope scope; - - free(req->ptr2); - - ssize_t written = req->result; - Local argv[1]; - argv[0] = written >= 0 ? Integer::New(written) : Integer::New(0); - - promise->EmitSuccess(1, argv); - return 0; + return scope.Close(EIOPromise::Open(*path, flags, mode)); } - -/* node.fs.write(fd, data, position, callback) +/* node.fs.write(fd, data, position=null) * Wrapper for write(2). * * 0 fd integer. file descriptor * 1 data the data to write (string = utf8, array = raw) * 2 position if integer, position to write at in the file. * if null, write from the current position - * - * 3 callback(errorno, written) */ static Handle Write (const Arguments& args) { - if ( args.Length() < 3 - || !args[0]->IsInt32() - ) return ThrowException(BAD_ARGUMENTS); - HandleScope scope; + if (args.Length() < 2 || !args[0]->IsInt32()) { + return ThrowException(BAD_ARGUMENTS); + } + int fd = args[0]->Int32Value(); - off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; + off_t offset = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; char *buf = NULL; size_t len = 0; @@ -231,92 +289,27 @@ Write (const Arguments& args) return ThrowException(BAD_ARGUMENTS); } - Promise *promise = Promise::Create(true); - eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, promise); - return scope.Close(promise->Handle()); -} - -static int -AfterUtf8Read (eio_req *req) -{ - Promise *promise = reinterpret_cast(req->data); - - if (req->result < 0) { - promise->EmitError(0, NULL); - return 0; - } - - HandleScope scope; - - Local argv[2]; - - if (req->result == 0) { - // eof - argv[0] = Local::New(Null()); - argv[1] = Integer::New(0); - } else { - char *buf = reinterpret_cast(req->ptr2); - argv[0] = String::New(buf, req->result); - argv[1] = Integer::New(req->result); - } - - promise->EmitSuccess(2, argv); - return 0; -} - -static int -AfterRawRead(eio_req *req) -{ - Promise *promise = reinterpret_cast(req->data); - - if (req->result < 0) { - promise->EmitError(0, NULL); - return 0; - } - - HandleScope scope; - Local argv[2]; - - if (req->result == 0) { - argv[0] = Local::New(Null()); - argv[1] = Integer::New(0); - } else { - char *buf = reinterpret_cast(req->ptr2); - size_t len = req->result; - Local array = Array::New(len); - for (unsigned int i = 0; i < len; i++) { - unsigned char val = reinterpret_cast(buf)[i]; - array->Set(Integer::New(i), Integer::New(val)); - } - argv[0] = array; - argv[1] = Integer::New(req->result); - } - - promise->EmitSuccess(2, argv); - return 0; + return scope.Close(EIOPromise::Write(fd, buf, len, offset)); } -/* node.fs.read(fd, length, position, encoding, callback) +/* node.fs.read(fd, length, position, encoding) * Wrapper for read(2). * * 0 fd integer. file descriptor * 1 length integer. length to read * 2 position if integer, position to read from in the file. * if null, read from the current position - * 3 encoding either node.fs.UTF8 or node.fs.RAW - * - * 4 callback(errorno, data) + * 3 encoding either node.UTF8 or node.RAW */ static Handle Read (const Arguments& args) { - if ( args.Length() < 2 - || !args[0]->IsInt32() // fd - || !args[1]->IsNumber() // len - ) return ThrowException(BAD_ARGUMENTS); - HandleScope scope; + if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsNumber()) { + return ThrowException(BAD_ARGUMENTS); + } + int fd = args[0]->Int32Value(); size_t len = args[1]->IntegerValue(); off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; @@ -326,94 +319,7 @@ Read (const Arguments& args) encoding = static_cast(args[3]->Int32Value()); } - 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, - encoding == UTF8 ? AfterUtf8Read : AfterRawRead, promise); - - return scope.Close(promise->Handle()); -} - -static int -AfterStat (eio_req *req) -{ - Promise *promise = reinterpret_cast(req->data); - - if (req->result < 0) { - promise->EmitError(0, NULL); - return 0; - } - - HandleScope scope; - - Local stats = Object::New(); - - struct stat *s = reinterpret_cast(req->ptr2); - - /* ID of device containing file */ - stats->Set(DEV_SYMBOL, Integer::New(s->st_dev)); - /* inode number */ - stats->Set(INO_SYMBOL, Integer::New(s->st_ino)); - /* protection */ - stats->Set(MODE_SYMBOL, Integer::New(s->st_mode)); - /* number of hard links */ - stats->Set(NLINK_SYMBOL, Integer::New(s->st_nlink)); - /* user ID of owner */ - stats->Set(UID_SYMBOL, Integer::New(s->st_uid)); - /* group ID of owner */ - stats->Set(GID_SYMBOL, Integer::New(s->st_gid)); - /* device ID (if special file) */ - stats->Set(RDEV_SYMBOL, Integer::New(s->st_rdev)); - /* total size, in bytes */ - stats->Set(SIZE_SYMBOL, Integer::New(s->st_size)); - /* blocksize for filesystem I/O */ - stats->Set(BLKSIZE_SYMBOL, Integer::New(s->st_blksize)); - /* number of blocks allocated */ - stats->Set(BLOCKS_SYMBOL, Integer::New(s->st_blocks)); - /* time of last access */ - stats->Set(ATIME_SYMBOL, Date::New(1000*static_cast(s->st_atime))); - /* time of last modification */ - stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast(s->st_mtime))); - /* time of last status change */ - stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast(s->st_ctime))); - - Local argv[1] = { stats }; - promise->EmitSuccess(1, argv); - - return 0; -} - -static Handle -Stat (const Arguments& args) -{ - if (args.Length() < 1 || !args[0]->IsString()) - return ThrowException(BAD_ARGUMENTS); - - HandleScope scope; - - String::Utf8Value path(args[0]->ToString()); - - Promise *promise = Promise::Create(true); - - eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, promise); - - return scope.Close(promise->Handle()); -} - -static Handle -StrError (const Arguments& args) -{ - if (args.Length() < 1) return v8::Undefined(); - if (!args[0]->IsNumber()) return v8::Undefined(); - - HandleScope scope; - - int errorno = args[0]->IntegerValue(); - - Local message = String::New(strerror(errorno)); - - return scope.Close(message); + return scope.Close(EIOPromise::Read(fd, len, pos, encoding)); } void @@ -421,7 +327,6 @@ File::Initialize (Handle target) { HandleScope scope; - // POSIX Wrappers NODE_SET_METHOD(target, "close", Close); NODE_SET_METHOD(target, "open", Open); NODE_SET_METHOD(target, "read", Read); @@ -430,6 +335,4 @@ File::Initialize (Handle target) NODE_SET_METHOD(target, "stat", Stat); NODE_SET_METHOD(target, "unlink", Unlink); NODE_SET_METHOD(target, "write", Write); - - NODE_SET_METHOD(target, "strerror", StrError); } diff --git a/src/file.h b/src/file.h index e48694c3b6..02da720b30 100644 --- a/src/file.h +++ b/src/file.h @@ -1,14 +1,114 @@ #ifndef node_file_h #define node_file_h +#include "node.h" +#include "events.h" #include namespace node { +/* Are you missing your favorite POSIX function? It might be very easy to + * add a wrapper. Take a look in deps/libeio/eio.h at the list of wrapper + * functions. If your function is in that list, just follow the lead of + * EIOPromise::Open. You'll need to add two functions, one static function + * in EIOPromise, and one static function which interprets the javascript + * args in src/file.cc. Then just a reference to that function in + * File::Initialize() and you should be good to go. + * Don't forget to add a test to test/mjsunit. + */ + +#define NODE_V8_METHOD_DECLARE(name) \ + static v8::Handle name (const v8::Arguments& args) + class File { public: static void Initialize (v8::Handle target); }; +class EIOPromise : public Promise { + public: + static v8::Handle + Open (const char *path, int flags, mode_t mode) + { + EIOPromise *p = Create(); + p->req_ = eio_open(path, flags, mode, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static v8::Handle + Close (int fd) + { + EIOPromise *p = Create(); + p->req_ = eio_close(fd, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static v8::Handle + Write (int fd, void *buf, size_t count, off_t offset) + { + EIOPromise *p = Create(); + p->req_ = eio_write(fd, buf, count, offset, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static v8::Handle + Read (int fd, size_t count, off_t offset, enum encoding encoding) + { + EIOPromise *p = Create(); + p->encoding_ = encoding; + // NOTE: 2nd param: NULL pointer tells eio to allocate it itself + p->req_ = eio_read(fd, NULL, count, offset, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static v8::Handle + Stat (const char *path) + { + EIOPromise *p = Create(); + p->req_ = eio_stat(path, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static v8::Handle + Rename (const char *path, const char *new_path) + { + EIOPromise *p = Create(); + p->req_ = eio_rename(path, new_path, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static v8::Handle + Unlink (const char *path) + { + EIOPromise *p = Create(); + p->req_ = eio_unlink(path, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static v8::Handle + RMDir (const char *path) + { + EIOPromise *p = Create(); + p->req_ = eio_rmdir(path, EIO_PRI_DEFAULT, After, p); + return p->handle_; + } + + static EIOPromise* Create (void); + + protected: + + void Attach (void); + void Detach (void); + + EIOPromise () : Promise() { } + + private: + + static int After (eio_req *req); + + eio_req *req_; + enum encoding encoding_; +}; + } // namespace node #endif // node_file_h