Browse Source

Implement new wrap for uv timer

v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
9d1bad8960
  1. 1
      src/node_extensions.h
  2. 263
      src/timer_wrap.cc
  3. 17
      test/pummel/test-timer-wrap.js
  4. 1
      wscript

1
src/node_extensions.h

@ -34,5 +34,6 @@ NODE_EXT_LIST_ITEM(node_http_parser)
NODE_EXT_LIST_ITEM(node_signal_watcher)
NODE_EXT_LIST_ITEM(node_stdio)
NODE_EXT_LIST_ITEM(node_os)
NODE_EXT_LIST_ITEM(node_timer_wrap)
NODE_EXT_LIST_END

263
src/timer_wrap.cc

@ -0,0 +1,263 @@
#include <v8.h>
#include <uv.h>
#include <node.h>
// Rules:
//
// - Do not throw from handle methods. Set errno.
//
// - MakeCallback may only be made directly off the event loop.
// That is there can be no JavaScript stack frames underneith it.
// (Is there anyway to assert that?)
//
// - No use of v8::WeakReferenceCallback. The close callback signifies that
// we're done with a handle - external resources can be freed.
//
// - Reusable?
// Target API:
//
// var socket = new Timer2();
// socket.init();
//
// var req = new Req();
// req.init(socket);
// req.onclose = function(status) {
// if (s) {
// console.error("failed to connect: %s", errno);
// } else {
// console.error("connected!");
// }
// })
//
// var r = socket.connect(req, "127.0.0.1", 8000);
//
// if (r) {
// console.error("got error: %s", errno);
// }
#define UNWRAP \
assert(!args.Holder().IsEmpty()); \
assert(args.Holder()->InternalFieldCount() > 0); \
TimerWrap* wrap = \
static_cast<TimerWrap*>(args.Holder()->GetPointerFromInternalField(0)); \
if (!wrap) { \
SetErrno(UV_EBADF); \
return scope.Close(Integer::New(-1)); \
}
namespace node {
using v8::Object;
using v8::Handle;
using v8::Local;
using v8::Persistent;
using v8::Value;
using v8::HandleScope;
using v8::FunctionTemplate;
using v8::String;
using v8::Function;
using v8::TryCatch;
using v8::Context;
using v8::Arguments;
using v8::Integer;
// MakeCallback may only be made directly off the event loop.
// That is there can be no JavaScript stack frames underneath it.
// (Is there any way to assert that?)
//
// Maybe make this a method of a node::Handle super class
//
// TODO: share me!
static void MakeCallback(Handle<Object> object,
const char* method,
int argc,
Handle<Value> argv[]) {
HandleScope scope;
Local<Value> callback_v = object->Get(String::New(method));
assert(callback_v->IsFunction());
Local<Function> callback = Local<Function>::Cast(callback_v);
// TODO Hook for long stack traces to be made here.
TryCatch try_catch;
callback->Call(object, argc, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
}
// TODO: share me!
static void SetErrno(uv_err_code code) {
uv_err_t err;
err.code = code;
Context::GetCurrent()->Global()->Set(String::NewSymbol("errno"),
String::NewSymbol(uv_err_name(err)));
}
class TimerWrap {
public:
static void Initialize(Handle<Object> target) {
HandleScope scope;
Local<FunctionTemplate> constructor = FunctionTemplate::New(New);
constructor->InstanceTemplate()->SetInternalFieldCount(1);
constructor->SetClassName(String::NewSymbol("Timer"));
NODE_SET_PROTOTYPE_METHOD(constructor, "start", Start);
NODE_SET_PROTOTYPE_METHOD(constructor, "stop", Stop);
NODE_SET_PROTOTYPE_METHOD(constructor, "setRepeat", SetRepeat);
NODE_SET_PROTOTYPE_METHOD(constructor, "getRepeat", GetRepeat);
NODE_SET_PROTOTYPE_METHOD(constructor, "again", Again);
NODE_SET_PROTOTYPE_METHOD(constructor, "close", Close);
target->Set(String::NewSymbol("Timer"), constructor->GetFunction());
}
private:
static Handle<Value> New(const Arguments& args) {
// This constructor should not be exposed to public javascript.
// Therefore we assert that we are not trying to call this as a
// normal function.
assert(args.IsConstructCall());
HandleScope scope;
TimerWrap *wrap = new TimerWrap(args.This());
assert(wrap);
return scope.Close(args.This());
}
TimerWrap(Handle<Object> object) {
int r = uv_timer_init(&handle_, OnClose, this);
assert(r == 0); // How do we proxy this error up to javascript?
// Suggestion: uv_timer_init() returns void.
assert(object_.IsEmpty());
assert(object->InternalFieldCount() > 0);
object_ = v8::Persistent<v8::Object>::New(object);
object_->SetPointerInInternalField(0, this);
}
~TimerWrap() {
assert(!object_.IsEmpty());
object_->SetPointerInInternalField(0, NULL);
object_.Dispose();
}
static Handle<Value> Start(const Arguments& args) {
HandleScope scope;
UNWRAP
int64_t timeout = args[0]->IntegerValue();
int64_t repeat = args[1]->IntegerValue();
int r = uv_timer_start(&wrap->handle_, OnTimeout, timeout, repeat);
// Can r ever be an error?
if (r) SetErrno(uv_last_error().code);
return scope.Close(Integer::New(r));
}
static Handle<Value> Stop(const Arguments& args) {
HandleScope scope;
UNWRAP
int r = uv_timer_stop(&wrap->handle_);
// Can r ever be an error?
if (r) SetErrno(uv_last_error().code);
return scope.Close(Integer::New(r));
}
static Handle<Value> Again(const Arguments& args) {
HandleScope scope;
UNWRAP
int r = uv_timer_again(&wrap->handle_);
if (r) SetErrno(uv_last_error().code);
return scope.Close(Integer::New(r));
}
static Handle<Value> SetRepeat(const Arguments& args) {
HandleScope scope;
UNWRAP
int64_t repeat = args[0]->IntegerValue();
uv_timer_set_repeat(&wrap->handle_, repeat);
return scope.Close(Integer::New(0));
}
static Handle<Value> GetRepeat(const Arguments& args) {
HandleScope scope;
UNWRAP
int64_t repeat = uv_timer_get_repeat(&wrap->handle_);
if (repeat < 0) SetErrno(uv_last_error().code);
return scope.Close(Integer::New(repeat));
}
// TODO: share me?
static Handle<Value> Close(const Arguments& args) {
HandleScope scope;
UNWRAP
int r = uv_close(&wrap->handle_);
if (r) SetErrno(uv_last_error().code);
return scope.Close(Integer::New(r));
}
static void OnTimeout(uv_handle_t* handle, int status) {
HandleScope scope;
TimerWrap* wrap = static_cast<TimerWrap*>(handle->data);
assert(wrap);
Local<Value> argv[1] = { Integer::New(status) };
MakeCallback(wrap->object_, "ontimeout", 1, argv);
}
static void OnClose(uv_handle_t* handle, int status) {
HandleScope scope;
TimerWrap* wrap = static_cast<TimerWrap*>(handle->data);
Local<Value> argv[1] = { Integer::New(status) };
argv[0] = Integer::New(status);
MakeCallback(wrap->object_, "onclose", 1, argv);
// Close the scope before we destruct this object. Probably nothing is
// wrong if the scope is destroyed after, I just don't want the
// possibility of a bug.
scope.Close(v8::Undefined());
delete wrap;
}
uv_handle_t handle_;
Persistent<Object> object_;
};
} // namespace node
NODE_MODULE(node_timer_wrap, node::TimerWrap::Initialize);

17
test/pummel/test-timer-wrap.js

@ -0,0 +1,17 @@
var common = require('../common');
var assert = require('assert');
var Timer = process.binding('timer_wrap').Timer;
var t = new Timer();
t.ontimeout = function() {
console.log('timeout');
t.close();
};
t.onclose = function() {
console.log('close');
};
t.start(1000, 0);

1
wscript

@ -835,6 +835,7 @@ def build(bld):
src/node_os.cc
src/node_dtrace.cc
src/node_string.cc
src/timer_wrap.cc
"""
if sys.platform.startswith("win32"):

Loading…
Cancel
Save