#include #include "SetTimeout.h" #include "Helpers.h" #include "Caches.h" using namespace v8; namespace tns { void SetTimeout::Init(Isolate* isolate, Local globalTemplate) { Local setTimeoutFuncTemplate = FunctionTemplate::New(isolate, SetTimeoutCallback); globalTemplate->Set(ToV8String(isolate, "setTimeout"), setTimeoutFuncTemplate); Local clearTimeoutFuncTemplate = FunctionTemplate::New(isolate, ClearTimeoutCallback); globalTemplate->Set(ToV8String(isolate, "clearTimeout"), clearTimeoutFuncTemplate); } void SetTimeout::SetTimeoutCallback(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (!args[0]->IsFunction()) { tns::Assert(false, isolate); } Local context = isolate->GetCurrentContext(); double timeout = 0.0; if (args.Length() > 1 && args[1]->IsNumber()) { if (!args[1]->NumberValue(context).To(&timeout)) { tns::Assert(false, isolate); } } // TODO: implement better unique number generator uint32_t key = ++count_; Local callback = args[0].As(); dispatch_block_t block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{ Elapsed(key); }); CacheEntry entry(isolate, new Persistent(isolate, callback)); cache_.emplace(key, entry); dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_MSEC); dispatch_after(time, dispatch_get_main_queue(), block); args.GetReturnValue().Set(key); } void SetTimeout::ClearTimeoutCallback(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (!args[0]->IsNumber()) { tns::Assert(false, isolate); } Local context = isolate->GetCurrentContext(); double value; if (!args[0]->NumberValue(context).To(&value)) { tns::Assert(false, isolate); } uint32_t key = value; auto it = cache_.find(key); if (it == cache_.end()) { return; } RemoveKey(key); } void SetTimeout::Elapsed(const uint32_t key) { auto it = cache_.find(key); if (it == cache_.end()) { return; } Isolate* isolate = it->second.isolate_; Persistent* poCallback = it->second.callback_; v8::Locker locker(isolate); Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); Local cb = poCallback->Get(isolate); std::shared_ptr cache = Caches::Get(isolate); Local context = cache->GetContext(); Local global = context->Global(); Local result; if (!cb->Call(context, global, 0, nullptr).ToLocal(&result)) { tns::Assert(false, isolate); } RemoveKey(key); } void SetTimeout::RemoveKey(const uint32_t key) { auto it = cache_.find(key); if (it == cache_.end()) { return; } Persistent* poCallback = it->second.callback_; poCallback->Reset(); delete poCallback; cache_.erase(it); } robin_hood::unordered_map SetTimeout::cache_; uint32_t SetTimeout::count_ = 0; }