namespace WTF { GMainLoopSource& GMainLoopSource::create() { return *new GMainLoopSource(DeleteOnDestroy); } GMainLoopSource::GMainLoopSource() : m_deleteOnDestroy(DoNotDeleteOnDestroy) , m_status(Ready) { } GMainLoopSource::GMainLoopSource(DeleteOnDestroyType deleteOnDestroy) : m_deleteOnDestroy(deleteOnDestroy) , m_status(Ready) { } GMainLoopSource::~GMainLoopSource() { cancel(); } bool GMainLoopSource::isScheduled() const { return m_status == Scheduled; } bool GMainLoopSource::isActive() const { return m_status != Ready; } void GMainLoopSource::cancel() { // Delete-on-destroy GMainLoopSource objects can't be cancelled. if (m_deleteOnDestroy == DeleteOnDestroy) return; // A valid context should only be present if GMainLoopSource is in the Scheduled or Dispatching state. ASSERT(!m_context.source || m_status == Scheduled || m_status == Dispatching); m_status = Ready; g_cancellable_cancel(m_context.socketCancellable.get()); if (!m_context.source) return; Context context; context = WTF::move(m_context); context.destroySource(); } void GMainLoopSource::scheduleIdleSource(const char* name, GSourceFunc sourceFunction, int priority, GMainContext* context) { ASSERT(m_status == Ready); m_status = Scheduled; g_source_set_name(m_context.source.get(), name); if (priority != G_PRIORITY_DEFAULT_IDLE) g_source_set_priority(m_context.source.get(), priority); g_source_set_callback(m_context.source.get(), sourceFunction, this, nullptr); g_source_attach(m_context.source.get(), context); } void GMainLoopSource::schedule(const char* name, std::function<void ()> function, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(g_idle_source_new()), nullptr, // cancellable nullptr, // socketCancellable WTF::move(function), nullptr, // boolCallback nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleIdleSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context); } void GMainLoopSource::schedule(const char* name, std::function<bool ()> function, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(g_idle_source_new()), nullptr, // cancellable nullptr, // socketCancellable nullptr, // voidCallback WTF::move(function), nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleIdleSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context); } void GMainLoopSource::schedule(const char* name, std::function<bool (GIOCondition)> function, GSocket* socket, GIOCondition condition, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); GCancellable* socketCancellable = g_cancellable_new(); m_context = { adoptGRef(g_socket_create_source(socket, condition, socketCancellable)), nullptr, // cancellable adoptGRef(socketCancellable), nullptr, // voidCallback nullptr, // boolCallback WTF::move(function), WTF::move(destroyFunction) }; ASSERT(m_status == Ready); m_status = Scheduled; g_source_set_name(m_context.source.get(), name); g_source_set_callback(m_context.source.get(), reinterpret_cast<GSourceFunc>(socketSourceCallback), this, nullptr); g_source_attach(m_context.source.get(), context); } void GMainLoopSource::scheduleTimeoutSource(const char* name, GSourceFunc sourceFunction, int priority, GMainContext* context) { ASSERT(m_status == Ready); m_status = Scheduled; g_source_set_name(m_context.source.get(), name); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority(m_context.source.get(), priority); g_source_set_callback(m_context.source.get(), sourceFunction, this, nullptr); g_source_attach(m_context.source.get(), context); } void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<void ()> function, std::chrono::milliseconds delay, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(g_timeout_source_new(delay.count())), nullptr, // cancellable nullptr, // socketCancellable WTF::move(function), nullptr, // boolCallback nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context); } void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<bool ()> function, std::chrono::milliseconds delay, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(g_timeout_source_new(delay.count())), nullptr, // cancellable nullptr, // socketCancellable nullptr, // voidCallback WTF::move(function), nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context); } void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<void ()> function, std::chrono::seconds delay, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(g_timeout_source_new_seconds(delay.count())), nullptr, // cancellable nullptr, // socketCancellable WTF::move(function), nullptr, // boolCallback nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context); } void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<bool ()> function, std::chrono::seconds delay, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(g_timeout_source_new_seconds(delay.count())), nullptr, // cancellable nullptr, // socketCancellable nullptr, // voidCallback WTF::move(function), nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context); } struct MicrosecondsTimeoutSource { GSource source; uint64_t delay; }; static GSourceFuncs microsecondsTimeoutSourceFunctions = { nullptr, // prepare nullptr, // check // dispatch [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean { bool repeat = callback(userData); if (repeat) g_source_set_ready_time(source, g_source_get_time(source) + reinterpret_cast<MicrosecondsTimeoutSource*>(source)->delay); return repeat; }, nullptr, // finalize nullptr, // closure_callback nullptr // closure_marshall }; static GSource* createMicrosecondsTimeoutSource(uint64_t delay) { GSource* source = g_source_new(µsecondsTimeoutSourceFunctions, sizeof(MicrosecondsTimeoutSource)); reinterpret_cast<MicrosecondsTimeoutSource*>(source)->delay = delay; g_source_set_ready_time(source, g_get_monotonic_time() + delay); return source; } void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<void ()> function, std::chrono::microseconds delay, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(createMicrosecondsTimeoutSource(delay.count())), nullptr, // cancellable nullptr, // socketCancellable WTF::move(function), nullptr, // boolCallback nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context); } void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<bool ()> function, std::chrono::microseconds delay, int priority, std::function<void ()> destroyFunction, GMainContext* context) { cancel(); ASSERT(!m_context.source); m_context = { adoptGRef(createMicrosecondsTimeoutSource(delay.count())), nullptr, // cancellable nullptr, // socketCancellable nullptr, // voidCallback WTF::move(function), nullptr, // socketCallback WTF::move(destroyFunction) }; scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context); } void GMainLoopSource::scheduleAndDeleteOnDestroy(const char* name, std::function<void()> function, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().schedule(name, function, priority, destroyFunction, context); } void GMainLoopSource::scheduleAndDeleteOnDestroy(const char* name, std::function<bool()> function, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().schedule(name, function, priority, destroyFunction, context); } void GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy(const char* name, std::function<void()> function, std::chrono::milliseconds delay, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().scheduleAfterDelay(name, function, delay, priority, destroyFunction, context); } void GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy(const char* name, std::function<bool()> function, std::chrono::milliseconds delay, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().scheduleAfterDelay(name, function, delay, priority, destroyFunction, context); } void GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy(const char* name, std::function<void()> function, std::chrono::seconds delay, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().scheduleAfterDelay(name, function, delay, priority, destroyFunction, context); } void GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy(const char* name, std::function<bool()> function, std::chrono::seconds delay, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().scheduleAfterDelay(name, function, delay, priority, destroyFunction, context); } void GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy(const char* name, std::function<void()> function, std::chrono::microseconds delay, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().scheduleAfterDelay(name, function, delay, priority, destroyFunction, context); } void GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy(const char* name, std::function<bool()> function, std::chrono::microseconds delay, int priority, std::function<void()> destroyFunction, GMainContext* context) { create().scheduleAfterDelay(name, function, delay, priority, destroyFunction, context); } bool GMainLoopSource::prepareVoidCallback(Context& context) { if (!m_context.source) return false; context = WTF::move(m_context); ASSERT(context.voidCallback); ASSERT(m_status == Scheduled); m_status = Dispatching; return true; } void GMainLoopSource::finishVoidCallback() { m_status = Ready; } void GMainLoopSource::voidCallback() { Context context; if (!prepareVoidCallback(context)) return; context.voidCallback(); if (m_status != Ready && !m_context.source) { // Switch to Ready if it hasn't been re-scheduled or cancelled. finishVoidCallback(); } context.destroySource(); if (m_deleteOnDestroy == DeleteOnDestroy) delete this; } bool GMainLoopSource::prepareBoolCallback(Context& context) { if (!m_context.source) return false; context = WTF::move(m_context); ASSERT(context.boolCallback); ASSERT(m_status == Scheduled || m_status == Dispatching); m_status = Dispatching; return true; } void GMainLoopSource::finishBoolCallback(bool retval, Context& context) { // m_status should reflect whether the GMainLoopSource has been rescheduled during dispatch. ASSERT((!m_context.source && m_status == Dispatching) || m_status == Scheduled); if (retval && !m_context.source) m_context = WTF::move(context); else if (!retval) m_status = Ready; } bool GMainLoopSource::boolCallback() { Context context; if (!prepareBoolCallback(context)) return Stop; bool retval = context.boolCallback(); if (m_status != Ready && !m_context.source) { // Prepare for a new iteration or switch to Ready if it hasn't been re-scheduled or cancelled. finishBoolCallback(retval, context); } if (context.source) { context.destroySource(); if (m_deleteOnDestroy == DeleteOnDestroy) delete this; } return retval; } bool GMainLoopSource::socketCallback(GIOCondition condition) { if (!m_context.source) return Stop; Context context; context = WTF::move(m_context); ASSERT(context.socketCallback); ASSERT(m_status == Scheduled || m_status == Dispatching); m_status = Dispatching; if (g_cancellable_is_cancelled(context.socketCancellable.get())) { context.destroySource(); return Stop; } bool retval = context.socketCallback(condition); if (m_status != Ready && !m_context.source) { // m_status should reflect whether the GMainLoopSource has been rescheduled during dispatch. ASSERT((!m_context.source && m_status == Dispatching) || m_status == Scheduled); if (retval && !m_context.source) m_context = WTF::move(context); else if (!retval) m_status = Ready; } if (context.source) context.destroySource(); return retval; } gboolean GMainLoopSource::voidSourceCallback(GMainLoopSource* source) { source->voidCallback(); return G_SOURCE_REMOVE; } gboolean GMainLoopSource::boolSourceCallback(GMainLoopSource* source) { return source->boolCallback() == Continue; } gboolean GMainLoopSource::socketSourceCallback(GSocket*, GIOCondition condition, GMainLoopSource* source) { return source->socketCallback(condition) == Continue; } void GMainLoopSource::Context::destroySource() { g_source_destroy(source.get()); if (destroyCallback) destroyCallback(); } } // namespace WTF
void GCActivityCallback::cancelTimer() { m_delay = -1; g_source_set_ready_time(m_timer.get(), -1); }
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
static void g_file_monitor_source_update_ready_time (GFileMonitorSource *fms) { g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms)); }
void KeyboardEventRepeating::cancel() { if (!!m_event.time) g_source_set_ready_time(m_source, -1); m_event = { 0, 0, 0 }; }