mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-16 01:06:15 +01:00
g_timeout_*_seconds: don't overflow for large intervals
Previously, the `guint interval` parameter, measured in seconds, was multiplied by 1000 and stored in another `guint` field. For intervals greater than (G_MAXUINT / 1000) seconds, this would overflow; the timeout would fire much sooner than intended. Since GTimeoutSource already keeps track of whether it was created at millisecond or second resolution, always store the passed interval directly. We later convert the interval to microseconds, stored in a gint64, so can move the `* 1000` to there. The eagle-eyed reader might notice that there is no obvious guarantee that the source's expiration time in microseconds won't overflow the gint64, but I don't think this is a new problem. Previously, the monotonic time would have to reach (2 ** 63 - 2 ** 32) microseconds for this overflow to occur; now it would have to reach approximately (2 ** 63 - 2 ** 42) microseconds. Both of these are 292.47 millennia to 5 significant figures. Fixes #1600.
This commit is contained in:
parent
6debbbfd73
commit
4ff3734ff5
11
glib/gmain.c
11
glib/gmain.c
@ -315,6 +315,7 @@ struct _GMainLoop
|
|||||||
struct _GTimeoutSource
|
struct _GTimeoutSource
|
||||||
{
|
{
|
||||||
GSource source;
|
GSource source;
|
||||||
|
/* Measured in seconds if 'seconds' is TRUE, or milliseconds otherwise. */
|
||||||
guint interval;
|
guint interval;
|
||||||
gboolean seconds;
|
gboolean seconds;
|
||||||
};
|
};
|
||||||
@ -4615,8 +4616,6 @@ g_timeout_set_expiration (GTimeoutSource *timeout_source,
|
|||||||
{
|
{
|
||||||
gint64 expiration;
|
gint64 expiration;
|
||||||
|
|
||||||
expiration = current_time + (guint64) timeout_source->interval * 1000;
|
|
||||||
|
|
||||||
if (timeout_source->seconds)
|
if (timeout_source->seconds)
|
||||||
{
|
{
|
||||||
gint64 remainder;
|
gint64 remainder;
|
||||||
@ -4638,6 +4637,8 @@ g_timeout_set_expiration (GTimeoutSource *timeout_source,
|
|||||||
timer_perturb = 0;
|
timer_perturb = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expiration = current_time + (guint64) timeout_source->interval * 1000 * 1000;
|
||||||
|
|
||||||
/* We want the microseconds part of the timeout to land on the
|
/* We want the microseconds part of the timeout to land on the
|
||||||
* 'timer_perturb' mark, but we need to make sure we don't try to
|
* 'timer_perturb' mark, but we need to make sure we don't try to
|
||||||
* set the timeout in the past. We do this by ensuring that we
|
* set the timeout in the past. We do this by ensuring that we
|
||||||
@ -4653,6 +4654,10 @@ g_timeout_set_expiration (GTimeoutSource *timeout_source,
|
|||||||
expiration -= remainder;
|
expiration -= remainder;
|
||||||
expiration += timer_perturb;
|
expiration += timer_perturb;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
expiration = current_time + (guint64) timeout_source->interval * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
g_source_set_ready_time ((GSource *) timeout_source, expiration);
|
g_source_set_ready_time ((GSource *) timeout_source, expiration);
|
||||||
}
|
}
|
||||||
@ -4735,7 +4740,7 @@ g_timeout_source_new_seconds (guint interval)
|
|||||||
GSource *source = g_source_new (&g_timeout_funcs, sizeof (GTimeoutSource));
|
GSource *source = g_source_new (&g_timeout_funcs, sizeof (GTimeoutSource));
|
||||||
GTimeoutSource *timeout_source = (GTimeoutSource *)source;
|
GTimeoutSource *timeout_source = (GTimeoutSource *)source;
|
||||||
|
|
||||||
timeout_source->interval = 1000 * interval;
|
timeout_source->interval = interval;
|
||||||
timeout_source->seconds = TRUE;
|
timeout_source->seconds = TRUE;
|
||||||
|
|
||||||
g_timeout_set_expiration (timeout_source, g_get_monotonic_time ());
|
g_timeout_set_expiration (timeout_source, g_get_monotonic_time ());
|
||||||
|
Loading…
Reference in New Issue
Block a user