mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-20 23:58:54 +02:00
Add simple dynamic scheduling to GPeriodic
GPeriodic will now try not to use more than 50% of the CPU for its own purposes unless there are no other tasks attempting to run.
This commit is contained in:
@@ -2911,7 +2911,8 @@ GPeriodic
|
|||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
g_periodic_new
|
g_periodic_new
|
||||||
g_periodic_get_hz
|
g_periodic_get_hz
|
||||||
g_periodic_get_priority
|
g_periodic_get_high_priority
|
||||||
|
g_periodic_get_low_priority
|
||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
GPeriodicTickFunc
|
GPeriodicTickFunc
|
||||||
g_periodic_add
|
g_periodic_add
|
||||||
|
@@ -1958,7 +1958,8 @@ g_simple_action_set_enabled
|
|||||||
g_periodic_block
|
g_periodic_block
|
||||||
g_periodic_damaged
|
g_periodic_damaged
|
||||||
g_periodic_get_hz
|
g_periodic_get_hz
|
||||||
g_periodic_get_priority
|
g_periodic_get_high_priority
|
||||||
|
g_periodic_get_low_priority
|
||||||
g_periodic_get_type
|
g_periodic_get_type
|
||||||
g_periodic_new
|
g_periodic_new
|
||||||
g_periodic_add
|
g_periodic_add
|
||||||
|
155
gio/gperiodic.c
155
gio/gperiodic.c
@@ -43,6 +43,44 @@
|
|||||||
* while the clock is not running, in which case the rate of repairs
|
* while the clock is not running, in which case the rate of repairs
|
||||||
* will be rate limited as if the clock were running.
|
* will be rate limited as if the clock were running.
|
||||||
*
|
*
|
||||||
|
* #GPeriodic has a configurable priority range consisting of a high and
|
||||||
|
* low value. Other sources with a priority higher than the high value
|
||||||
|
* might starve #GPeriodic and sources with the priority lower than the
|
||||||
|
* low value may be starved by #GPeriodic.
|
||||||
|
*
|
||||||
|
* #GPeriodic will engage in dynamic scheduling with respect to sources
|
||||||
|
* that have their priorities within the high to low range. A given
|
||||||
|
* #GPeriodic will ensure that the events dispatched from itself are
|
||||||
|
* generally using less than 50% of the CPU (on average) if other tasks
|
||||||
|
* are pending. If no other sources within the range are pending then
|
||||||
|
* #GPeriodic will use up to all of the available CPU (which can lead to
|
||||||
|
* starvation of lower-priority sources, as mentioned above). The 50%
|
||||||
|
* figure is entirely arbitrary and may change or become configurable in
|
||||||
|
* the future.
|
||||||
|
*
|
||||||
|
* For example, if a #GPeriodic has been set to run at 10Hz and a
|
||||||
|
* particular iteration uses 140ms of time, then 2 ticks will be
|
||||||
|
* "skipped" to give other sources a chance to run (ie: the next tick
|
||||||
|
* will occur 300ms later rather than 100ms later, giving 160ms of time
|
||||||
|
* for other sources).
|
||||||
|
*
|
||||||
|
* This means that the high priority value for #GPeriodic should be set
|
||||||
|
* quite high (above anything else) and the low priority value for
|
||||||
|
* #GPeriodic should be set lower than everything except true "idle"
|
||||||
|
* handlers (ie: things that you want to run only when the program is
|
||||||
|
* truly idle).
|
||||||
|
*
|
||||||
|
* #GPeriodic generally assumes that although the things attached to it
|
||||||
|
* may be poorly behaved in terms of non-yielding behaviour (either
|
||||||
|
* individually or in aggregate), the other sources on the main loop
|
||||||
|
* should be "well behaved". Other sources should try not to block the
|
||||||
|
* CPU for a substantial portion of the periodic interval.
|
||||||
|
*
|
||||||
|
* The sources attached to a #GPeriodic are permitted to be somewhat
|
||||||
|
* less well-behaved because they are generally rendering the UI for the
|
||||||
|
* user (which should be done smoothly) and also because they will be
|
||||||
|
* throttled by #GPeriodic.
|
||||||
|
*
|
||||||
* #GPeriodic is intended to be used as a paint clock for managing
|
* #GPeriodic is intended to be used as a paint clock for managing
|
||||||
* geometry updates and painting of windows.
|
* geometry updates and painting of windows.
|
||||||
*
|
*
|
||||||
@@ -87,6 +125,9 @@ struct _GPeriodic
|
|||||||
guint64 last_run;
|
guint64 last_run;
|
||||||
guint blocked;
|
guint blocked;
|
||||||
guint hz;
|
guint hz;
|
||||||
|
guint skip_frames;
|
||||||
|
guint stop_skip_id;
|
||||||
|
gint low_priority;
|
||||||
|
|
||||||
GSList *ticks; /* List<GPeriodicTick> */
|
GSList *ticks; /* List<GPeriodicTick> */
|
||||||
GSList *repairs; /* List<GPeriodicRepair> */
|
GSList *repairs; /* List<GPeriodicRepair> */
|
||||||
@@ -104,7 +145,8 @@ enum
|
|||||||
{
|
{
|
||||||
PROP_NONE,
|
PROP_NONE,
|
||||||
PROP_HZ,
|
PROP_HZ,
|
||||||
PROP_PRIORITY
|
PROP_HIGH_PRIORITY,
|
||||||
|
PROP_LOW_PRIORITY
|
||||||
};
|
};
|
||||||
|
|
||||||
static guint g_periodic_tick;
|
static guint g_periodic_tick;
|
||||||
@@ -116,11 +158,29 @@ g_periodic_get_microticks (GPeriodic *periodic)
|
|||||||
return g_source_get_time (periodic->source) * periodic->hz;
|
return g_source_get_time (periodic->source) * periodic->hz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_periodic_stop_skip (gpointer data)
|
||||||
|
{
|
||||||
|
GPeriodic *periodic = data;
|
||||||
|
|
||||||
|
periodic->skip_frames = 0;
|
||||||
|
|
||||||
|
g_message ("Skipping frames ends");
|
||||||
|
|
||||||
|
periodic->stop_skip_id = 0;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
g_periodic_run (GPeriodic *periodic)
|
g_periodic_run (GPeriodic *periodic)
|
||||||
{
|
{
|
||||||
|
gint64 start, usec;
|
||||||
|
|
||||||
g_assert (periodic->blocked == 0);
|
g_assert (periodic->blocked == 0);
|
||||||
|
|
||||||
|
start = g_get_monotonic_time ();
|
||||||
|
|
||||||
if (periodic->ticks)
|
if (periodic->ticks)
|
||||||
{
|
{
|
||||||
guint64 microseconds;
|
guint64 microseconds;
|
||||||
@@ -160,6 +220,43 @@ g_periodic_run (GPeriodic *periodic)
|
|||||||
g_signal_emit (periodic, g_periodic_repair, 0);
|
g_signal_emit (periodic, g_periodic_repair, 0);
|
||||||
periodic->in_repair = FALSE;
|
periodic->in_repair = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usec = g_get_monotonic_time () - start;
|
||||||
|
g_assert (usec >= 0);
|
||||||
|
|
||||||
|
/* Take the time it took to render, multiply by two and round down to
|
||||||
|
* a whole number of frames. This ensures that we don't take more
|
||||||
|
* than 50% of the CPU with rendering.
|
||||||
|
*
|
||||||
|
* Examples (at 10fps for easy math. 1 frame = 100ms):
|
||||||
|
*
|
||||||
|
* 0-49ms to render: no frames skipped
|
||||||
|
*
|
||||||
|
* We used less than half of the time to render. OK. We will run
|
||||||
|
* the next frame 100ms after this one ran (no skips).
|
||||||
|
*
|
||||||
|
* 50-99ms to render: 1 frame skipped
|
||||||
|
*
|
||||||
|
* We used more than half the time. Skip one frame so that we run
|
||||||
|
* 200ms later rather than 100ms later. We already used up to
|
||||||
|
* 99ms worth of that 200ms window, so that gives 101ms for other
|
||||||
|
* things to run (50%). For figures closer to 50ms the other
|
||||||
|
* stuff will actually get more than 50%.
|
||||||
|
*
|
||||||
|
* 100-150ms to render: 2 frames skipped, etc.
|
||||||
|
*/
|
||||||
|
periodic->skip_frames = 2 * usec * periodic->hz / 1000000;
|
||||||
|
|
||||||
|
if (periodic->skip_frames)
|
||||||
|
{
|
||||||
|
g_message ("Slow painting; (possibly) skipping %d frames\n",
|
||||||
|
periodic->skip_frames);
|
||||||
|
|
||||||
|
if (periodic->stop_skip_id == 0)
|
||||||
|
periodic->stop_skip_id = g_idle_add_full (periodic->low_priority,
|
||||||
|
g_periodic_stop_skip,
|
||||||
|
periodic, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@@ -173,7 +270,7 @@ g_periodic_prepare (GSource *source,
|
|||||||
{
|
{
|
||||||
gint64 remaining;
|
gint64 remaining;
|
||||||
|
|
||||||
remaining = periodic->last_run + 1000000 -
|
remaining = periodic->last_run + 1000000 * (1 + periodic->skip_frames) -
|
||||||
g_periodic_get_microticks (periodic);
|
g_periodic_get_microticks (periodic);
|
||||||
|
|
||||||
if (remaining > 0)
|
if (remaining > 0)
|
||||||
@@ -216,7 +313,7 @@ g_periodic_check (GSource *source)
|
|||||||
guint64 now = g_periodic_get_microticks (periodic);
|
guint64 now = g_periodic_get_microticks (periodic);
|
||||||
|
|
||||||
/* Run if it's not too soon. */
|
/* Run if it's not too soon. */
|
||||||
return !(now < periodic->last_run + 1000000);
|
return !(now < periodic->last_run + 1000000 * (periodic->skip_frames + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -271,10 +368,14 @@ g_periodic_get_property (GObject *object, guint prop_id,
|
|||||||
|
|
||||||
switch (prop_id)
|
switch (prop_id)
|
||||||
{
|
{
|
||||||
case PROP_PRIORITY:
|
case PROP_HIGH_PRIORITY:
|
||||||
g_value_set_int (value, g_source_get_priority (periodic->source));
|
g_value_set_int (value, g_source_get_priority (periodic->source));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_LOW_PRIORITY:
|
||||||
|
g_value_set_int (value, periodic->low_priority);
|
||||||
|
break;
|
||||||
|
|
||||||
case PROP_HZ:
|
case PROP_HZ:
|
||||||
g_value_set_uint (value, periodic->hz);
|
g_value_set_uint (value, periodic->hz);
|
||||||
break;
|
break;
|
||||||
@@ -292,10 +393,14 @@ g_periodic_set_property (GObject *object, guint prop_id,
|
|||||||
|
|
||||||
switch (prop_id)
|
switch (prop_id)
|
||||||
{
|
{
|
||||||
case PROP_PRIORITY:
|
case PROP_HIGH_PRIORITY:
|
||||||
g_source_set_priority (periodic->source, g_value_get_int (value));
|
g_source_set_priority (periodic->source, g_value_get_int (value));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_LOW_PRIORITY:
|
||||||
|
periodic->low_priority = g_value_get_int (value);
|
||||||
|
break;
|
||||||
|
|
||||||
case PROP_HZ:
|
case PROP_HZ:
|
||||||
periodic->hz = g_value_get_uint (value);
|
periodic->hz = g_value_get_uint (value);
|
||||||
break;
|
break;
|
||||||
@@ -351,11 +456,17 @@ g_periodic_class_init (GObjectClass *class)
|
|||||||
1, 120, 1, G_PARAM_READWRITE |
|
1, 120, 1, G_PARAM_READWRITE |
|
||||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (class, PROP_PRIORITY,
|
g_object_class_install_property (class, PROP_HIGH_PRIORITY,
|
||||||
g_param_spec_int ("priority", "priority level",
|
g_param_spec_int ("high-priority", "high priority level",
|
||||||
"the GSource priority level to run at",
|
"the GSource priority level to run at",
|
||||||
G_MININT, G_MAXINT, 0, G_PARAM_READWRITE |
|
G_MININT, G_MAXINT, 0, G_PARAM_READWRITE |
|
||||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (class, PROP_LOW_PRIORITY,
|
||||||
|
g_param_spec_int ("low-priority", "low priority level",
|
||||||
|
"ignore tasks below this priority level",
|
||||||
|
G_MININT, G_MAXINT, 0, G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -582,21 +693,39 @@ g_periodic_get_hz (GPeriodic *periodic)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_periodic_get_priority:
|
* g_periodic_get_high_priority:
|
||||||
* @periodic: a #GPeriodic clock
|
* @periodic: a #GPeriodic clock
|
||||||
*
|
*
|
||||||
* Gets the #GSource priority of the clock.
|
* Gets the #GSource priority of the clock.
|
||||||
*
|
*
|
||||||
* Returns: the priority level
|
* Returns: the high priority level
|
||||||
*
|
*
|
||||||
* Since: 2.28
|
* Since: 2.28
|
||||||
**/
|
**/
|
||||||
gint
|
gint
|
||||||
g_periodic_get_priority (GPeriodic *periodic)
|
g_periodic_get_high_priority (GPeriodic *periodic)
|
||||||
{
|
{
|
||||||
return g_source_get_priority (periodic->source);
|
return g_source_get_priority (periodic->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_periodic_get_low_priority:
|
||||||
|
* @periodic: a #GPeriodic clock
|
||||||
|
*
|
||||||
|
* Gets the priority level that #GPeriodic uses to check for mainloop
|
||||||
|
* inactivity. Other sources scheduled below this level of priority are
|
||||||
|
* effectively ignored by #GPeriodic and may be starved.
|
||||||
|
*
|
||||||
|
* Returns: the low priority level
|
||||||
|
*
|
||||||
|
* Since: 2.28
|
||||||
|
**/
|
||||||
|
gint
|
||||||
|
g_periodic_get_low_priority (GPeriodic *periodic)
|
||||||
|
{
|
||||||
|
return periodic->low_priority;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_periodic_new:
|
* g_periodic_new:
|
||||||
* @hz: the frequency of the new clock in Hz (between 1 and 120)
|
* @hz: the frequency of the new clock in Hz (between 1 and 120)
|
||||||
@@ -618,12 +747,14 @@ g_periodic_get_priority (GPeriodic *periodic)
|
|||||||
**/
|
**/
|
||||||
GPeriodic *
|
GPeriodic *
|
||||||
g_periodic_new (guint hz,
|
g_periodic_new (guint hz,
|
||||||
gint priority)
|
gint high_priority,
|
||||||
|
gint low_priority)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (1 <= hz && hz <= 120, NULL);
|
g_return_val_if_fail (1 <= hz && hz <= 120, NULL);
|
||||||
|
|
||||||
return g_object_new (G_TYPE_PERIODIC,
|
return g_object_new (G_TYPE_PERIODIC,
|
||||||
"hz", hz,
|
"hz", hz,
|
||||||
"priority", priority,
|
"high-priority", high_priority,
|
||||||
|
"low-priority", low_priority,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
@@ -45,9 +45,11 @@ typedef void (* GPeriodicRepairFunc) (GPeriod
|
|||||||
|
|
||||||
GType g_periodic_get_type (void);
|
GType g_periodic_get_type (void);
|
||||||
GPeriodic * g_periodic_new (guint hz,
|
GPeriodic * g_periodic_new (guint hz,
|
||||||
gint priority);
|
gint high_priority,
|
||||||
|
gint low_priority);
|
||||||
guint g_periodic_get_hz (GPeriodic *periodic);
|
guint g_periodic_get_hz (GPeriodic *periodic);
|
||||||
gint g_periodic_get_priority (GPeriodic *periodic);
|
gint g_periodic_get_high_priority (GPeriodic *periodic);
|
||||||
|
gint g_periodic_get_low_priority (GPeriodic *periodic);
|
||||||
|
|
||||||
guint g_periodic_add (GPeriodic *periodic,
|
guint g_periodic_add (GPeriodic *periodic,
|
||||||
GPeriodicTickFunc callback,
|
GPeriodicTickFunc callback,
|
||||||
|
Reference in New Issue
Block a user