namespace WTF { GSourceFuncs GSourceWrap::sourceFunctions = { // prepare [](GSource* source, gint*) -> gboolean { return g_source_get_ready_time(source) == 0; }, nullptr, // check // dispatch [](GSource* source, GSourceFunc callback, gpointer data) -> gboolean { ASSERT(source); if (g_source_get_ready_time(source) == -1) return G_SOURCE_CONTINUE; DispatchContext context{ source, data }; return callback(&context); }, nullptr, // finalize nullptr, // closure_callback nullptr, // closure_marshall }; gboolean GSourceWrap::staticDelayBasedVoidCallback(gpointer data) { auto& dispatch = *reinterpret_cast<DispatchContext*>(data); auto& callback = *reinterpret_cast<DelayBased::CallbackContext<void ()>*>(dispatch.second); if (g_cancellable_is_cancelled(callback.second.cancellable.get())) return G_SOURCE_CONTINUE; callback.second.dispatching = true; g_source_set_ready_time(dispatch.first, -1); callback.first(); callback.second.dispatching = false; return G_SOURCE_CONTINUE; } gboolean GSourceWrap::dynamicDelayBasedVoidCallback(gpointer data) { auto& dispatch = *reinterpret_cast<DispatchContext*>(data); auto& callback = *reinterpret_cast<DelayBased::CallbackContext<void ()>*>(dispatch.second); if (g_cancellable_is_cancelled(callback.second.cancellable.get())) return G_SOURCE_CONTINUE; callback.second.dispatching = true; g_source_set_ready_time(dispatch.first, -1); g_source_set_callback(dispatch.first, nullptr, nullptr, nullptr); callback.first(); callback.second.dispatching = false; return G_SOURCE_CONTINUE; } gboolean GSourceWrap::dynamicDelayBasedBoolCallback(gpointer data) { auto& dispatch = *reinterpret_cast<DispatchContext*>(data); auto& callback = *reinterpret_cast<DelayBased::CallbackContext<bool ()>*>(dispatch.second); if (g_cancellable_is_cancelled(callback.second.cancellable.get())) return G_SOURCE_CONTINUE; callback.second.dispatching = true; g_source_set_ready_time(dispatch.first, -1); if (callback.first()) g_source_set_ready_time(dispatch.first, targetTimeForDelay(callback.second.delay)); else g_source_set_callback(dispatch.first, nullptr, nullptr, nullptr); callback.second.dispatching = false; return G_SOURCE_CONTINUE; } gboolean GSourceWrap::staticOneShotCallback(gpointer data) { auto& dispatch = *reinterpret_cast<DispatchContext*>(data); auto& callback = *reinterpret_cast<OneShot::CallbackContext*>(dispatch.second); g_source_set_ready_time(dispatch.first, -1); callback.first(); return G_SOURCE_REMOVE; } gboolean GSourceWrap::staticSocketCallback(GSocket*, GIOCondition condition, gpointer data) { auto& callback = *reinterpret_cast<Socket::CallbackContext*>(data); if (g_cancellable_is_cancelled(callback.second.get())) return G_SOURCE_REMOVE; return callback.first(condition); } gint64 GSourceWrap::targetTimeForDelay(std::chrono::microseconds delay) { if (!delay.count()) return 0; gint64 currentTime = g_get_monotonic_time(); gint64 targetTime = currentTime + std::min<gint64>(G_MAXINT64 - currentTime, delay.count()); ASSERT(targetTime >= currentTime); return targetTime; } GSourceWrap::Base::~Base() { if (m_source) g_source_destroy(m_source.get()); } bool GSourceWrap::DelayBased::isScheduled() const { ASSERT(m_source); return g_source_get_ready_time(m_source.get()) != -1; } bool GSourceWrap::DelayBased::isActive() const { return isScheduled() || m_context.dispatching; } void GSourceWrap::DelayBased::initialize(const char* name, int priority, GMainContext* context) { ASSERT(!m_source); m_source = adoptGRef(g_source_new(&sourceFunctions, sizeof(GSource))); m_context.delay = std::chrono::microseconds(0); m_context.cancellable = adoptGRef(g_cancellable_new()); m_context.dispatching = false; g_source_set_name(m_source.get(), name); g_source_set_priority(m_source.get(), priority); if (!context) context = g_main_context_get_thread_default(); g_source_attach(m_source.get(), context); } void GSourceWrap::DelayBased::schedule(std::chrono::microseconds delay) { ASSERT(m_source); m_context.delay = delay; if (g_cancellable_is_cancelled(m_context.cancellable.get())) m_context.cancellable = adoptGRef(g_cancellable_new()); g_source_set_ready_time(m_source.get(), targetTimeForDelay(delay)); } void GSourceWrap::DelayBased::cancel() { ASSERT(m_source); g_cancellable_cancel(m_context.cancellable.get()); g_source_set_ready_time(m_source.get(), -1); } GSourceWrap::Static::Static(const char* name, std::function<void ()>&& function, int priority, GMainContext* context) { initialize(name, WTF::move(function), priority, context); } void GSourceWrap::Static::initialize(const char* name, std::function<void ()>&& function, int priority, GMainContext* context) { DelayBased::initialize(name, priority, context); g_source_set_callback(m_source.get(), static_cast<GSourceFunc>(staticDelayBasedVoidCallback), new CallbackContext<void ()>{ WTF::move(function), m_context }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext<void ()>>)); } void GSourceWrap::Static::schedule(std::chrono::microseconds delay) { DelayBased::schedule(delay); } void GSourceWrap::Static::cancel() { DelayBased::cancel(); } GSourceWrap::Dynamic::Dynamic(const char* name, int priority, GMainContext* context) { DelayBased::initialize(name, priority, context); } void GSourceWrap::Dynamic::schedule(std::function<void ()>&& function, std::chrono::microseconds delay) { g_source_set_callback(m_source.get(), static_cast<GSourceFunc>(dynamicDelayBasedVoidCallback), new CallbackContext<void ()>{ WTF::move(function), m_context }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext<void ()>>)); DelayBased::schedule(delay); } void GSourceWrap::Dynamic::schedule(std::function<bool ()>&& function, std::chrono::microseconds delay) { g_source_set_callback(m_source.get(), static_cast<GSourceFunc>(dynamicDelayBasedBoolCallback), new CallbackContext<bool ()>{ WTF::move(function), m_context }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext<bool ()>>)); DelayBased::schedule(delay); } void GSourceWrap::Dynamic::cancel() { DelayBased::cancel(); g_source_set_callback(m_source.get(), nullptr, nullptr, nullptr); } void GSourceWrap::OneShot::construct(const char* name, std::function<void ()>&& function, std::chrono::microseconds delay, int priority, GMainContext* context) { GRefPtr<GSource> source = adoptGRef(g_source_new(&sourceFunctions, sizeof(GSource))); g_source_set_name(source.get(), name); g_source_set_priority(source.get(), priority); g_source_set_callback(source.get(), static_cast<GSourceFunc>(staticOneShotCallback), new CallbackContext{ WTF::move(function), nullptr }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext>)); g_source_set_ready_time(source.get(), targetTimeForDelay(delay)); if (!context) context = g_main_context_get_thread_default(); g_source_attach(source.get(), context); } void GSourceWrap::Socket::initialize(const char* name, std::function<bool (GIOCondition)>&& function, GSocket* socket, GIOCondition condition, int priority, GMainContext* context) { ASSERT(!m_source); GCancellable* cancellable = g_cancellable_new(); m_source = adoptGRef(g_socket_create_source(socket, condition, cancellable)); m_cancellable = adoptGRef(cancellable); g_source_set_name(m_source.get(), name); g_source_set_priority(m_source.get(), priority); g_source_set_callback(m_source.get(), reinterpret_cast<GSourceFunc>(staticSocketCallback), new CallbackContext{ WTF::move(function), m_cancellable }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext>)); if (!context) context = g_main_context_get_thread_default(); g_source_attach(m_source.get(), context); } void GSourceWrap::Socket::cancel() { g_cancellable_cancel(m_cancellable.get()); } GSourceWrap::Queue::Queue() { g_mutex_init(&m_mutex); } GSourceWrap::Queue::~Queue() { g_mutex_clear(&m_mutex); } void GSourceWrap::Queue::initialize(const char* name, int priority, GMainContext* context) { m_sourceWrap.initialize(name, std::bind(&Queue::dispatchQueue, this), priority, context); } void GSourceWrap::Queue::queue(std::function<void ()>&& function) { WTF::GMutexLocker<GMutex> lock(m_mutex); m_queue.append(WTF::move(function)); m_sourceWrap.schedule(); } void GSourceWrap::Queue::dispatchQueue() { while (1) { decltype(m_queue) queue; { WTF::GMutexLocker<GMutex> lock(m_mutex); queue = WTF::move(m_queue); } if (!queue.size()) break; for (auto& function : queue) function(); } } } // namespace WTF
bool GSourceWrap::DelayBased::isScheduled() const { ASSERT(m_source); return g_source_get_ready_time(m_source.get()) != -1; }
bool RunLoop::TimerBase::isActive() const { return g_source_get_ready_time(m_source.get()) != -1; }
namespace WTF { static GSourceFuncs runLoopSourceFunctions = { nullptr, // prepare nullptr, // check // dispatch [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean { if (g_source_get_ready_time(source) == -1) return G_SOURCE_CONTINUE; g_source_set_ready_time(source, -1); return callback(userData); }, nullptr, // finalize nullptr, // closure_callback nullptr, // closure_marshall }; RunLoop::RunLoop() { m_mainContext = g_main_context_get_thread_default(); if (!m_mainContext) m_mainContext = isMainThread() ? g_main_context_default() : adoptGRef(g_main_context_new()); ASSERT(m_mainContext); GRefPtr<GMainLoop> innermostLoop = adoptGRef(g_main_loop_new(m_mainContext.get(), FALSE)); ASSERT(innermostLoop); m_mainLoops.append(innermostLoop); m_source = adoptGRef(g_source_new(&runLoopSourceFunctions, sizeof(GSource))); g_source_set_name(m_source.get(), "[WebKit] RunLoop work"); g_source_set_can_recurse(m_source.get(), TRUE); g_source_set_callback(m_source.get(), [](gpointer userData) -> gboolean { static_cast<RunLoop*>(userData)->performWork(); return G_SOURCE_CONTINUE; }, this, nullptr); g_source_set_priority(m_source.get(), G_PRIORITY_HIGH + 30); g_source_attach(m_source.get(), m_mainContext.get()); } RunLoop::~RunLoop() { g_source_destroy(m_source.get()); for (int i = m_mainLoops.size() - 1; i >= 0; --i) { if (!g_main_loop_is_running(m_mainLoops[i].get())) continue; g_main_loop_quit(m_mainLoops[i].get()); } } void RunLoop::run() { RunLoop& runLoop = RunLoop::current(); GMainContext* mainContext = runLoop.m_mainContext.get(); // The innermost main loop should always be there. ASSERT(!runLoop.m_mainLoops.isEmpty()); GMainLoop* innermostLoop = runLoop.m_mainLoops[0].get(); if (!g_main_loop_is_running(innermostLoop)) { g_main_context_push_thread_default(mainContext); g_main_loop_run(innermostLoop); g_main_context_pop_thread_default(mainContext); return; } // Create and run a nested loop if the innermost one was already running. GMainLoop* nestedMainLoop = g_main_loop_new(mainContext, FALSE); runLoop.m_mainLoops.append(adoptGRef(nestedMainLoop)); g_main_context_push_thread_default(mainContext); g_main_loop_run(nestedMainLoop); g_main_context_pop_thread_default(mainContext); runLoop.m_mainLoops.removeLast(); } void RunLoop::stop() { // The innermost main loop should always be there. ASSERT(!m_mainLoops.isEmpty()); GRefPtr<GMainLoop> lastMainLoop = m_mainLoops.last(); if (g_main_loop_is_running(lastMainLoop.get())) g_main_loop_quit(lastMainLoop.get()); } void RunLoop::wakeUp() { g_source_set_ready_time(m_source.get(), g_get_monotonic_time()); } RunLoop::TimerBase::TimerBase(RunLoop& runLoop) : m_runLoop(runLoop) , m_source(adoptGRef(g_source_new(&runLoopSourceFunctions, sizeof(GSource)))) { g_source_set_name(m_source.get(), "[WebKit] RunLoop::Timer work"); g_source_set_callback(m_source.get(), [](gpointer userData) -> gboolean { RunLoop::TimerBase* timer = static_cast<RunLoop::TimerBase*>(userData); timer->fired(); if (timer->m_isRepeating) timer->updateReadyTime(); return G_SOURCE_CONTINUE; }, this, nullptr); g_source_set_priority(m_source.get(), G_PRIORITY_HIGH + 30); g_source_attach(m_source.get(), m_runLoop.m_mainContext.get()); } RunLoop::TimerBase::~TimerBase() { g_source_destroy(m_source.get()); } void RunLoop::TimerBase::setPriority(int priority) { g_source_set_priority(m_source.get(), priority); } void RunLoop::TimerBase::updateReadyTime() { if (!m_fireInterval.count()) { g_source_set_ready_time(m_source.get(), 0); return; } gint64 currentTime = g_get_monotonic_time(); gint64 targetTime = currentTime + std::min<gint64>(G_MAXINT64 - currentTime, m_fireInterval.count()); ASSERT(targetTime >= currentTime); g_source_set_ready_time(m_source.get(), targetTime); } void RunLoop::TimerBase::start(double fireInterval, bool repeat) { auto intervalDuration = std::chrono::duration<double>(fireInterval); auto safeDuration = std::chrono::microseconds::max(); if (intervalDuration < safeDuration) safeDuration = std::chrono::duration_cast<std::chrono::microseconds>(intervalDuration); m_fireInterval = safeDuration; m_isRepeating = repeat; updateReadyTime(); } void RunLoop::TimerBase::stop() { g_source_set_ready_time(m_source.get(), -1); } bool RunLoop::TimerBase::isActive() const { return g_source_get_ready_time(m_source.get()) != -1; } } // namespace WTF
namespace JSC { #if USE(CF) const CFTimeInterval HeapTimer::s_decade = 60 * 60 * 24 * 365 * 10; static const void* retainAPILock(const void* info) { static_cast<JSLock*>(const_cast<void*>(info))->ref(); return info; } static void releaseAPILock(const void* info) { static_cast<JSLock*>(const_cast<void*>(info))->deref(); } HeapTimer::HeapTimer(VM* vm, CFRunLoopRef runLoop) : m_vm(vm) , m_runLoop(runLoop) { memset(&m_context, 0, sizeof(CFRunLoopTimerContext)); m_context.info = &vm->apiLock(); m_context.retain = retainAPILock; m_context.release = releaseAPILock; m_timer = adoptCF(CFRunLoopTimerCreate(kCFAllocatorDefault, s_decade, s_decade, 0, 0, HeapTimer::timerDidFire, &m_context)); CFRunLoopAddTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes); } HeapTimer::~HeapTimer() { CFRunLoopRemoveTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes); CFRunLoopTimerInvalidate(m_timer.get()); } void HeapTimer::timerDidFire(CFRunLoopTimerRef timer, void* context) { JSLock* apiLock = static_cast<JSLock*>(context); apiLock->lock(); VM* vm = apiLock->vm(); // The VM has been destroyed, so we should just give up. if (!vm) { apiLock->unlock(); return; } HeapTimer* heapTimer = 0; if (vm->heap.fullActivityCallback() && vm->heap.fullActivityCallback()->m_timer.get() == timer) heapTimer = vm->heap.fullActivityCallback(); else if (vm->heap.edenActivityCallback() && vm->heap.edenActivityCallback()->m_timer.get() == timer) heapTimer = vm->heap.edenActivityCallback(); else if (vm->heap.sweeper()->m_timer.get() == timer) heapTimer = vm->heap.sweeper(); else RELEASE_ASSERT_NOT_REACHED(); { JSLockHolder locker(vm); heapTimer->doWork(); } apiLock->unlock(); } #elif PLATFORM(EFL) HeapTimer::HeapTimer(VM* vm) : m_vm(vm) , m_timer(0) { } HeapTimer::~HeapTimer() { stop(); } Ecore_Timer* HeapTimer::add(double delay, void* agent) { return ecore_timer_add(delay, reinterpret_cast<Ecore_Task_Cb>(timerEvent), agent); } void HeapTimer::stop() { if (!m_timer) return; ecore_timer_del(m_timer); m_timer = 0; } bool HeapTimer::timerEvent(void* info) { HeapTimer* agent = static_cast<HeapTimer*>(info); JSLockHolder locker(agent->m_vm); agent->doWork(); agent->m_timer = 0; return ECORE_CALLBACK_CANCEL; } #elif USE(GLIB) static GSourceFuncs heapTimerSourceFunctions = { nullptr, // prepare nullptr, // check // dispatch [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean { if (g_source_get_ready_time(source) == -1) return G_SOURCE_CONTINUE; g_source_set_ready_time(source, -1); return callback(userData); }, nullptr, // finalize nullptr, // closure_callback nullptr, // closure_marshall }; HeapTimer::HeapTimer(VM* vm) : m_vm(vm) , m_apiLock(&vm->apiLock()) , m_timer(adoptGRef(g_source_new(&heapTimerSourceFunctions, sizeof(GSource)))) { g_source_set_name(m_timer.get(), "[JavaScriptCore] HeapTimer"); g_source_set_callback(m_timer.get(), [](gpointer userData) -> gboolean { static_cast<HeapTimer*>(userData)->timerDidFire(); return G_SOURCE_CONTINUE; }, this, nullptr); g_source_attach(m_timer.get(), g_main_context_get_thread_default()); } HeapTimer::~HeapTimer() { g_source_destroy(m_timer.get()); } void HeapTimer::timerDidFire() { m_apiLock->lock(); if (!m_apiLock->vm()) { // The VM has been destroyed, so we should just give up. m_apiLock->unlock(); return; } { JSLockHolder locker(m_vm); doWork(); } m_apiLock->unlock(); } #else HeapTimer::HeapTimer(VM* vm) : m_vm(vm) { } HeapTimer::~HeapTimer() { } void HeapTimer::invalidate() { } #endif } // namespace JSC