Browse Source

Clean up eio wrappers. Create EIOPromise.

v0.7.4-release
Ryan 16 years ago
parent
commit
747d6723aa
  1. 35
      src/events.cc
  2. 7
      src/events.h
  3. 441
      src/file.cc
  4. 100
      src/file.h

35
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<Object> handle =
Promise::constructor_template->GetFunction()->NewInstance();
Promise *promise = ObjectWrap::Unwrap<Promise>(handle);
promise->ref_ = ref;
if (ref) ev_ref(EV_DEFAULT_UC);
return promise;
}
bool
Promise::EmitSuccess (int argc, v8::Handle<v8::Value> argv[])
{
@ -262,3 +242,18 @@ Promise::EmitError (int argc, v8::Handle<v8::Value> argv[])
return r;
}
Promise*
Promise::Create (void)
{
HandleScope scope;
Local<Object> handle =
Promise::constructor_template->GetFunction()->NewInstance();
Promise *promise = new Promise();
promise->Wrap(handle);
promise->Attach();
return promise;
}

7
src/events.h

@ -23,14 +23,12 @@ class Promise : public EventEmitter {
public:
static void Initialize (v8::Handle<v8::Object> target);
static v8::Persistent<v8::FunctionTemplate> constructor_template;
static Promise* Create (bool ref = false);
static Promise* Create (void);
bool EmitSuccess (int argc, v8::Handle<v8::Value> argv[]);
bool EmitError (int argc, v8::Handle<v8::Value> argv[]);
void Block ();
v8::Handle<v8::Object> 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

441
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<Promise*>(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<Value>
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<Object> 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<double>(t))
int
EIOPromise::After (eio_req *req)
{
Promise *promise = reinterpret_cast<Promise*>(req->data);
HandleScope scope;
EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
assert(req == promise->req_);
if (req->errorno != 0) {
Local<Value> exception = Exception::Error(
String::NewSymbol(strerror(req->errorno)));
promise->EmitError(1, &exception);
return 0;
}
int argc = 0;
Local<Value> 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<Object> stats = Object::New();
struct stat *s = reinterpret_cast<struct stat*>(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<Value>::New(Null());
argv[1] = Integer::New(0);
} else {
char *buf = reinterpret_cast<char*>(req->ptr2);
size_t len = req->result;
Local<Array> array = Array::New(len);
for (unsigned int i = 0; i < len; i++) {
unsigned char val = reinterpret_cast<const unsigned char*>(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) {
promise->EmitSuccess(0, NULL);
// eof
argv[0] = Local<Value>::New(Null());
argv[1] = Integer::New(0);
} else {
promise->EmitError(0, NULL);
char *buf = reinterpret_cast<char*>(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<Value> Rename (const Arguments& args)
static Handle<Value>
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<Value>
Stat (const Arguments& args)
{
Promise *promise = reinterpret_cast<Promise*>(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<Value> Unlink (const Arguments& args)
static Handle<Value>
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<Value> Unlink (const Arguments& args)
{
Promise *promise = reinterpret_cast<Promise*>(req->data);
if (req->result == 0) {
promise->EmitSuccess(0, NULL);
} else {
promise->EmitError(0, NULL);
}
return 0;
}
HandleScope scope;
static Handle<Value> 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<Value>
RMDir (const Arguments& args)
{
Promise *promise = reinterpret_cast<Promise*>(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<Value> 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<Value>
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<mode_t>(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<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
return 0;
}
HandleScope scope;
free(req->ptr2);
ssize_t written = req->result;
Local<Value> 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<Value>
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<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
return 0;
}
HandleScope scope;
Local<Value> argv[2];
if (req->result == 0) {
// eof
argv[0] = Local<Value>::New(Null());
argv[1] = Integer::New(0);
} else {
char *buf = reinterpret_cast<char*>(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<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
return 0;
}
HandleScope scope;
Local<Value> argv[2];
if (req->result == 0) {
argv[0] = Local<Value>::New(Null());
argv[1] = Integer::New(0);
} else {
char *buf = reinterpret_cast<char*>(req->ptr2);
size_t len = req->result;
Local<Array> array = Array::New(len);
for (unsigned int i = 0; i < len; i++) {
unsigned char val = reinterpret_cast<const unsigned char*>(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<Value>
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<enum encoding>(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<Promise*>(req->data);
if (req->result < 0) {
promise->EmitError(0, NULL);
return 0;
}
HandleScope scope;
Local<Object> stats = Object::New();
struct stat *s = reinterpret_cast<struct stat*>(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<double>(s->st_atime)));
/* time of last modification */
stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_mtime)));
/* time of last status change */
stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_ctime)));
Local<Value> argv[1] = { stats };
promise->EmitSuccess(1, argv);
return 0;
}
static Handle<Value>
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<Value>
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<String> 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<Object> 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<Object> 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);
}

100
src/file.h

@ -1,14 +1,114 @@
#ifndef node_file_h
#define node_file_h
#include "node.h"
#include "events.h"
#include <v8.h>
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<v8::Value> name (const v8::Arguments& args)
class File {
public:
static void Initialize (v8::Handle<v8::Object> target);
};
class EIOPromise : public Promise {
public:
static v8::Handle<v8::Object>
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<v8::Object>
Close (int fd)
{
EIOPromise *p = Create();
p->req_ = eio_close(fd, EIO_PRI_DEFAULT, After, p);
return p->handle_;
}
static v8::Handle<v8::Object>
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<v8::Object>
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<v8::Object>
Stat (const char *path)
{
EIOPromise *p = Create();
p->req_ = eio_stat(path, EIO_PRI_DEFAULT, After, p);
return p->handle_;
}
static v8::Handle<v8::Object>
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<v8::Object>
Unlink (const char *path)
{
EIOPromise *p = Create();
p->req_ = eio_unlink(path, EIO_PRI_DEFAULT, After, p);
return p->handle_;
}
static v8::Handle<v8::Object>
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

Loading…
Cancel
Save