00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include "statemachine.h"
00004
00005 static void clear_pending_tasks (SMObject *object);
00006 static void state_change (SMObject *object, SMObjectState new_state);
00007 static void sm_object_set_property (GObject *object,
00008 guint prop_id,
00009 const GValue *value,
00010 GParamSpec *pspec);
00011 static void sm_object_get_property (GObject *object,
00012 guint prop_id,
00013 GValue *value,
00014 GParamSpec *pspec);
00015 enum
00016 {
00017 PROP_0,
00018 PROP_NAME,
00019 };
00020
00021 enum
00022 {
00023 STATE_CHANGED,
00024 ACQUISITION_FAILED,
00025 ACQUISITION_PROGRESS,
00026 LAST_SIGNAL
00027 };
00028
00029 static guint sm_object_signals[LAST_SIGNAL] = { 0 };
00030
00031 G_DEFINE_TYPE(SMObject, sm_object, G_TYPE_OBJECT)
00032
00033 static void
00034 sm_object_init (SMObject *obj)
00035 {
00036 obj->state = SM_OBJECT_STATE_SHUTDOWN;
00037 }
00038
00039 static void
00040 sm_object_class_init (SMObjectClass *klass)
00041 {
00042 GObjectClass *object_class;
00043
00044 object_class = G_OBJECT_CLASS (klass);
00045
00046 object_class->set_property = sm_object_set_property;
00047 object_class->get_property = sm_object_get_property;
00048
00049 g_object_class_install_property (object_class,
00050 PROP_NAME,
00051 g_param_spec_string ("name",
00052 "name",
00053 "name",
00054 NULL,
00055 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
00056 sm_object_signals[STATE_CHANGED] =
00057 g_signal_new ("state-changed",
00058 G_OBJECT_CLASS_TYPE (klass),
00059 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
00060 0,
00061 NULL, NULL,
00062 g_cclosure_marshal_VOID__STRING,
00063 G_TYPE_NONE, 1, G_TYPE_STRING);
00064 sm_object_signals[ACQUISITION_PROGRESS] =
00065 g_signal_new ("acquisition-progress",
00066 G_OBJECT_CLASS_TYPE (klass),
00067 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
00068 0,
00069 NULL, NULL,
00070 g_cclosure_marshal_VOID__DOUBLE,
00071 G_TYPE_NONE, 1, G_TYPE_DOUBLE);
00072 sm_object_signals[ACQUISITION_FAILED] =
00073 g_signal_new ("acquisition-failed",
00074 G_OBJECT_CLASS_TYPE (klass),
00075 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
00076 0,
00077 NULL, NULL,
00078 g_cclosure_marshal_VOID__VOID,
00079 G_TYPE_NONE, 0);
00080 }
00081
00082
00083 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
00084
00085 GQuark
00086 sm_error_quark (void)
00087 {
00088 static GQuark ret = 0;
00089 if (!ret)
00090 ret = g_quark_from_static_string ("SMObjectErrorQuark");
00091 return ret;
00092 }
00093
00094 GType
00095 sm_object_state_get_type (void)
00096 {
00097 static GType etype = 0;
00098
00099 if (etype == 0)
00100 {
00101 static const GEnumValue values[] =
00102 {
00103
00104 ENUM_ENTRY (SM_OBJECT_STATE_SHUTDOWN, "Shutdown"),
00105 ENUM_ENTRY (SM_OBJECT_STATE_INITIALIZED, "Loading"),
00106 ENUM_ENTRY (SM_OBJECT_STATE_ACQUIRED, "Acquired"),
00107 ENUM_ENTRY (SM_OBJECT_STATE_OPERATING, "Operating"),
00108 { 0, 0, 0 }
00109 };
00110
00111 etype = g_enum_register_static ("SMObjectState", values);
00112 }
00113
00114 return etype;
00115 }
00116
00117 GType
00118 sm_error_get_type (void)
00119 {
00120 static GType etype = 0;
00121
00122 if (etype == 0)
00123 {
00124 static const GEnumValue values[] =
00125 {
00126
00127 ENUM_ENTRY (SM_ERROR_INVALID_STATE, "InvalidState"),
00128 ENUM_ENTRY (SM_ERROR_NAME_IN_USE, "NameInUse"),
00129 { 0, 0, 0 }
00130 };
00131
00132 g_assert (SM_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
00133
00134 etype = g_enum_register_static ("SMError", values);
00135 }
00136
00137 return etype;
00138 }
00139
00140 static void
00141 sm_object_set_property (GObject *object,
00142 guint prop_id,
00143 const GValue *value,
00144 GParamSpec *pspec)
00145 {
00146 SMObject *sm = SM_OBJECT (object);
00147
00148 switch (prop_id)
00149 {
00150 case PROP_NAME:
00151 sm->name = g_strdup (g_value_get_string (value));
00152 break;
00153 default:
00154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00155 break;
00156 }
00157 }
00158
00159 static void
00160 sm_object_get_property (GObject *object,
00161 guint prop_id,
00162 GValue *value,
00163 GParamSpec *pspec)
00164 {
00165 SMObject *sm= SM_OBJECT (object);
00166
00167 switch (prop_id)
00168 {
00169 case PROP_NAME:
00170 g_value_set_string (value, sm->name);
00171 break;
00172 default:
00173 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00174 break;
00175 }
00176 }
00177
00178 static const char *
00179 state_to_string (SMObjectState state)
00180 {
00181 GEnumValue *value;
00182 GEnumClass *prop_class;
00183 const char *ret;
00184
00185 prop_class = g_type_class_ref (SM_TYPE_OBJECT_STATE);
00186 value = g_enum_get_value (prop_class, state);
00187 ret = value->value_nick;
00188
00189 g_type_class_unref (prop_class);
00190 return ret;
00191 }
00192
00193 static void
00194 queue_task (SMObject *object, guint delay, GSourceFunc func)
00195 {
00196 guint id;
00197 id = g_timeout_add (delay, func, object);
00198 object->pending_tasks = g_slist_prepend (object->pending_tasks, GUINT_TO_POINTER (id));
00199 }
00200
00201 static gboolean
00202 idle_state_change (gpointer data)
00203 {
00204 SMObject *object = data;
00205
00206 g_print ("doing idle state change for %s to %s\n",
00207 object->name, state_to_string (object->requested_state));
00208 state_change (object, object->requested_state);
00209 return FALSE;
00210 }
00211
00212 static gboolean
00213 idle_further_acquire (gpointer data)
00214 {
00215 SMObject *object = data;
00216
00217 g_print ("doing idle acquisition for machine %s\n", object->name);
00218 object->acquisition_progress += g_random_double_range (0.20, 0.7);
00219 if (object->acquisition_progress > 1.0)
00220 {
00221 object->acquisition_progress = 1.0;
00222 return FALSE;
00223 }
00224 else
00225 {
00226 g_signal_emit (object, sm_object_signals[ACQUISITION_PROGRESS], 0, object->acquisition_progress);
00227 return TRUE;
00228 }
00229 }
00230
00231 static void
00232 clear_pending_tasks (SMObject *object)
00233 {
00234 GSList *tmp;
00235 for (tmp = object->pending_tasks; tmp; tmp = tmp->next)
00236 g_source_remove (GPOINTER_TO_UINT (tmp->data));
00237 g_slist_free (object->pending_tasks);
00238 object->pending_tasks = NULL;
00239 }
00240
00241 static void
00242 state_change (SMObject *object, SMObjectState new_state)
00243 {
00244 g_signal_emit (object, sm_object_signals[STATE_CHANGED], 0,
00245 state_to_string (new_state));
00246
00247 clear_pending_tasks (object);
00248
00249 if (new_state == SM_OBJECT_STATE_ACQUIRED)
00250 {
00251 object->acquisition_progress = 0.0;
00252 queue_task (object, 1000, idle_further_acquire);
00253 }
00254 else if (new_state == SM_OBJECT_STATE_INITIALIZED)
00255 {
00256 if (g_random_int_range (0, 2) == 0)
00257 {
00258 object->requested_state = SM_OBJECT_STATE_ACQUIRED;
00259 queue_task (object, 3000, idle_state_change);
00260 }
00261 }
00262
00263 object->state = new_state;
00264 }
00265
00266 gboolean
00267 sm_object_get_info (SMObject *object, char **name, char **state, GError **error)
00268 {
00269 *name= g_strdup (object->name);
00270 *state = g_strdup (state_to_string (object->state));
00271 return TRUE;
00272 }
00273
00274 gboolean
00275 sm_object_start (SMObject *object, GError **error)
00276 {
00277 if (object->state != SM_OBJECT_STATE_SHUTDOWN)
00278 {
00279 g_set_error (error,
00280 SM_ERROR,
00281 SM_ERROR_INVALID_STATE,
00282 "%s",
00283 "Can't start from non-shutdown state");
00284 return FALSE;
00285 }
00286 state_change (object, SM_OBJECT_STATE_INITIALIZED);
00287 return TRUE;
00288 }
00289
00290 gboolean
00291 sm_object_shutdown (SMObject *object, GError **error)
00292 {
00293 if (object->state == SM_OBJECT_STATE_SHUTDOWN)
00294 {
00295 g_set_error (error,
00296 SM_ERROR,
00297 SM_ERROR_INVALID_STATE,
00298 "%s",
00299 "Can't shutdown from shutdown state");
00300 return FALSE;
00301 }
00302 state_change (object, SM_OBJECT_STATE_SHUTDOWN);
00303 return TRUE;
00304 }
00305
00306 gboolean
00307 sm_object_reinitialize (SMObject *object, GError **error)
00308 {
00309 if (object->state != SM_OBJECT_STATE_ACQUIRED
00310 && object->state != SM_OBJECT_STATE_OPERATING)
00311 {
00312 g_set_error (error,
00313 SM_ERROR,
00314 SM_ERROR_INVALID_STATE,
00315 "Can't reinitialize from state %d",
00316 object->state);
00317 return FALSE;
00318 }
00319 state_change (object, SM_OBJECT_STATE_INITIALIZED);
00320 return TRUE;
00321 }
00322
00323 gboolean
00324 sm_object_reacquire (SMObject *object, GError **error)
00325 {
00326 if (object->state == SM_OBJECT_STATE_ACQUIRED)
00327 {
00328 g_set_error (error,
00329 SM_ERROR,
00330 SM_ERROR_INVALID_STATE,
00331 "Can't reacquire from state %d",
00332 object->state);
00333 return FALSE;
00334 }
00335 state_change (object, SM_OBJECT_STATE_ACQUIRED);
00336 return TRUE;
00337 }
00338
00339 gboolean
00340 sm_object_get_acquiring_progress (SMObject *object, gdouble *out, GError **error)
00341 {
00342 if (object->state != SM_OBJECT_STATE_ACQUIRED)
00343 {
00344 g_set_error (error,
00345 SM_ERROR,
00346 SM_ERROR_INVALID_STATE,
00347 "Can't get progress from state %d",
00348 object->state);
00349 return FALSE;
00350 }
00351 *out = object->acquisition_progress;
00352 return TRUE;
00353 }