This repository has been archived by the owner on Aug 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
express.c
374 lines (329 loc) · 8.15 KB
/
express.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/**
* @file express.c
* @brief Simple Express chain implementation.
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/**
* @typedef Node
* @brief Represents a linked list node.
* @see Node
*
* @struct Node
* @brief Represents doubly linked list node.
* @see node_create
*
* Use create_node function to allocate one.
*
* To free just call the **stdlib** `free()` function.
*/
typedef struct Node {
void *value; /**< Pointer to the linked list node value */
struct Node *next; /**< Pointer to the linked list next node */
struct Node *prev; /**< Pointer to the linked list previous node */
} Node;
/**
* @typedef List
* @brief Represents a doubly linked list.
* @see List
*
* @struct List
* @brief Represents a doubly linked list data structure.
* @see list_push
* @see list_shift
* @see list_clear
*
* You don't have to allocate it.
*
* But don't forget to call `list_clear(*List)` to free any remaining nodes,
* as nodes created in *heap*.
*/
typedef struct List {
Node *head; /**< aaa */
Node *tail; /**< adds */
} List;
/**
* @typedef ExpressCommand
* @brief Represents command for the Express chain execute function.
*
* @enum ExpressCommand
* @brief Represents command for the Express chain execute function.
* @see ExpressCallback
* @see express_execute
*/
typedef enum ExpressCommand {
E_CONTINUE, /**< Continue chain execution */
E_TRIGGER, /**< Trigger stop action */
} ExpressCommand;
/**
* @typedef Express
* @brief Object that stores chain of callbacks and executes them one after the
* other.
* @see Express
*
* @struct Express
* @brief Object that stores chain of callbacks and executes them one after the
* other.
*
* @see List
* @see express_create
* @see express_add
* @see express_destroy
*
* You don't have to allocate this object in heap.
* Just create it by using `Express express_create()` function.
*
* Don't forget to call `express_destory(*Express)`, this function just make
* sure that no linked list nodes remain in the heap.
*
* This object is **thread safe**.
*/
typedef struct Express {
List chain; /**< Linked list object that stores all the callback functions.*/
pthread_mutex_t lock; /**< Muxtex Lock for thread safety.*/
} Express;
/**
* @typedef ExpressCallback
* @brief A callback that passed to *express_add*.
* @param ExpressCommand Pointer to ExpressCallback function.
* @see express_add
* @see ExpressCommand
*
* @return ExpressCommand that tells the Express chain executer what to do next.
*/
typedef ExpressCommand (*ExpressCallback)(void);
/* =============== Function Prototypes ================== */
/**
* @brief Creates an empty Express object.
*
* @return An empty Express object, that is allocated on the stack.
*/
Express express_create();
/**
* @brief Adds ExpressCallback to the chain of execution.
*
* @param app Pointer to Express object.
* @param cb Pointer to ExpressCallback function to add to the chain.
*
* This function is *Thread Safe*.
*/
void express_add(Express *app, ExpressCallback cb);
/**
* @brief Executes the Express chain.
*
* @param app Pointer to Express object.
*
* This function is *Thread Safe*.
*/
void express_execute(Express *app);
/**
* @brief Cleans any heap nodes created for the chain.
*
* @param app Pointer to Express object.
*/
void express_destroy(Express *app);
/**
* @brief ExpressCallback function that prints hello.
* @see express_add
*
* @return E_CONTINUE as an ExpressCommand to continue chain exection.
*/
ExpressCommand hello_callback(void);
/**
* @brief ExpressCallback that prints out.
* @see express_add
*
* @return E_CONTINUE as an ExpressCommand to continue chain exection.
*/
ExpressCommand out_callback(void);
/**
* @brief ExpressCallback that prints trigger.
* @see express_add
*
* @return E_TRIGGER as an ExpressCommand to stop chain exection.
*/
ExpressCommand trigger_callback(void);
/* =============== Main ================== */
int main(void) {
Express app = express_create();
express_add(&app, hello_callback);
express_add(&app, trigger_callback);
express_add(&app, out_callback);
express_execute(&app);
express_destroy(&app);
return 0;
}
ExpressCommand hello_callback() {
printf("Hello\n");
return E_CONTINUE;
}
ExpressCommand out_callback() {
printf("Out\n");
return E_CONTINUE;
}
ExpressCommand trigger_callback() {
printf("Trigger\n");
return E_TRIGGER;
}
/* =============== Node Type ================== */
/**
* @brief Allocates a new Node object in heap.
*
*
* @param value Pointer to the value holded by the node.
* @param next Pointer to the next node.
* @param prev Pointer to the previous node.
* @return Pointer to the heap allocated Node object.
* @see Node
*
* Don't forget to free the Node by calling `free(*Node)`
*/
Node *node_create(void *value, Node *next, Node *prev) {
Node *node = malloc(sizeof(Node));
if (!node) {
fprintf(stderr, "Failed to allocate memory\n");
exit(EXIT_FAILURE);
}
node->value = value;
node->next = next;
node->prev = prev;
return node;
}
/* =============== List Type ================== */
/**
* @brief Pushes/Adds a new value to the list
*
* @param list Pointer to List.
* @param value Pointer to the value.
* @see List
* @see Node
* @see node_create
* @see Express::chain
*
* Creates a new node using node_create then pushes the node to the end of the
* List.
*/
void list_push(List *list, void *value) {
if (!list || !value)
return;
Node *node = node_create(value, NULL, list->tail);
if (!node)
return;
if (list->tail) {
list->tail->next = node;
list->tail = node;
} else {
list->head = node;
list->tail = node;
}
}
/**
* @brief Frees Node objects from heap.
*
* @param list Pointer to List to clear and free.
* @see List
* @see Express::chain
*
* Just freese the Node object, you must handle Node::value on your own.
*/
void list_clear(List *list) {
if (!list)
return;
list->tail = NULL;
while (list->head) {
Node *next = list->head->next;
free(list->head);
list->head = next;
}
}
/**
* @brief Pops the first Node from the beginning of the chain.
*
* @param list Pointer to the List to pop from.
* @return Pointer to the value holded by the popped Node.
* @see Node
*
* The value is never allocated or freed by the list functions.
* Do it on your own.
*
* Simple example to free all values holded by the linked list.
*
* ~~~~~~~~~~~~~~~~~~~~~{.c}
* Express app = express_create();
* ...
* void *value = NULL;
*
* while (value = list_shift(&app.chain)) {
* free(value);
* }
* ~~~~~~~~~~~~~~~~~~~~~~
*
* The previous example could fail if a value was set as *NULL*.
* So, implement your types well.
*/
void *list_shift(List *list) {
if (!list || !list->tail)
return NULL;
Node *node = list->head;
if (list->head == list->tail)
list->head = list->tail = NULL;
else {
node->next->prev = NULL;
list->head = node->next;
}
void *value = node->value;
free(node);
return value;
}
/* =============== Express ================== */
Express express_create() {
Express app = {0};
if (pthread_mutex_init(&app.lock, NULL) != 0) {
fprintf(stderr, "Failed to initialize lock\n");
exit(1);
}
return app;
}
void express_destroy(Express *app) {
list_clear(&app->chain);
pthread_mutex_destroy(&app->lock);
}
/**
* @brief Adds a callback to the Express chain.
*
* @param app Pointer to Express object.
* @param cb Pointer to ExpressCallback function.
*
* This function will do nothing if **app** or **cb** is **NULL**.
*/
void express_add(Express *app, ExpressCallback cb) {
if (!app || !cb)
return;
pthread_mutex_lock(&app->lock);
list_push(&app->chain, cb);
pthread_mutex_unlock(&app->lock);
}
/**
* @brief Executes Express chain of callbacks
*
* @param app Pointer to express object.
*
* Call this function after adding all callbacks to **Express**.
*
* @see Express
* @see express_add
*/
void express_execute(Express *app) {
if (!app)
return;
pthread_mutex_lock(&app->lock);
ExpressCallback cb = NULL;
ExpressCommand cmd = E_CONTINUE;
while (cmd == E_CONTINUE) {
cb = list_shift(&app->chain);
if (cb)
cmd = cb();
}
pthread_mutex_unlock(&app->lock);
}