aboutsummaryrefslogtreecommitdiff
path: root/qemu-timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu-timer.c')
-rw-r--r--qemu-timer.c97
1 files changed, 80 insertions, 17 deletions
diff --git a/qemu-timer.c b/qemu-timer.c
index 6b62e88669..e15ce477cc 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -45,6 +45,7 @@
/* timers */
typedef struct QEMUClock {
+ /* We rely on BQL to protect the timerlists */
QLIST_HEAD(, QEMUTimerList) timerlists;
NotifierList reset_notifiers;
@@ -71,6 +72,9 @@ struct QEMUTimerList {
QLIST_ENTRY(QEMUTimerList) list;
QEMUTimerListNotifyCB *notify_cb;
void *notify_opaque;
+
+ /* lightweight method to mark the end of timerlist's running */
+ QemuEvent timers_done_ev;
};
/**
@@ -99,6 +103,7 @@ QEMUTimerList *timerlist_new(QEMUClockType type,
QEMUClock *clock = qemu_clock_ptr(type);
timer_list = g_malloc0(sizeof(QEMUTimerList));
+ qemu_event_init(&timer_list->timers_done_ev, false);
timer_list->clock = clock;
timer_list->notify_cb = cb;
timer_list->notify_opaque = opaque;
@@ -143,13 +148,25 @@ void qemu_clock_notify(QEMUClockType type)
}
}
+/* Disabling the clock will wait for related timerlists to stop
+ * executing qemu_run_timers. Thus, this functions should not
+ * be used from the callback of a timer that is based on @clock.
+ * Doing so would cause a deadlock.
+ *
+ * Caller should hold BQL.
+ */
void qemu_clock_enable(QEMUClockType type, bool enabled)
{
QEMUClock *clock = qemu_clock_ptr(type);
+ QEMUTimerList *tl;
bool old = clock->enabled;
clock->enabled = enabled;
if (enabled && !old) {
qemu_clock_notify(type);
+ } else if (!enabled && old) {
+ QLIST_FOREACH(tl, &clock->timerlists, list) {
+ qemu_event_wait(&tl->timers_done_ev);
+ }
}
}
@@ -338,6 +355,34 @@ static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts)
}
}
+static bool timer_mod_ns_locked(QEMUTimerList *timer_list,
+ QEMUTimer *ts, int64_t expire_time)
+{
+ QEMUTimer **pt, *t;
+
+ /* add the timer in the sorted list */
+ pt = &timer_list->active_timers;
+ for (;;) {
+ t = *pt;
+ if (!timer_expired_ns(t, expire_time)) {
+ break;
+ }
+ pt = &t->next;
+ }
+ ts->expire_time = MAX(expire_time, 0);
+ ts->next = *pt;
+ *pt = ts;
+
+ return pt == &timer_list->active_timers;
+}
+
+static void timerlist_rearm(QEMUTimerList *timer_list)
+{
+ /* Interrupt execution to force deadline recalculation. */
+ qemu_clock_warp(timer_list->clock->type);
+ timerlist_notify(timer_list);
+}
+
/* stop a timer, but do not dealloc it */
void timer_del(QEMUTimer *ts)
{
@@ -353,30 +398,39 @@ void timer_del(QEMUTimer *ts)
void timer_mod_ns(QEMUTimer *ts, int64_t expire_time)
{
QEMUTimerList *timer_list = ts->timer_list;
- QEMUTimer **pt, *t;
+ bool rearm;
qemu_mutex_lock(&timer_list->active_timers_lock);
timer_del_locked(timer_list, ts);
+ rearm = timer_mod_ns_locked(timer_list, ts, expire_time);
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
- /* add the timer in the sorted list */
- pt = &timer_list->active_timers;
- for(;;) {
- t = *pt;
- if (!timer_expired_ns(t, expire_time)) {
- break;
+ if (rearm) {
+ timerlist_rearm(timer_list);
+ }
+}
+
+/* modify the current timer so that it will be fired when current_time
+ >= expire_time or the current deadline, whichever comes earlier.
+ The corresponding callback will be called. */
+void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time)
+{
+ QEMUTimerList *timer_list = ts->timer_list;
+ bool rearm;
+
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+ if (ts->expire_time == -1 || ts->expire_time > expire_time) {
+ if (ts->expire_time != -1) {
+ timer_del_locked(timer_list, ts);
}
- pt = &t->next;
+ rearm = timer_mod_ns_locked(timer_list, ts, expire_time);
+ } else {
+ rearm = false;
}
- ts->expire_time = MAX(expire_time, 0);
- ts->next = *pt;
- *pt = ts;
qemu_mutex_unlock(&timer_list->active_timers_lock);
- /* Rearm if necessary */
- if (pt == &timer_list->active_timers) {
- /* Interrupt execution to force deadline recalculation. */
- qemu_clock_warp(timer_list->clock->type);
- timerlist_notify(timer_list);
+ if (rearm) {
+ timerlist_rearm(timer_list);
}
}
@@ -385,6 +439,11 @@ void timer_mod(QEMUTimer *ts, int64_t expire_time)
timer_mod_ns(ts, expire_time * ts->scale);
}
+void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time)
+{
+ timer_mod_anticipate_ns(ts, expire_time * ts->scale);
+}
+
bool timer_pending(QEMUTimer *ts)
{
return ts->expire_time >= 0;
@@ -403,8 +462,9 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
QEMUTimerCB *cb;
void *opaque;
+ qemu_event_reset(&timer_list->timers_done_ev);
if (!timer_list->clock->enabled) {
- return progress;
+ goto out;
}
current_time = qemu_clock_get_ns(timer_list->clock->type);
@@ -428,6 +488,9 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
cb(opaque);
progress = true;
}
+
+out:
+ qemu_event_set(&timer_list->timers_done_ev);
return progress;
}