Return FSM_NOMEM in fsm_init() if malloc() fails
[eleutheria.git] / misc / fsm / fsm.c
blob7aff9265c861b32eadc48fdbdc4c4670fe3c368e
1 #include "fsm.h"
3 #include "states.h"
4 #include "types.h"
6 #include <sys/queue.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <pthread.h>
12 /* Callback funtions prototypes */
13 static unsigned int fsm_hashf(const void *key);
14 static int fsm_cmpf(const void *arg1, const void *arg2);
15 static void fsm_printf(const void *key, const void *data);
16 static void fsm_pq_lock(const fsm_t *fsm);
17 static void fsm_pq_unlock(const fsm_t *fsm);
19 fsmret_t fsm_init(fsm_t **fsm, size_t size, unsigned int factor, unsigned int nqueues)
21 unsigned int i;
23 /* FIXME: Validate input */
25 /* Allocate memory for fsm data structure */
26 if ((*fsm = malloc(sizeof **fsm)) == NULL)
27 return FSM_NOMEM;
29 /* Allocate memory for fsm's states' hash table */
30 if (((*fsm)->sttable = malloc(sizeof *(*fsm)->sttable)) == NULL) {
31 free(*fsm);
32 return FSM_NOMEM;
35 /* Allocate memory for priority queues */
36 if (((*fsm)->pqtable = malloc(nqueues * sizeof *(*fsm)->pqtable)) == NULL) {
37 free((*fsm)->sttable);
38 free(*fsm);
39 return FSM_NOMEM;
42 /* Allocate memory for "mutex object" -- machine dependent code */
43 if (((*fsm)->mobj = malloc(sizeof(pthread_mutex_t))) == NULL) {
44 free((*fsm)->mobj);
45 free((*fsm)->sttable);
46 free(*fsm);
47 return FSM_NOMEM;
50 /* Initialize queues */
51 (*fsm)->nqueues = nqueues;
52 for (i = 0; i < nqueues; i++)
53 STAILQ_INIT(&(*fsm)->pqtable[i]);
55 /* Initialize states' hash table */
56 if (htable_init((*fsm)->sttable, size, factor,
57 fsm_hashf, fsm_cmpf, fsm_printf) == HT_NOMEM) {
58 free((*fsm)->pqtable);
59 free((*fsm)->sttable);
60 free(*fsm);
61 return FSM_NOMEM;
64 /* Machine dependent code */
65 pthread_mutex_init((pthread_mutex_t *)(*fsm)->mobj, NULL);
67 return FSM_OK;
70 fsmret_t fsm_add_state(fsm_t *fsm, unsigned int key, state_t *state)
72 /* There is no need to allocate memory for state's key,
73 since this is done in state_init() */
75 *state->st_key = key;
77 /* Insert state to hash table */
78 if (htable_insert(fsm->sttable, state->st_key, state) == HT_EXISTS)
79 return FSM_EXISTS;
81 return FSM_OK;
84 fsmret_t fsm_free(fsm_t *fsm)
86 pqhead_t *phead;
87 pqnode_t *pnode;
88 const hnode_t *pstnode;
89 unsigned int i, stpos;
91 /* Free states' table */
92 pstnode = NULL;
93 stpos = 0;
94 while ((pstnode = htable_get_next_elm(fsm->sttable, &stpos, pstnode)) != NULL)
95 state_free(pstnode->hn_data);
97 /* Shallow free */
98 htable_free_all_obj(fsm->sttable, HT_FREEKEY);
99 htable_free(fsm->sttable);
100 free(fsm->sttable);
102 /* Free queues' elements */
103 for (i = 0; i < fsm->nqueues; i++) {
104 phead = &fsm->pqtable[i];
105 while (STAILQ_FIRST(phead) != NULL) {
106 pnode = STAILQ_FIRST(phead);
107 STAILQ_REMOVE_HEAD(phead, pq_next);
108 free(pnode->data);
109 free(pnode);
113 free(fsm->pqtable);
114 free(fsm->mobj);
115 free(fsm);
117 return FSM_OK;
120 void fsm_print_states(const fsm_t *fsm)
122 htable_print(fsm->sttable);
125 fsmret_t fsm_set_state(fsm_t *fsm, unsigned int stkey)
127 state_t *state;
129 /* Does this state exist in states' hash table ? */
130 if ((state = htable_search(fsm->sttable, &stkey)) == NULL)
131 return FSM_NOTFOUND;
133 /* Set fsm to new state */
134 fsm->cstate = state;
136 return FSM_OK;
139 unsigned int fsm_get_current_state(const fsm_t *fsm)
141 return *fsm->cstate->st_key;
144 fsmret_t fsm_queue_event(fsm_t *fsm, unsigned int evtkey, void *data, size_t size, unsigned int prio)
146 pqhead_t *phead;
147 pqnode_t *pnode;
149 if (prio >= fsm->nqueues)
150 return FSM_EPRIO;
152 /* Allocate memory for new pending event */
153 if ((pnode = malloc(sizeof *pnode)) == NULL)
154 return FSM_NOMEM;
156 pnode->evtkey = evtkey;
157 pnode->prio = prio;
160 * Allocate memory for data and copy them over.
161 * Note that this strategy leads to memory fragmentation,
162 * and should be addressed with a custom memory allocator,
163 * in due time.
165 if ((pnode->data = malloc(size)) == NULL) {
166 free(pnode);
167 return FSM_NOMEM;
169 memcpy(pnode->data, data, size);
171 /* Get the head of the queue with the appropriate priority */
172 phead = &fsm->pqtable[prio];
174 /* Insert new event in tail (we serve from head) */
175 fsm_pq_lock(fsm);
176 STAILQ_INSERT_TAIL(phead, pnode, pq_next);
177 fsm_pq_unlock(fsm);
179 return FSM_OK;
181 fsmret_t fsm_dequeue_event(fsm_t *fsm)
183 pqhead_t *phead;
184 pqnode_t *pnode;
185 unsigned int i;
187 /* Scan queues starting from the one with the biggest priority */
188 i = fsm->nqueues - 1;
189 do {
190 phead = &fsm->pqtable[i];
191 if ((pnode = STAILQ_FIRST(phead)) != NULL) {
192 if (fsm_process_event(fsm, pnode->evtkey, pnode->data) == FSM_NOTFOUND) {
194 * FIXME: The event should stay in queue, if it has
195 * a sticky bit. But we haven't implemented such a bitmap
196 * in event's structure yet
200 /* Delete event */
201 pnode = STAILQ_FIRST(phead);
202 STAILQ_REMOVE_HEAD(phead, pq_next);
203 free(pnode->data); /* Argh, memory fragmentation */
204 free(pnode);
205 return FSM_OK;
207 } while (i-- != 0);
209 return FSM_EMPTY;
212 size_t fsm_get_queued_events(const fsm_t *fsm)
214 const pqhead_t *phead;
215 const pqnode_t *pnode;
216 size_t i, total;
218 total = 0;
219 for (i = 0; i < fsm->nqueues; i++) {
220 phead = &fsm->pqtable[i];
221 STAILQ_FOREACH(pnode, phead, pq_next)
222 total++;
225 return total;
228 fsmret_t fsm_process_event(fsm_t *fsm, unsigned int evtkey, void *data)
230 event_t *event;
232 /* Can the current state handle the incoming event ? */
233 if ((event = htable_search(fsm->cstate->evttable, &evtkey)) == NULL)
234 return FSM_NOTFOUND;
236 /* Execute appropriate action */
237 if (event->evt_actionf != NULL)
238 event->evt_actionf(data);
240 /* Is the transition made to an existent state ? */
241 if ((event->evt_newstate == NULL)
242 || (htable_search(fsm->sttable, event->evt_newstate->st_key) == NULL))
243 return FSM_NOTFOUND;
245 /* Set new state */
246 fsm->cstate = event->evt_newstate;
248 return FSM_OK;
251 fsmret_t fsm_validate(const fsm_t *fsm)
253 /* Is FSM empty of states ? */
254 if (htable_get_used(fsm->sttable) == 0)
255 return FSM_EMPTY;
257 return FSM_CLEAN;
260 void fsm_export_to_dot(const fsm_t *fsm, FILE *fp)
262 const hnode_t *pstnode;
263 const hnode_t *pevtnode;
264 unsigned int stpos;
265 unsigned int evtpos;
267 fprintf(fp, "digraph {\n");
269 /* Traverse all states of FSM */
270 pstnode = NULL;
271 stpos = 0;
272 while ((pstnode = htable_get_next_elm(fsm->sttable, &stpos, pstnode)) != NULL) {
274 /* Traverse all events associated with the current state */
275 pevtnode = NULL;
276 evtpos = 0;
277 while ((pevtnode = htable_get_next_elm(((state_t *)(pstnode->hn_data))->evttable, &evtpos, pevtnode)) != NULL) {
278 printf("S%u -> S%u [label=\"E%u\"]\n",
279 *(unsigned int *)pstnode->hn_key,
280 *(unsigned int *)(((event_t *)pevtnode->hn_data)->evt_newstate->st_key),
281 *(unsigned int *)pevtnode->hn_key);
285 fprintf(fp, "}\n");
289 /* Callback funtions */
290 static unsigned int fsm_hashf(const void *key)
292 return *(const unsigned int *)key;
295 static int fsm_cmpf(const void *arg1, const void *arg2)
297 unsigned int a = *(const unsigned int *)arg1;
298 unsigned int b = *(const unsigned int *)arg2;
300 if (a > b)
301 return -1;
302 else if (a == b)
303 return 0;
304 else
305 return 1;
308 static void fsm_printf(const void *key, const void *data)
310 printf("key: %u ", *(const unsigned int *)key);
313 static void fsm_pq_lock(const fsm_t *fsm)
315 /* Machine dependent code */
316 pthread_mutex_lock((pthread_mutex_t *) fsm->mobj);
319 static void fsm_pq_unlock(const fsm_t *fsm)
321 /* Machine dependent code */
322 pthread_mutex_unlock((pthread_mutex_t *) fsm->mobj);