/* In prepare, we're just checking the monotonic time against * our projected wakeup. */ static gboolean g_datetime_source_prepare (GSource *source, gint *timeout) { GDateTimeSource *datetime_source = (GDateTimeSource*)source; gint64 monotonic_now; #ifdef HAVE_TIMERFD if (datetime_source->pollfd.fd != -1) { *timeout = -1; return datetime_source->initially_expired; /* Should be TRUE at most one time, FALSE forever after */ } #endif monotonic_now = g_source_get_time (source); if (monotonic_now < datetime_source->wakeup_expiration) { /* Round up to ensure that we don't try again too early */ *timeout = (datetime_source->wakeup_expiration - monotonic_now + 999) / 1000; return FALSE; } *timeout = 0; return g_datetime_source_is_expired (datetime_source); }
/** FD event source dispatch() method. * This is called if either prepare() or check() returned TRUE. */ static gboolean fd_source_dispatch(GSource *source, GSourceFunc callback, void *user_data) { struct fd_source *fsource; const char *name; unsigned int revents; gboolean keep; fsource = (struct fd_source *)source; name = g_source_get_name(source); revents = fsource->pollfd.revents; if (revents != 0) { sr_spew("%s: %s " G_POLLFD_FORMAT ", revents 0x%.2X", __func__, name, fsource->pollfd.fd, revents); } else { sr_spew("%s: %s " G_POLLFD_FORMAT ", timed out", __func__, name, fsource->pollfd.fd); } if (!callback) { sr_err("Callback not set, cannot dispatch event."); return G_SOURCE_REMOVE; } keep = (*(sr_receive_data_callback)callback) (fsource->pollfd.fd, revents, user_data); if (fsource->timeout_us >= 0 && G_LIKELY(keep) && G_LIKELY(!g_source_is_destroyed(source))) fsource->due_us = g_source_get_time(source) + fsource->timeout_us; return keep; }
static gint master_clock_get_swap_wait_time (ClutterMasterClockDefault *master_clock) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); const GSList *stages, *l; gint64 min_update_time = -1; stages = clutter_stage_manager_peek_stages (stage_manager); for (l = stages; l != NULL; l = l->next) { gint64 update_time = _clutter_stage_get_update_time (l->data); if (min_update_time == -1 || (update_time != -1 && update_time < min_update_time)) min_update_time = update_time; } if (min_update_time == -1) { return -1; } else { gint64 now = g_source_get_time (master_clock->source); if (min_update_time < now) { return 0; } else { gint64 delay_us = min_update_time - now; return (delay_us + 999) / 1000; } } }
static gboolean clutter_clock_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { ClutterClockSource *clock_source = (ClutterClockSource *) source; ClutterMasterClockDefault *master_clock = clock_source->master_clock; gboolean stages_updated = FALSE; GSList *stages; CLUTTER_NOTE (SCHEDULER, "Master clock [tick]"); _clutter_threads_acquire_lock (); /* Get the time to use for this frame */ master_clock->cur_tick = g_source_get_time (source); #ifdef CLUTTER_ENABLE_DEBUG master_clock->remaining_budget = master_clock->frame_budget; #endif /* We need to protect ourselves against stages being destroyed during * event handling - master_clock_list_ready_stages() returns a * list of referenced that we'll unref afterwards. */ stages = master_clock_list_ready_stages (master_clock); master_clock->idle = FALSE; /* Each frame is split into three separate phases: */ /* 1. process all the events; each stage goes through its events queue * and processes each event according to its type, then emits the * various signals that are associated with the event */ master_clock_process_events (master_clock, stages); /* 2. advance the timelines */ master_clock_advance_timelines (master_clock); /* 3. relayout and redraw the stages */ stages_updated = master_clock_update_stages (master_clock, stages); /* The master clock goes idle if no stages were updated and falls back * to polling for timeline progressions... */ if (!stages_updated) master_clock->idle = TRUE; master_clock_reschedule_stage_updates (master_clock, stages); g_slist_foreach (stages, (GFunc) g_object_unref, NULL); g_slist_free (stages); master_clock->prev_tick = master_clock->cur_tick; _clutter_threads_release_lock (); return TRUE; }
static gboolean sleep_source_prepare (GSource *source, gint *timeout) { sleep_source_prepare_time = g_source_get_time (source); *timeout = -1; return FALSE; }
static gboolean sleep_source_check (GSource *source) { if (g_source_get_time (source) != sleep_source_prepare_time) sleep_serial++; return FALSE; }
/** FD event source check() method. * This is called after poll() returns to check whether an event fired. */ static gboolean fd_source_check(GSource *source) { struct fd_source *fsource; unsigned int revents; fsource = (struct fd_source *)source; revents = fsource->pollfd.revents; return (revents != 0 || (fsource->timeout_us >= 0 && fsource->due_us <= g_source_get_time(source))); }
static void collect_timeout (gchar * sessionid, GstRTSPSession * sess, GstPoolSource * psrc) { gint timeout; GTimeVal now; gint64 tmp; tmp = g_source_get_time ((GSource *) psrc); now.tv_sec = tmp / G_USEC_PER_SEC; now.tv_usec = tmp % G_USEC_PER_SEC; timeout = gst_rtsp_session_next_timeout (sess, &now); GST_INFO ("%p: next timeout: %d", sess, timeout); if (psrc->timeout == -1 || timeout < psrc->timeout) psrc->timeout = timeout; }
/* In check, we're looking at the wall clock. */ static gboolean g_datetime_source_check (GSource *source) { GDateTimeSource *datetime_source = (GDateTimeSource*)source; #ifdef HAVE_TIMERFD if (datetime_source->pollfd.fd != -1) return datetime_source->pollfd.revents != 0; #endif if (g_datetime_source_is_expired (datetime_source)) return TRUE; g_datetime_source_reschedule (datetime_source, g_source_get_time (source)); return FALSE; }
/** USB event source check() method. */ static gboolean usb_source_check(GSource *source) { struct usb_source *usource; GPollFD *pollfd; unsigned int revents; unsigned int i; usource = (struct usb_source *)source; revents = 0; for (i = 0; i < usource->pollfds->len; i++) { pollfd = g_ptr_array_index(usource->pollfds, i); revents |= pollfd->revents; } return (revents != 0 || (usource->due_us != INT64_MAX && usource->due_us <= g_source_get_time(source))); }
static CoglBool cogl_glib_source_check (GSource *source) { CoglGLibSource *cogl_source = (CoglGLibSource *) source; int i; if (cogl_source->expiration_time >= 0 && g_source_get_time (source) >= cogl_source->expiration_time) return TRUE; for (i = 0; i < cogl_source->poll_fds->len; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); if (poll_fd->revents != 0) return TRUE; } return FALSE; }
static gboolean evict_source_check (GSource *source) { EvictSource *ev = (EvictSource *)source; g_assert (ev != NULL); g_assert (ev->heap != NULL); if (ev->heap->len > 0) { CacheItem *item; gint64 now; now = g_source_get_time (source); item = egg_heap_peek (ev->heap, gpointer); return (item->evict_at <= now); } return FALSE; }
/** USB event source dispatch() method. */ static gboolean usb_source_dispatch(GSource *source, GSourceFunc callback, void *user_data) { struct usb_source *usource; GPollFD *pollfd; unsigned int revents; unsigned int i; gboolean keep; usource = (struct usb_source *)source; revents = 0; /* * This is somewhat arbitrary, but drivers use revents to distinguish * actual I/O from timeouts. When we remove the user timeout from the * driver API, this will no longer be needed. */ for (i = 0; i < usource->pollfds->len; i++) { pollfd = g_ptr_array_index(usource->pollfds, i); revents |= pollfd->revents; } if (revents != 0) sr_spew("%s: revents 0x%.2X", __func__, revents); else sr_spew("%s: timed out", __func__); if (!callback) { sr_err("Callback not set, cannot dispatch event."); return G_SOURCE_REMOVE; } keep = (*(sr_receive_data_callback)callback)(-1, revents, user_data); if (G_LIKELY(keep) && G_LIKELY(!g_source_is_destroyed(source))) { if (usource->timeout_us >= 0) usource->due_us = g_source_get_time(source) + usource->timeout_us; else usource->due_us = INT64_MAX; } return keep; }
static gboolean gb_frame_source_prepare (GSource *source, gint *timeout_) { GbFrameSource *fsource = (GbFrameSource *)source; gint64 current_time; guint elapsed_time; guint new_frame_num; guint frame_time; current_time = g_source_get_time(source) / 1000; elapsed_time = current_time - fsource->start_time; new_frame_num = elapsed_time * fsource->fps / 1000; /* If time has gone backwards or the time since the last frame is * greater than the two frames worth then reset the time and do a * frame now */ if (new_frame_num < fsource->frame_count || new_frame_num - fsource->frame_count > 2) { /* Get the frame time rounded up to the nearest ms */ frame_time = (1000 + fsource->fps - 1) / fsource->fps; /* Reset the start time */ fsource->start_time = current_time; /* Move the start time as if one whole frame has elapsed */ fsource->start_time -= frame_time; fsource->frame_count = 0; *timeout_ = 0; return TRUE; } else if (new_frame_num > fsource->frame_count) { *timeout_ = 0; return TRUE; } else { *timeout_ = (fsource->frame_count + 1) * 1000 / fsource->fps - elapsed_time; return FALSE; } }
/** FD event source dispatch() method. * This is called if either prepare() or check() returned TRUE. */ static gboolean fd_source_dispatch(GSource *source, GSourceFunc callback, void *user_data) { struct fd_source *fsource; unsigned int revents; gboolean keep; fsource = (struct fd_source *)source; revents = fsource->pollfd.revents; if (!callback) { sr_err("Callback not set, cannot dispatch event."); return G_SOURCE_REMOVE; } keep = (*(sr_receive_data_callback)callback) (fsource->pollfd.fd, revents, user_data); if (fsource->timeout_us >= 0 && G_LIKELY(keep) && G_LIKELY(!g_source_is_destroyed(source))) fsource->due_us = g_source_get_time(source) + fsource->timeout_us; return keep; }
/** FD event source prepare() method. * This is called immediately before poll(). */ static gboolean fd_source_prepare(GSource *source, int *timeout) { int64_t now_us; struct fd_source *fsource; int remaining_ms; fsource = (struct fd_source *)source; if (fsource->timeout_us >= 0) { now_us = g_source_get_time(source); if (fsource->due_us == 0) { /* First-time initialization of the expiration time */ fsource->due_us = now_us + fsource->timeout_us; } remaining_ms = (MAX(0, fsource->due_us - now_us) + 999) / 1000; } else { remaining_ms = -1; } *timeout = remaining_ms; return (remaining_ms == 0); }
static gboolean keyboard_repeat (gpointer data) { ClutterSeatEvdev *seat = data; GSource *source; /* There might be events queued in libinput that could cancel the repeat timer. */ _clutter_device_manager_evdev_dispatch (seat->manager_evdev); if (!seat->repeat_timer) return G_SOURCE_REMOVE; g_return_val_if_fail (seat->repeat_device != NULL, G_SOURCE_REMOVE); source = g_main_context_find_source_by_id (NULL, seat->repeat_timer); clutter_seat_evdev_notify_key (seat, seat->repeat_device, g_source_get_time (source), seat->repeat_key, AUTOREPEAT_VALUE, FALSE); return G_SOURCE_CONTINUE; }
static gboolean g_datetime_source_is_expired (GDateTimeSource *datetime_source) { gint64 real_now; gint64 monotonic_now; real_now = g_get_real_time (); monotonic_now = g_source_get_time ((GSource*)datetime_source); if (datetime_source->initially_expired) return TRUE; if (datetime_source->real_expiration <= real_now) return TRUE; /* We can't really detect without system support when things * change; so just trigger every second (i.e. our wakeup * expiration) */ if (datetime_source->cancel_on_set && monotonic_now >= datetime_source->wakeup_expiration) return TRUE; return FALSE; }
/** USB event source prepare() method. */ static gboolean usb_source_prepare(GSource *source, int *timeout) { int64_t now_us, usb_due_us; struct usb_source *usource; struct timeval usb_timeout; int remaining_ms; int ret; usource = (struct usb_source *)source; ret = libusb_get_next_timeout(usource->usb_ctx, &usb_timeout); if (G_UNLIKELY(ret < 0)) { sr_err("Failed to get libusb timeout: %s", libusb_error_name(ret)); } now_us = g_source_get_time(source); if (usource->due_us == 0) { /* First-time initialization of the expiration time */ usource->due_us = now_us + usource->timeout_us; } if (ret == 1) { usb_due_us = (int64_t)usb_timeout.tv_sec * G_USEC_PER_SEC + usb_timeout.tv_usec + now_us; if (usb_due_us < usource->due_us) usource->due_us = usb_due_us; } if (usource->due_us != INT64_MAX) remaining_ms = (MAX(0, usource->due_us - now_us) + 999) / 1000; else remaining_ms = -1; *timeout = remaining_ms; return (remaining_ms == 0); }
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
/* * master_clock_next_frame_delay: * @master_clock: a #ClutterMasterClock * * Computes the number of delay before we need to draw the next frame. * * Return value: -1 if there is no next frame pending, otherwise the * number of millseconds before the we need to draw the next frame */ static gint master_clock_next_frame_delay (ClutterMasterClock *master_clock) { gint64 now, next; if (!master_clock_is_running (master_clock)) return -1; /* When we have sync-to-vblank, we count on swap-buffer requests (or * swap-buffer-complete events if supported in the backend) to throttle our * frame rate so no additional delay is needed to start the next frame. * * If the master-clock has become idle due to no timeline progression causing * redraws then we can no longer rely on vblank synchronization because the * last real stage update/redraw may have happened a long time ago and so we * fallback to polling for timeline progressions every 1/frame_rate seconds. * * (NB: if there aren't even any timelines running then the master clock will * be completely stopped in master_clock_is_running()) */ if (clutter_feature_available (CLUTTER_FEATURE_SYNC_TO_VBLANK) && !master_clock->idle) { CLUTTER_NOTE (SCHEDULER, "vblank available and updated stages"); return 0; } if (master_clock->prev_tick == 0) { /* If we weren't previously running, then draw the next frame * immediately */ CLUTTER_NOTE (SCHEDULER, "draw the first frame immediately"); return 0; } /* Otherwise, wait at least 1/frame_rate seconds since we last * started a frame */ #if GLIB_CHECK_VERSION (2, 27, 3) now = g_source_get_time (master_clock->source); #else { GTimeVal source_time; g_source_get_current_time (master_clock->source, &source_time); now = source_time.tv_sec * 1000000L + source_time.tv_usec; } #endif next = master_clock->prev_tick; /* If time has gone backwards then there's no way of knowing how long we should wait so let's just dispatch immediately */ if (now <= next) { CLUTTER_NOTE (SCHEDULER, "Time has gone backwards"); return 0; } next += (1000000L / clutter_get_default_frame_rate ()); if (next <= now) { CLUTTER_NOTE (SCHEDULER, "Less than %lu microsecs", 1000000L / (gulong) clutter_get_default_frame_rate ()); return 0; } else { CLUTTER_NOTE (SCHEDULER, "Waiting %" G_GINT64_FORMAT " msecs", (next - now) / 1000); return (next - now) / 1000; } }
static gboolean g_file_monitor_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { GFileMonitorSource *fms = (GFileMonitorSource *) source; QueuedEvent *event; GQueue event_queue; gint64 now; /* make sure the monitor still exists */ if (!fms->instance) return FALSE; now = g_source_get_time (source); /* Acquire the lock once and grab all events in one go, handling the * queued events first. This avoids strange possibilities in cases of * long delays, such as CHANGED events coming before CREATED events. * * We do this by converting the applicable pending changes into queued * events (after the ones already queued) and then stealing the entire * event queue in one go. */ g_mutex_lock (&fms->lock); /* Create events for any pending changes that are due to fire */ while (!g_sequence_is_empty (fms->pending_changes)) { GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes); PendingChange *pending = g_sequence_get (iter); /* We've gotten to a pending change that's not ready. Stop. */ if (pending_change_get_ready_time (pending, fms) > now) break; if (pending->dirty) { /* It's time to send another CHANGED and update the record */ g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL); pending->last_emission = now; pending->dirty = FALSE; g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms); } else { /* It's time to send CHANGES_DONE and remove the pending record */ g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL); g_file_monitor_source_remove_pending_change (fms, iter, pending->child); } } /* Steal the queue */ memcpy (&event_queue, &fms->event_queue, sizeof event_queue); memset (&fms->event_queue, 0, sizeof fms->event_queue); g_file_monitor_source_update_ready_time (fms); g_mutex_unlock (&fms->lock); /* We now have our list of events to deliver */ while ((event = g_queue_pop_head (&event_queue))) { /* an event handler could destroy 'instance', so check each time */ if (fms->instance) g_file_monitor_emit_event (fms->instance, event->child, event->other, event->event_type); queued_event_free (event); } return TRUE; }
static gboolean clutter_clock_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { ClutterClockSource *clock_source = (ClutterClockSource *) source; ClutterMasterClock *master_clock = clock_source->master_clock; ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); gboolean stages_updated = FALSE; GSList *stages, *l; CLUTTER_STATIC_TIMER (master_dispatch_timer, "Mainloop", "Master Clock", "Master clock dispatch", 0); CLUTTER_STATIC_TIMER (master_event_process, "Master Clock", "Event Processing", "The time spent processing events on all stages", 0); CLUTTER_TIMER_START (_clutter_uprof_context, master_dispatch_timer); CLUTTER_NOTE (SCHEDULER, "Master clock [tick]"); clutter_threads_enter (); /* Get the time to use for this frame */ #if GLIB_CHECK_VERSION (2, 27, 3) master_clock->cur_tick = g_source_get_time (source); #else { GTimeVal source_time; g_source_get_current_time (source, &source_time); master_clock->cur_tick = source_time.tv_sec * 1000000L + source_time.tv_usec; } #endif /* We need to protect ourselves against stages being destroyed during * event handling */ stages = clutter_stage_manager_list_stages (stage_manager); g_slist_foreach (stages, (GFunc) g_object_ref, NULL); CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process); master_clock->idle = FALSE; /* Process queued events */ for (l = stages; l != NULL; l = l->next) { /* NB: If a stage is busy waiting for a swap-buffers completion then * we don't process its events so we can maximize the benefits of * motion compression, and avoid multiple picks per frame. */ if (_clutter_stage_get_pending_swaps (l->data) == 0) _clutter_stage_process_queued_events (l->data); } CLUTTER_TIMER_STOP (_clutter_uprof_context, master_event_process); _clutter_master_clock_advance (master_clock); _clutter_run_repaint_functions (); /* Update any stage that needs redraw/relayout after the clock * is advanced. */ for (l = stages; l != NULL; l = l->next) { /* If a stage has a swap-buffers pending we don't want to draw to it * in case the driver may block the CPU while it waits for the next * backbuffer to become available. * * TODO: We should be able to identify if we are running triple or N * buffered and in these cases we can still draw if there is 1 swap * pending so we can hopefully always be ready to swap for the next * vblank and really match the vsync frequency. */ if (_clutter_stage_get_pending_swaps (l->data) == 0) stages_updated |= _clutter_stage_do_update (l->data); } /* The master clock goes idle if no stages were updated and falls back * to polling for timeline progressions... */ if (!stages_updated) master_clock->idle = TRUE; g_slist_foreach (stages, (GFunc) g_object_unref, NULL); g_slist_free (stages); master_clock->prev_tick = master_clock->cur_tick; clutter_threads_leave (); CLUTTER_TIMER_STOP (_clutter_uprof_context, master_dispatch_timer); return TRUE; }
static gboolean g_fam_file_monitor_callback (gint fd, GIOCondition condition, gpointer user_data) { gint64 now = g_source_get_time (fam_source); g_mutex_lock (&fam_lock); while (FAMPending (&fam_connection)) { const gchar *child; FAMEvent ev; if (FAMNextEvent (&fam_connection, &ev) != 1) { /* The daemon died. We're in a really bad situation now * because we potentially have a bunch of request structures * outstanding which no longer make any sense to anyone. * * The best thing that we can do is do nothing. Notification * won't work anymore for this process. */ g_mutex_unlock (&fam_lock); g_warning ("Lost connection to FAM (file monitoring) service. Expect no further file monitor events."); return FALSE; } /* We expect ev.filename to be a relative path for children in a * monitored directory, and an absolute path for a monitored file * or the directory itself. */ if (ev.filename[0] != '/') child = ev.filename; else child = NULL; switch (ev.code) { case FAMAcknowledge: g_source_unref (ev.userdata); break; case FAMChanged: g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CHANGED, child, NULL, NULL, now); break; case FAMDeleted: g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_DELETED, child, NULL, NULL, now); break; case FAMCreated: g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CREATED, child, NULL, NULL, now); break; default: /* unknown type */ break; } } g_mutex_unlock (&fam_lock); return TRUE; }
static CoglBool cogl_glib_source_prepare (GSource *source, int *timeout) { CoglGLibSource *cogl_source = (CoglGLibSource *) source; CoglPollFD *poll_fds; int n_poll_fds; int64_t cogl_timeout; int age; int i; age = cogl_poll_renderer_get_info (cogl_source->renderer, &poll_fds, &n_poll_fds, &cogl_timeout); /* We have to be careful not to call g_source_add/remove_poll unless * the FDs have changed because it will cause the main loop to * immediately wake up. If we call it every time the source is * prepared it will effectively never go idle. */ if (age != cogl_source->poll_fds_age) { /* Remove any existing polls before adding the new ones */ for (i = 0; i < cogl_source->poll_fds->len; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); g_source_remove_poll (source, poll_fd); } g_array_set_size (cogl_source->poll_fds, n_poll_fds); for (i = 0; i < n_poll_fds; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); poll_fd->fd = poll_fds[i].fd; g_source_add_poll (source, poll_fd); } } cogl_source->poll_fds_age = age; /* Update the events */ for (i = 0; i < n_poll_fds; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); poll_fd->events = poll_fds[i].events; poll_fd->revents = 0; } if (cogl_timeout == -1) { *timeout = -1; cogl_source->expiration_time = -1; } else { /* Round up to ensure that we don't try again too early */ *timeout = (cogl_timeout + 999) / 1000; cogl_source->expiration_time = (g_source_get_time (source) + cogl_timeout); } return *timeout == 0; }