state: add save_after_timeout() method to replace all custom timeout code
This was a common pattern that we had in many places, so it makes sense to consolidate it.
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-state")
|
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-state")
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT_MS 1000
|
||||||
#define ESCAPED_CHARACTER '\\'
|
#define ESCAPED_CHARACTER '\\'
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
@@ -123,6 +124,7 @@ compress_string (const gchar *str)
|
|||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_NAME,
|
PROP_NAME,
|
||||||
|
PROP_TIMEOUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _WpState
|
struct _WpState
|
||||||
@@ -131,9 +133,11 @@ struct _WpState
|
|||||||
|
|
||||||
/* Props */
|
/* Props */
|
||||||
gchar *name;
|
gchar *name;
|
||||||
|
guint timeout;
|
||||||
|
|
||||||
gchar *location;
|
gchar *location;
|
||||||
GKeyFile *keyfile;
|
GSource *timeout_source;
|
||||||
|
WpProperties *timeout_props;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (WpState, wp_state, G_TYPE_OBJECT)
|
G_DEFINE_TYPE (WpState, wp_state, G_TYPE_OBJECT)
|
||||||
@@ -186,6 +190,9 @@ wp_state_set_property (GObject * object, guint property_id,
|
|||||||
g_clear_pointer (&self->name, g_free);
|
g_clear_pointer (&self->name, g_free);
|
||||||
self->name = g_value_dup_string (value);
|
self->name = g_value_dup_string (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_TIMEOUT:
|
||||||
|
self->timeout = g_value_get_uint (value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -202,6 +209,9 @@ wp_state_get_property (GObject * object, guint property_id, GValue * value,
|
|||||||
case PROP_NAME:
|
case PROP_NAME:
|
||||||
g_value_set_string (value, self->name);
|
g_value_set_string (value, self->name);
|
||||||
break;
|
break;
|
||||||
|
case PROP_TIMEOUT:
|
||||||
|
g_value_set_uint (value, self->timeout);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -215,6 +225,8 @@ wp_state_finalize (GObject * object)
|
|||||||
|
|
||||||
g_clear_pointer (&self->name, g_free);
|
g_clear_pointer (&self->name, g_free);
|
||||||
g_clear_pointer (&self->location, g_free);
|
g_clear_pointer (&self->location, g_free);
|
||||||
|
g_clear_pointer (&self->timeout_source, g_source_unref);
|
||||||
|
g_clear_pointer (&self->timeout_props, wp_properties_unref);
|
||||||
|
|
||||||
G_OBJECT_CLASS (wp_state_parent_class)->finalize (object);
|
G_OBJECT_CLASS (wp_state_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
@@ -222,6 +234,7 @@ wp_state_finalize (GObject * object)
|
|||||||
static void
|
static void
|
||||||
wp_state_init (WpState * self)
|
wp_state_init (WpState * self)
|
||||||
{
|
{
|
||||||
|
self->timeout = DEFAULT_TIMEOUT_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -237,6 +250,11 @@ wp_state_class_init (WpStateClass * klass)
|
|||||||
g_param_spec_string ("name", "name",
|
g_param_spec_string ("name", "name",
|
||||||
"The file name where the state will be stored", NULL,
|
"The file name where the state will be stored", NULL,
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class, PROP_TIMEOUT,
|
||||||
|
g_param_spec_uint ("timeout", "timeout",
|
||||||
|
"The timeout in milliseconds to save the state", 0, G_MAXUINT,
|
||||||
|
DEFAULT_TIMEOUT_MS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -339,6 +357,57 @@ wp_state_save (WpState *self, WpProperties *props, GError ** error)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
timeout_save_state_callback (WpState *self)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
if (!wp_state_save (self, self->timeout_props, &error))
|
||||||
|
wp_warning_object (self, "%s", error->message);
|
||||||
|
|
||||||
|
g_clear_pointer (&self->timeout_source, g_source_unref);
|
||||||
|
g_clear_pointer (&self->timeout_props, wp_properties_unref);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Saves new properties in the state, overwriting all previous data,
|
||||||
|
* after a timeout
|
||||||
|
*
|
||||||
|
* This is similar to wp_state_save() but it will save the state after a timeout
|
||||||
|
* has elapsed. If the state is saved again before the timeout elapses, the
|
||||||
|
* timeout is reset.
|
||||||
|
*
|
||||||
|
* This function is useful to avoid saving the state too often. When called
|
||||||
|
* consecutively, it will save the state only once. Every time it is called,
|
||||||
|
* it will cancel the previous timer and start a new one, resulting in timing
|
||||||
|
* out only after the last call.
|
||||||
|
*
|
||||||
|
* \ingroup wpstate
|
||||||
|
* \param self the state
|
||||||
|
* \param core the core, used to add the timeout callback to the main loop
|
||||||
|
* \param props (transfer none): the properties to save. This object will be
|
||||||
|
* referenced and kept alive until the timeout elapses, but not deep copied.
|
||||||
|
* \since 0.5.0
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
wp_state_save_after_timeout (WpState *self, WpCore *core, WpProperties *props)
|
||||||
|
{
|
||||||
|
/* Clear the current timeout callback */
|
||||||
|
if (self->timeout_source)
|
||||||
|
g_source_destroy (self->timeout_source);
|
||||||
|
g_clear_pointer (&self->timeout_source, g_source_unref);
|
||||||
|
g_clear_pointer (&self->timeout_props, wp_properties_unref);
|
||||||
|
|
||||||
|
self->timeout_props = wp_properties_ref (props);
|
||||||
|
|
||||||
|
/* Add the timeout callback */
|
||||||
|
wp_core_timeout_add_closure (core, &self->timeout_source, self->timeout,
|
||||||
|
g_cclosure_new_object (G_CALLBACK (timeout_save_state_callback),
|
||||||
|
G_OBJECT (self)));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Loads the state data from the file system
|
* \brief Loads the state data from the file system
|
||||||
*
|
*
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#define __WIREPLUMBER_STATE_H__
|
#define __WIREPLUMBER_STATE_H__
|
||||||
|
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@@ -38,6 +39,10 @@ void wp_state_clear (WpState *self);
|
|||||||
WP_API
|
WP_API
|
||||||
gboolean wp_state_save (WpState *self, WpProperties *props, GError ** error);
|
gboolean wp_state_save (WpState *self, WpProperties *props, GError ** error);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
void wp_state_save_after_timeout (WpState *self, WpCore *core,
|
||||||
|
WpProperties *props);
|
||||||
|
|
||||||
WP_API
|
WP_API
|
||||||
WpProperties * wp_state_load (WpState *self);
|
WpProperties * wp_state_load (WpState *self);
|
||||||
|
|
||||||
|
@@ -1522,6 +1522,16 @@ state_save (lua_State *L)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
state_save_after_timeout (lua_State *L)
|
||||||
|
{
|
||||||
|
WpState *state = wplua_checkobject (L, 1, WP_TYPE_STATE);
|
||||||
|
luaL_checktype (L, 2, LUA_TTABLE);
|
||||||
|
g_autoptr (WpProperties) props = wplua_table_to_properties (L, 2);
|
||||||
|
wp_state_save_after_timeout (state, get_wp_core (L), props);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
state_load (lua_State *L)
|
state_load (lua_State *L)
|
||||||
{
|
{
|
||||||
@@ -1534,6 +1544,7 @@ state_load (lua_State *L)
|
|||||||
static const luaL_Reg state_methods[] = {
|
static const luaL_Reg state_methods[] = {
|
||||||
{ "clear", state_clear },
|
{ "clear", state_clear },
|
||||||
{ "save" , state_save },
|
{ "save" , state_save },
|
||||||
|
{ "save_after_timeout", state_save_after_timeout },
|
||||||
{ "load" , state_load },
|
{ "load" , state_load },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
@@ -34,7 +34,6 @@ struct _WpSettingsPlugin
|
|||||||
WpImplMetadata *impl_metadata;
|
WpImplMetadata *impl_metadata;
|
||||||
WpState *state;
|
WpState *state;
|
||||||
WpProperties *settings;
|
WpProperties *settings;
|
||||||
GSource *timeout_source;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -49,46 +48,18 @@ G_DEFINE_TYPE (WpSettingsPlugin, wp_settings_plugin, WP_TYPE_PLUGIN)
|
|||||||
|
|
||||||
#define NAME "sm-settings"
|
#define NAME "sm-settings"
|
||||||
#define PERSISTENT_SETTING "persistent.settings"
|
#define PERSISTENT_SETTING "persistent.settings"
|
||||||
#define SAVE_INTERVAL_MS 1000
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wp_settings_plugin_init (WpSettingsPlugin * self)
|
wp_settings_plugin_init (WpSettingsPlugin * self)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
timeout_save_state_callback (WpSettingsPlugin *self)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
if (!wp_state_save (self->state, self->settings, &error))
|
|
||||||
wp_warning_object (self, "%s", error->message);
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
timeout_save_settings (WpSettingsPlugin *self, guint ms)
|
|
||||||
{
|
|
||||||
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
|
||||||
g_return_if_fail (core);
|
|
||||||
|
|
||||||
/* Clear the current timeout callback */
|
|
||||||
if (self->timeout_source)
|
|
||||||
g_source_destroy (self->timeout_source);
|
|
||||||
g_clear_pointer (&self->timeout_source, g_source_unref);
|
|
||||||
|
|
||||||
/* Add the timeout callback */
|
|
||||||
wp_core_timeout_add_closure (core, &self->timeout_source, ms,
|
|
||||||
g_cclosure_new_object (G_CALLBACK (timeout_save_state_callback),
|
|
||||||
G_OBJECT (self)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_metadata_changed (WpMetadata *m, guint32 subject,
|
on_metadata_changed (WpMetadata *m, guint32 subject,
|
||||||
const gchar *setting, const gchar *type, const gchar *new_value, gpointer d)
|
const gchar *setting, const gchar *type, const gchar *new_value, gpointer d)
|
||||||
{
|
{
|
||||||
WpSettingsPlugin *self = WP_SETTINGS_PLUGIN(d);
|
WpSettingsPlugin *self = WP_SETTINGS_PLUGIN(d);
|
||||||
|
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
|
||||||
const gchar *old_value = wp_properties_get (self->settings, setting);
|
const gchar *old_value = wp_properties_get (self->settings, setting);
|
||||||
|
|
||||||
if (!old_value) {
|
if (!old_value) {
|
||||||
@@ -102,7 +73,7 @@ on_metadata_changed (WpMetadata *m, guint32 subject,
|
|||||||
wp_properties_set (self->settings, setting, new_value);
|
wp_properties_set (self->settings, setting, new_value);
|
||||||
|
|
||||||
/* update the state */
|
/* update the state */
|
||||||
timeout_save_settings (self, SAVE_INTERVAL_MS);
|
wp_state_save_after_timeout (self->state, core, self->settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
WpProperties *
|
WpProperties *
|
||||||
@@ -220,7 +191,7 @@ on_metadata_activated (WpMetadata * m, GAsyncResult * res, gpointer user_data)
|
|||||||
|
|
||||||
g_clear_pointer (&self->settings, wp_properties_unref);
|
g_clear_pointer (&self->settings, wp_properties_unref);
|
||||||
self->settings = g_steal_pointer (&config_settings);
|
self->settings = g_steal_pointer (&config_settings);
|
||||||
timeout_save_settings (self, SAVE_INTERVAL_MS);
|
wp_state_save_after_timeout (self->state, core, self->settings);
|
||||||
} else {
|
} else {
|
||||||
wp_info_object (self, PERSISTENT_SETTING
|
wp_info_object (self, PERSISTENT_SETTING
|
||||||
" is enabled, current saved settings will be used");
|
" is enabled, current saved settings will be used");
|
||||||
@@ -277,10 +248,6 @@ wp_settings_plugin_disable (WpPlugin * plugin)
|
|||||||
{
|
{
|
||||||
WpSettingsPlugin * self = WP_SETTINGS_PLUGIN (plugin);
|
WpSettingsPlugin * self = WP_SETTINGS_PLUGIN (plugin);
|
||||||
|
|
||||||
if (self->timeout_source)
|
|
||||||
g_source_destroy (self->timeout_source);
|
|
||||||
g_clear_pointer (&self->timeout_source, g_source_unref);
|
|
||||||
|
|
||||||
g_clear_object (&self->impl_metadata);
|
g_clear_object (&self->impl_metadata);
|
||||||
g_clear_pointer (&self->settings, wp_properties_unref);
|
g_clear_pointer (&self->settings, wp_properties_unref);
|
||||||
g_clear_object (&self->state);
|
g_clear_object (&self->state);
|
||||||
|
@@ -10,7 +10,6 @@
|
|||||||
-- state file during the bootup, finally it has a hook which finds a default
|
-- state file during the bootup, finally it has a hook which finds a default
|
||||||
-- node out of the user preferences
|
-- node out of the user preferences
|
||||||
|
|
||||||
cutils = require ("common-utils")
|
|
||||||
settings = require ("settings-node")
|
settings = require ("settings-node")
|
||||||
log = Log.open_topic ("s-default-nodes")
|
log = Log.open_topic ("s-default-nodes")
|
||||||
|
|
||||||
@@ -175,7 +174,7 @@ function updateStored (def_node_type, stored)
|
|||||||
index = index + 1
|
index = index + 1
|
||||||
until v == nil
|
until v == nil
|
||||||
|
|
||||||
cutils.storeAfterTimeout (state, state_table)
|
state:save_after_timeout (state_table)
|
||||||
end
|
end
|
||||||
|
|
||||||
function toggleState (enable)
|
function toggleState (enable)
|
||||||
|
@@ -87,7 +87,7 @@ streams_om = ObjectManager {
|
|||||||
local function saveHeadsetProfile (device, profile_name)
|
local function saveHeadsetProfile (device, profile_name)
|
||||||
local key = "saved-headset-profile:" .. device.properties ["device.name"]
|
local key = "saved-headset-profile:" .. device.properties ["device.name"]
|
||||||
headset_profiles [key] = profile_name
|
headset_profiles [key] = profile_name
|
||||||
cutils.storeAfterTimeout (state, headset_profiles)
|
state:save_after_timeout (headset_profiles)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getSavedHeadsetProfile (device)
|
local function getSavedHeadsetProfile (device)
|
||||||
|
@@ -119,7 +119,7 @@ function updateStoredProfile (device, profile)
|
|||||||
end
|
end
|
||||||
|
|
||||||
state_table[dev_name] = profile.name
|
state_table[dev_name] = profile.name
|
||||||
cutils.storeAfterTimeout (state, state_table)
|
state:save_after_timeout (state_table)
|
||||||
|
|
||||||
log:info (device, string.format (
|
log:info (device, string.format (
|
||||||
"stored profile '%s' (%d) for device '%s'",
|
"stored profile '%s' (%d) for device '%s'",
|
||||||
|
@@ -242,7 +242,7 @@ function saveRouteProps (dev_info, route)
|
|||||||
iec958Codecs = props.iec958Codecs and Json.Array (props.iec958Codecs),
|
iec958Codecs = props.iec958Codecs and Json.Array (props.iec958Codecs),
|
||||||
}:to_string ()
|
}:to_string ()
|
||||||
|
|
||||||
cutils.storeAfterTimeout (state, state_table)
|
state:save_after_timeout (state_table)
|
||||||
end
|
end
|
||||||
|
|
||||||
function getStoredRouteProps (dev_info, route)
|
function getStoredRouteProps (dev_info, route)
|
||||||
@@ -273,7 +273,7 @@ function saveProfileRoutes (dev_info, profile_name)
|
|||||||
if #routes > 0 then
|
if #routes > 0 then
|
||||||
local key = dev_info.name .. ":profile:" .. profile_name
|
local key = dev_info.name .. ":profile:" .. profile_name
|
||||||
state_table [key] = Json.Array (routes):to_string()
|
state_table [key] = Json.Array (routes):to_string()
|
||||||
cutils.storeAfterTimeout (state, state_table)
|
state:save_after_timeout (state_table)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -108,23 +108,6 @@ function cutils.arrayContains (a, value)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
state_save_sources = {}
|
|
||||||
|
|
||||||
function cutils.storeAfterTimeout (state, state_table)
|
|
||||||
local state_name = state["name"]
|
|
||||||
if state_save_sources [state_name] ~= nil then
|
|
||||||
state_save_sources [state_name]:destroy ()
|
|
||||||
state_save_sources [state_name] = nil
|
|
||||||
end
|
|
||||||
state_save_sources [state_name] = Core.timeout_add (1000, function ()
|
|
||||||
local saved, err = state:save (state_table)
|
|
||||||
if not saved then
|
|
||||||
Log.warning (err)
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function cutils.get_application_name ()
|
function cutils.get_application_name ()
|
||||||
return Core.get_properties()["application.name"] or "WirePlumber"
|
return Core.get_properties()["application.name"] or "WirePlumber"
|
||||||
end
|
end
|
||||||
|
@@ -387,7 +387,7 @@ function saveStreamProps (key, p)
|
|||||||
p.channelVolumes = p.channelVolumes and Json.Array (p.channelVolumes)
|
p.channelVolumes = p.channelVolumes and Json.Array (p.channelVolumes)
|
||||||
|
|
||||||
state_table [key] = Json.Object (p):to_string ()
|
state_table [key] = Json.Object (p):to_string ()
|
||||||
cutils.storeAfterTimeout (state, state_table)
|
state:save_after_timeout (state_table)
|
||||||
end
|
end
|
||||||
|
|
||||||
function formKey (properties)
|
function formKey (properties)
|
||||||
|
Reference in New Issue
Block a user