diff options
Diffstat (limited to 'glib/examples/statemachine/statemachine.c')
-rw-r--r-- | glib/examples/statemachine/statemachine.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/glib/examples/statemachine/statemachine.c b/glib/examples/statemachine/statemachine.c new file mode 100644 index 00000000..2bde0058 --- /dev/null +++ b/glib/examples/statemachine/statemachine.c @@ -0,0 +1,351 @@ +#include <stdio.h> +#include <stdlib.h> +#include "statemachine.h" + +static void clear_pending_tasks (SMObject *object); +static void state_change (SMObject *object, SMObjectState new_state); +static void sm_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void sm_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +enum +{ + PROP_0, + PROP_NAME, +}; + +enum +{ + STATE_CHANGED, + ACQUISITION_FAILED, + ACQUISITION_PROGRESS, + LAST_SIGNAL +}; + +static guint sm_object_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(SMObject, sm_object, G_TYPE_OBJECT) + +static void +sm_object_init (SMObject *obj) +{ + obj->state = SM_OBJECT_STATE_SHUTDOWN; +} + +static void +sm_object_class_init (SMObjectClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = sm_object_set_property; + object_class->get_property = sm_object_get_property; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "name", + "name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + sm_object_signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + sm_object_signals[ACQUISITION_PROGRESS] = + g_signal_new ("acquisition-progress", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, G_TYPE_DOUBLE); + sm_object_signals[ACQUISITION_FAILED] = + g_signal_new ("acquisition-failed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GQuark +sm_error_quark (void) +{ + static GQuark ret = 0; + if (!ret) + ret = g_quark_from_static_string ("SMObjectErrorQuark"); + return ret; +} + +GType +sm_object_state_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + + ENUM_ENTRY (SM_OBJECT_STATE_SHUTDOWN, "Shutdown"), + ENUM_ENTRY (SM_OBJECT_STATE_INITIALIZED, "Loading"), + ENUM_ENTRY (SM_OBJECT_STATE_ACQUIRED, "Resource acquired"), + ENUM_ENTRY (SM_OBJECT_STATE_OPERATING, "Operating normally"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("SMObjectState", values); + } + + return etype; +} + +GType +sm_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + + ENUM_ENTRY (SM_ERROR_INVALID_STATE, "InvalidState"), + ENUM_ENTRY (SM_ERROR_NAME_IN_USE, "NameInUse"), + { 0, 0, 0 } + }; + + g_assert (SM_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("SMError", values); + } + + return etype; +} + +static void +sm_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SMObject *sm = SM_OBJECT (object); + + switch (prop_id) + { + case PROP_NAME: + sm->name = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +sm_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SMObject *sm= SM_OBJECT (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, sm->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +queue_task (SMObject *object, guint delay, GSourceFunc func) +{ + guint id; + id = g_timeout_add (delay, func, object); + object->pending_tasks = g_slist_prepend (object->pending_tasks, GUINT_TO_POINTER (id)); +} + +static gboolean +idle_state_change (gpointer data) +{ + SMObject *object = data; + + state_change (object, object->requested_state); + return FALSE; +} + +static gboolean +idle_further_acquire (gpointer data) +{ + SMObject *object = data; + + object->acquisition_progress += g_random_double_range (0.05, 0.5); + if (object->acquisition_progress > 1.0) + { + object->acquisition_progress = 1.0; + return FALSE; + } + else + { + g_signal_emit (object, sm_object_signals[ACQUISITION_PROGRESS], 0, object->acquisition_progress); + return TRUE; + } +} + +static void +clear_pending_tasks (SMObject *object) +{ + GSList *tmp; + for (tmp = object->pending_tasks; tmp; tmp = tmp->next) + g_source_remove (GPOINTER_TO_UINT (tmp->data)); + g_slist_free (object->pending_tasks); + object->pending_tasks = NULL; +} + +static const char * +state_to_string (SMObjectState state) +{ + GEnumValue *value; + GEnumClass *prop_class; + const char *ret; + + prop_class = g_type_class_ref (SM_TYPE_OBJECT_STATE); + value = g_enum_get_value (prop_class, state); + ret = value->value_nick; + + g_type_class_unref (prop_class); + return ret; +} + +static void +state_change (SMObject *object, SMObjectState new_state) +{ + g_signal_emit (object, sm_object_signals[STATE_CHANGED], 0, + state_to_string (object->state), + state_to_string (new_state)); + + clear_pending_tasks (object); + + if (new_state == SM_OBJECT_STATE_ACQUIRED) + { + object->acquisition_progress = 0.0; + queue_task (object, 1000, idle_further_acquire); + } + else if (new_state == SM_OBJECT_STATE_INITIALIZED) + { + if (g_random_int_range (0, 2) == 0) + { + object->requested_state = SM_OBJECT_STATE_ACQUIRED; + queue_task (object, 3000, idle_state_change); + } + } + + object->state = new_state; +} + +gboolean +sm_object_get_info (SMObject *object, char **name, char **state, GError **error) +{ + *name= g_strdup (object->name); + *state = g_strdup (state_to_string (object->state)); + return TRUE; +} + +gboolean +sm_object_start (SMObject *object, GError **error) +{ + if (object->state != SM_OBJECT_STATE_SHUTDOWN) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "%s", + "Can't start from non-shutdown state"); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_INITIALIZED); + return TRUE; +} + +gboolean +sm_object_shutdown (SMObject *object, GError **error) +{ + if (object->state != SM_OBJECT_STATE_INITIALIZED) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "%s", + "Can't shutdown from shutdown state"); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_SHUTDOWN); + return TRUE; +} + +gboolean +sm_object_reinitialize (SMObject *object, GError **error) +{ + if (object->state != SM_OBJECT_STATE_ACQUIRED + && object->state != SM_OBJECT_STATE_OPERATING) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "Can't reinitialize from state %d", + object->state); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_INITIALIZED); + return TRUE; +} + +gboolean +sm_object_reacquire (SMObject *object, GError **error) +{ + if (object->state != SM_OBJECT_STATE_ACQUIRED) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "Can't reacquire from state %d", + object->state); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_ACQUIRED); + return TRUE; +} + +gboolean +sm_object_get_acquiring_progress (SMObject *object, gdouble *out, GError **error) +{ + if (object->state != SM_OBJECT_STATE_ACQUIRED) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "Can't get progress from state %d", + object->state); + return FALSE; + } + *out = object->acquisition_progress; + return TRUE; +} |