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")
|
||||
|
||||
#define DEFAULT_TIMEOUT_MS 1000
|
||||
#define ESCAPED_CHARACTER '\\'
|
||||
|
||||
static char *
|
||||
@@ -123,6 +124,7 @@ compress_string (const gchar *str)
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_NAME,
|
||||
PROP_TIMEOUT,
|
||||
};
|
||||
|
||||
struct _WpState
|
||||
@@ -131,9 +133,11 @@ struct _WpState
|
||||
|
||||
/* Props */
|
||||
gchar *name;
|
||||
guint timeout;
|
||||
|
||||
gchar *location;
|
||||
GKeyFile *keyfile;
|
||||
GSource *timeout_source;
|
||||
WpProperties *timeout_props;
|
||||
};
|
||||
|
||||
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);
|
||||
self->name = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
self->timeout = g_value_get_uint (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -202,6 +209,9 @@ wp_state_get_property (GObject * object, guint property_id, GValue * value,
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, self->name);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
g_value_set_uint (value, self->timeout);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -215,6 +225,8 @@ wp_state_finalize (GObject * object)
|
||||
|
||||
g_clear_pointer (&self->name, 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);
|
||||
}
|
||||
@@ -222,6 +234,7 @@ wp_state_finalize (GObject * object)
|
||||
static void
|
||||
wp_state_init (WpState * self)
|
||||
{
|
||||
self->timeout = DEFAULT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -237,6 +250,11 @@ wp_state_class_init (WpStateClass * klass)
|
||||
g_param_spec_string ("name", "name",
|
||||
"The file name where the state will be stored", NULL,
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#define __WIREPLUMBER_STATE_H__
|
||||
|
||||
#include "properties.h"
|
||||
#include "core.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -38,6 +39,10 @@ void wp_state_clear (WpState *self);
|
||||
WP_API
|
||||
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
|
||||
WpProperties * wp_state_load (WpState *self);
|
||||
|
||||
|
@@ -1522,6 +1522,16 @@ state_save (lua_State *L)
|
||||
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
|
||||
state_load (lua_State *L)
|
||||
{
|
||||
@@ -1534,6 +1544,7 @@ state_load (lua_State *L)
|
||||
static const luaL_Reg state_methods[] = {
|
||||
{ "clear", state_clear },
|
||||
{ "save" , state_save },
|
||||
{ "save_after_timeout", state_save_after_timeout },
|
||||
{ "load" , state_load },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
@@ -34,7 +34,6 @@ struct _WpSettingsPlugin
|
||||
WpImplMetadata *impl_metadata;
|
||||
WpState *state;
|
||||
WpProperties *settings;
|
||||
GSource *timeout_source;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -49,46 +48,18 @@ G_DEFINE_TYPE (WpSettingsPlugin, wp_settings_plugin, WP_TYPE_PLUGIN)
|
||||
|
||||
#define NAME "sm-settings"
|
||||
#define PERSISTENT_SETTING "persistent.settings"
|
||||
#define SAVE_INTERVAL_MS 1000
|
||||
|
||||
static void
|
||||
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
|
||||
on_metadata_changed (WpMetadata *m, guint32 subject,
|
||||
const gchar *setting, const gchar *type, const gchar *new_value, gpointer 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);
|
||||
|
||||
if (!old_value) {
|
||||
@@ -102,7 +73,7 @@ on_metadata_changed (WpMetadata *m, guint32 subject,
|
||||
wp_properties_set (self->settings, setting, new_value);
|
||||
|
||||
/* update the state */
|
||||
timeout_save_settings (self, SAVE_INTERVAL_MS);
|
||||
wp_state_save_after_timeout (self->state, core, self->settings);
|
||||
}
|
||||
|
||||
WpProperties *
|
||||
@@ -220,7 +191,7 @@ on_metadata_activated (WpMetadata * m, GAsyncResult * res, gpointer user_data)
|
||||
|
||||
g_clear_pointer (&self->settings, wp_properties_unref);
|
||||
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 {
|
||||
wp_info_object (self, PERSISTENT_SETTING
|
||||
" is enabled, current saved settings will be used");
|
||||
@@ -277,10 +248,6 @@ wp_settings_plugin_disable (WpPlugin * 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_pointer (&self->settings, wp_properties_unref);
|
||||
g_clear_object (&self->state);
|
||||
|
@@ -10,7 +10,6 @@
|
||||
-- state file during the bootup, finally it has a hook which finds a default
|
||||
-- node out of the user preferences
|
||||
|
||||
cutils = require ("common-utils")
|
||||
settings = require ("settings-node")
|
||||
log = Log.open_topic ("s-default-nodes")
|
||||
|
||||
@@ -175,7 +174,7 @@ function updateStored (def_node_type, stored)
|
||||
index = index + 1
|
||||
until v == nil
|
||||
|
||||
cutils.storeAfterTimeout (state, state_table)
|
||||
state:save_after_timeout (state_table)
|
||||
end
|
||||
|
||||
function toggleState (enable)
|
||||
|
@@ -87,7 +87,7 @@ streams_om = ObjectManager {
|
||||
local function saveHeadsetProfile (device, profile_name)
|
||||
local key = "saved-headset-profile:" .. device.properties ["device.name"]
|
||||
headset_profiles [key] = profile_name
|
||||
cutils.storeAfterTimeout (state, headset_profiles)
|
||||
state:save_after_timeout (headset_profiles)
|
||||
end
|
||||
|
||||
local function getSavedHeadsetProfile (device)
|
||||
|
@@ -119,7 +119,7 @@ function updateStoredProfile (device, profile)
|
||||
end
|
||||
|
||||
state_table[dev_name] = profile.name
|
||||
cutils.storeAfterTimeout (state, state_table)
|
||||
state:save_after_timeout (state_table)
|
||||
|
||||
log:info (device, string.format (
|
||||
"stored profile '%s' (%d) for device '%s'",
|
||||
|
@@ -242,7 +242,7 @@ function saveRouteProps (dev_info, route)
|
||||
iec958Codecs = props.iec958Codecs and Json.Array (props.iec958Codecs),
|
||||
}:to_string ()
|
||||
|
||||
cutils.storeAfterTimeout (state, state_table)
|
||||
state:save_after_timeout (state_table)
|
||||
end
|
||||
|
||||
function getStoredRouteProps (dev_info, route)
|
||||
@@ -273,7 +273,7 @@ function saveProfileRoutes (dev_info, profile_name)
|
||||
if #routes > 0 then
|
||||
local key = dev_info.name .. ":profile:" .. profile_name
|
||||
state_table [key] = Json.Array (routes):to_string()
|
||||
cutils.storeAfterTimeout (state, state_table)
|
||||
state:save_after_timeout (state_table)
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -108,23 +108,6 @@ function cutils.arrayContains (a, value)
|
||||
return false
|
||||
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 ()
|
||||
return Core.get_properties()["application.name"] or "WirePlumber"
|
||||
end
|
||||
|
@@ -387,7 +387,7 @@ function saveStreamProps (key, p)
|
||||
p.channelVolumes = p.channelVolumes and Json.Array (p.channelVolumes)
|
||||
|
||||
state_table [key] = Json.Object (p):to_string ()
|
||||
cutils.storeAfterTimeout (state, state_table)
|
||||
state:save_after_timeout (state_table)
|
||||
end
|
||||
|
||||
function formKey (properties)
|
||||
|
Reference in New Issue
Block a user