| #include <node.h> |
| #include <node_timer.h> |
| #include <assert.h> |
| |
| using namespace v8; |
| using namespace node; |
| |
| Persistent<FunctionTemplate> Timer::constructor_template; |
| |
| static Persistent<String> timeout_symbol; |
| static Persistent<String> repeat_symbol; |
| static Persistent<String> callback_symbol; |
| |
| void |
| Timer::Initialize (Handle<Object> target) |
| { |
| HandleScope scope; |
| |
| Local<FunctionTemplate> t = FunctionTemplate::New(Timer::New); |
| constructor_template = Persistent<FunctionTemplate>::New(t); |
| constructor_template->InstanceTemplate()->SetInternalFieldCount(1); |
| constructor_template->SetClassName(String::NewSymbol("Timer")); |
| |
| timeout_symbol = NODE_PSYMBOL("timeout"); |
| repeat_symbol = NODE_PSYMBOL("repeat"); |
| callback_symbol = NODE_PSYMBOL("callback"); |
| |
| NODE_SET_PROTOTYPE_METHOD(constructor_template, "start", Timer::Start); |
| NODE_SET_PROTOTYPE_METHOD(constructor_template, "stop", Timer::Stop); |
| NODE_SET_PROTOTYPE_METHOD(constructor_template, "again", Timer::Again); |
| |
| constructor_template->InstanceTemplate()->SetAccessor(repeat_symbol, |
| RepeatGetter, RepeatSetter); |
| |
| target->Set(String::NewSymbol("Timer"), constructor_template->GetFunction()); |
| } |
| |
| Handle<Value> |
| Timer::RepeatGetter (Local<String> property, const AccessorInfo& info) |
| { |
| HandleScope scope; |
| Timer *timer = ObjectWrap::Unwrap<Timer>(info.This()); |
| |
| assert(timer); |
| assert (property == repeat_symbol); |
| |
| Local<Integer> v = Integer::New(timer->watcher_.repeat); |
| |
| return scope.Close(v); |
| } |
| |
| void |
| Timer::RepeatSetter (Local<String> property, Local<Value> value, const AccessorInfo& info) |
| { |
| HandleScope scope; |
| Timer *timer = ObjectWrap::Unwrap<Timer>(info.This()); |
| |
| assert(timer); |
| assert(property == repeat_symbol); |
| |
| timer->watcher_.repeat = NODE_V8_UNIXTIME(value); |
| } |
| |
| void |
| Timer::OnTimeout (EV_P_ ev_timer *watcher, int revents) |
| { |
| Timer *timer = static_cast<Timer*>(watcher->data); |
| |
| assert(revents == EV_TIMEOUT); |
| |
| HandleScope scope; |
| |
| Local<Value> callback_v = timer->handle_->Get(callback_symbol); |
| if (!callback_v->IsFunction()) { |
| timer->Stop(); |
| return; |
| } |
| |
| Local<Function> callback = Local<Function>::Cast(callback_v); |
| |
| TryCatch try_catch; |
| |
| callback->Call(timer->handle_, 0, NULL); |
| |
| if (try_catch.HasCaught()) { |
| FatalException(try_catch); |
| } |
| |
| if (timer->watcher_.repeat == 0) timer->Unref(); |
| } |
| |
| Timer::~Timer () |
| { |
| ev_timer_stop(EV_DEFAULT_UC_ &watcher_); |
| } |
| |
| Handle<Value> |
| Timer::New (const Arguments& args) |
| { |
| HandleScope scope; |
| |
| Timer *t = new Timer(); |
| t->Wrap(args.Holder()); |
| |
| return args.This(); |
| } |
| |
| Handle<Value> |
| Timer::Start (const Arguments& args) |
| { |
| HandleScope scope; |
| Timer *timer = ObjectWrap::Unwrap<Timer>(args.Holder()); |
| |
| if (args.Length() != 2) |
| return ThrowException(String::New("Bad arguments")); |
| |
| bool was_active = ev_is_active(&timer->watcher_); |
| |
| ev_tstamp after = NODE_V8_UNIXTIME(args[0]); |
| ev_tstamp repeat = NODE_V8_UNIXTIME(args[1]); |
| ev_timer_init(&timer->watcher_, Timer::OnTimeout, after, repeat); |
| timer->watcher_.data = timer; |
| |
| // Update the event loop time. Need to call this because processing JS can |
| // take non-negligible amounts of time. |
| ev_now_update(EV_DEFAULT_UC); |
| |
| ev_timer_start(EV_DEFAULT_UC_ &timer->watcher_); |
| |
| if (!was_active) timer->Ref(); |
| |
| return Undefined(); |
| } |
| |
| |
| Handle<Value> Timer::Stop(const Arguments& args) { |
| HandleScope scope; |
| Timer *timer = ObjectWrap::Unwrap<Timer>(args.Holder()); |
| timer->Stop(); |
| return Undefined(); |
| } |
| |
| |
| void Timer::Stop () { |
| if (watcher_.active) { |
| ev_timer_stop(EV_DEFAULT_UC_ &watcher_); |
| Unref(); |
| } |
| } |
| |
| |
| Handle<Value> Timer::Again(const Arguments& args) { |
| HandleScope scope; |
| Timer *timer = ObjectWrap::Unwrap<Timer>(args.Holder()); |
| |
| int was_active = ev_is_active(&timer->watcher_); |
| |
| if (args.Length() > 0) { |
| ev_tstamp repeat = NODE_V8_UNIXTIME(args[0]); |
| if (repeat > 0) timer->watcher_.repeat = repeat; |
| } |
| |
| ev_timer_again(EV_DEFAULT_UC_ &timer->watcher_); |
| |
| // ev_timer_again can start or stop the watcher. |
| // So we need to check what happened and adjust the ref count |
| // appropriately. |
| |
| if (ev_is_active(&timer->watcher_)) { |
| if (!was_active) timer->Ref(); |
| } else { |
| if (was_active) timer->Unref(); |
| } |
| |
| return Undefined(); |
| } |