afb-evt: prepare futur afb_eventid
[src/app-framework-binder.git] / src / afb-evt.c
1 /*
2  * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  * Author José Bollo <jose.bollo@iot.bzh>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <pthread.h>
26
27 #include <json-c/json.h>
28 #include <afb/afb-event-itf.h>
29
30 #include "afb-evt.h"
31 #include "afb-hook.h"
32 #include "verbose.h"
33
34 struct afb_evt_watch;
35
36 /*
37  * Structure for event listeners
38  */
39 struct afb_evt_listener {
40
41         /* chaining listeners */
42         struct afb_evt_listener *next;
43
44         /* interface for callbacks */
45         const struct afb_evt_itf *itf;
46
47         /* closure for the callback */
48         void *closure;
49
50         /* head of the list of events listened */
51         struct afb_evt_watch *watchs;
52
53         /* mutex of the listener */
54         pthread_mutex_t mutex;
55
56         /* count of reference to the listener */
57         int refcount;
58 };
59
60 /*
61  * Structure for describing events
62  */
63 struct afb_evtid {
64
65         /* interface */
66         struct afb_event_itf *itf;
67
68         /* next event */
69         struct afb_evtid *next;
70
71         /* head of the list of listeners watching the event */
72         struct afb_evt_watch *watchs;
73
74         /* id of the event */
75         int id;
76
77         /* hooking */
78         int hookflags;
79
80         /* refcount */
81         int refcount;
82
83         /* mutex of the event */
84         pthread_mutex_t mutex;
85
86         /* fullname of the event */
87         char fullname[1];
88 };
89
90 /*
91  * Structure for associating events and listeners
92  */
93 struct afb_evt_watch {
94
95         /* the evtid */
96         struct afb_evtid *evtid;
97
98         /* link to the next watcher for the same evtid */
99         struct afb_evt_watch *next_by_evtid;
100
101         /* the listener */
102         struct afb_evt_listener *listener;
103
104         /* link to the next watcher for the same listener */
105         struct afb_evt_watch *next_by_listener;
106
107         /* activity */
108         unsigned activity;
109 };
110
111 /* declare functions */
112 static void evt_hooked_drop(struct afb_evtid *evtid);
113
114 /* the interface for events */
115 static struct afb_event_itf afb_evt_event_itf = {
116         .broadcast = (void*)afb_evt_evtid_broadcast,
117         .push = (void*)afb_evt_evtid_push,
118         .drop = (void*)afb_evt_evtid_unref,
119         .name = (void*)afb_evt_evtid_name
120 };
121
122 /* the interface for events */
123 static struct afb_event_itf afb_evt_hooked_event_itf = {
124         .broadcast = (void*)afb_evt_evtid_hooked_broadcast,
125         .push = (void*)afb_evt_evtid_hooked_push,
126         .drop = (void*)evt_hooked_drop,
127         .name = (void*)afb_evt_evtid_hooked_name
128 };
129
130 /* head of the list of listeners */
131 static pthread_mutex_t listeners_mutex = PTHREAD_MUTEX_INITIALIZER;
132 static struct afb_evt_listener *listeners = NULL;
133
134 /* handling id of events */
135 static pthread_mutex_t events_mutex = PTHREAD_MUTEX_INITIALIZER;
136 static struct afb_evtid *evtids = NULL;
137 static int event_id_counter = 0;
138 static int event_id_wrapped = 0;
139
140 /*
141  * Broadcasts the 'event' of 'id' with its 'obj'
142  * 'obj' is released (like json_object_put)
143  * Returns the count of listener having receive the event.
144  */
145 static int broadcast(const char *event, struct json_object *obj, int id)
146 {
147         int result;
148         struct afb_evt_listener *listener;
149
150         result = 0;
151         pthread_mutex_lock(&listeners_mutex);
152         listener = listeners;
153         while(listener) {
154                 if (listener->itf->broadcast != NULL) {
155                         listener->itf->broadcast(listener->closure, event, id, json_object_get(obj));
156                         result++;
157                 }
158                 listener = listener->next;
159         }
160         pthread_mutex_unlock(&listeners_mutex);
161         json_object_put(obj);
162         return result;
163 }
164
165 /*
166  * Broadcasts the 'event' of 'id' with its 'obj'
167  * 'obj' is released (like json_object_put)
168  * calls hooks if hookflags isn't 0
169  * Returns the count of listener having receive the event.
170  */
171 static int hooked_broadcast(const char *event, struct json_object *obj, int id, int hookflags)
172 {
173         int result;
174
175         json_object_get(obj);
176
177         if (hookflags & afb_hook_flag_evt_broadcast_before)
178                 afb_hook_evt_broadcast_before(event, id, obj);
179
180         result = broadcast(event, obj, id);
181
182         if (hookflags & afb_hook_flag_evt_broadcast_after)
183                 afb_hook_evt_broadcast_after(event, id, obj, result);
184
185         json_object_put(obj);
186
187         return result;
188 }
189
190 /*
191  * Broadcasts the event 'evtid' with its 'object'
192  * 'object' is released (like json_object_put)
193  * Returns the count of listener that received the event.
194  */
195 int afb_evt_evtid_broadcast(struct afb_evtid *evtid, struct json_object *object)
196 {
197         return broadcast(evtid->fullname, object, evtid->id);
198 }
199
200 /*
201  * Broadcasts the event 'evtid' with its 'object'
202  * 'object' is released (like json_object_put)
203  * Returns the count of listener that received the event.
204  */
205 int afb_evt_evtid_hooked_broadcast(struct afb_evtid *evtid, struct json_object *object)
206 {
207         return hooked_broadcast(evtid->fullname, object, evtid->id, evtid->hookflags);
208 }
209
210 /*
211  * Broadcasts the 'event' with its 'object'
212  * 'object' is released (like json_object_put)
213  * Returns the count of listener having receive the event.
214  */
215 int afb_evt_broadcast(const char *event, struct json_object *object)
216 {
217         return hooked_broadcast(event, object, 0, -1);
218 }
219
220 /*
221  * Pushes the event 'evtid' with 'obj' to its listeners
222  * 'obj' is released (like json_object_put)
223  * Returns the count of listener that received the event.
224  */
225 int afb_evt_evtid_push(struct afb_evtid *evtid, struct json_object *obj)
226 {
227         int result;
228         struct afb_evt_watch *watch;
229         struct afb_evt_listener *listener;
230
231         result = 0;
232         pthread_mutex_lock(&evtid->mutex);
233         watch = evtid->watchs;
234         while(watch) {
235                 listener = watch->listener;
236                 assert(listener->itf->push != NULL);
237                 if (watch->activity != 0) {
238                         listener->itf->push(listener->closure, evtid->fullname, evtid->id, json_object_get(obj));
239                         result++;
240                 }
241                 watch = watch->next_by_evtid;
242         }
243         pthread_mutex_unlock(&evtid->mutex);
244         json_object_put(obj);
245         return result;
246 }
247
248 /*
249  * Pushes the event 'evtid' with 'obj' to its listeners
250  * 'obj' is released (like json_object_put)
251  * Emits calls to hooks.
252  * Returns the count of listener taht received the event.
253  */
254 int afb_evt_evtid_hooked_push(struct afb_evtid *evtid, struct json_object *obj)
255 {
256         int result;
257
258         /* lease the object */
259         json_object_get(obj);
260
261         /* hook before push */
262         if (evtid->hookflags & afb_hook_flag_evt_push_before)
263                 afb_hook_evt_push_before(evtid->fullname, evtid->id, obj);
264
265         /* push */
266         result = afb_evt_evtid_push(evtid, obj);
267
268         /* hook after push */
269         if (evtid->hookflags & afb_hook_flag_evt_push_after)
270                 afb_hook_evt_push_after(evtid->fullname, evtid->id, obj, result);
271
272         /* release the object */
273         json_object_put(obj);
274         return result;
275 }
276
277 /*
278  * remove the 'watch'
279  */
280 static void remove_watch(struct afb_evt_watch *watch)
281 {
282         struct afb_evt_watch **prv;
283         struct afb_evtid *evtid;
284         struct afb_evt_listener *listener;
285
286         /* notify listener if needed */
287         evtid = watch->evtid;
288         listener = watch->listener;
289         if (watch->activity != 0 && listener->itf->remove != NULL)
290                 listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
291
292         /* unlink the watch for its event */
293         prv = &evtid->watchs;
294         while(*prv != watch)
295                 prv = &(*prv)->next_by_evtid;
296         *prv = watch->next_by_evtid;
297
298         /* unlink the watch for its listener */
299         prv = &listener->watchs;
300         while(*prv != watch)
301                 prv = &(*prv)->next_by_listener;
302         *prv = watch->next_by_listener;
303
304         /* recycle memory */
305         free(watch);
306 }
307
308 /*
309  * Creates an event of name 'fullname' and returns it or NULL on error.
310  */
311 struct afb_evtid *afb_evt_evtid_create(const char *fullname)
312 {
313         size_t len;
314         struct afb_evtid *evtid, *oevt;
315
316         /* allocates the event */
317         len = strlen(fullname);
318         evtid = malloc(len + sizeof * evtid);
319         if (evtid == NULL)
320                 goto error;
321
322         /* allocates the id */
323         pthread_mutex_lock(&events_mutex);
324         do {
325                 if (++event_id_counter < 0) {
326                         event_id_wrapped = 1;
327                         event_id_counter = 1024; /* heuristic: small numbers are not destroyed */
328                 }
329                 if (!event_id_wrapped)
330                         break;
331                 oevt = evtids;
332                 while(oevt != NULL && oevt->id != event_id_counter)
333                         oevt = oevt->next;
334         } while (oevt != NULL);
335
336         /* initialize the event */
337         memcpy(evtid->fullname, fullname, len + 1);
338         evtid->next = evtids;
339         evtid->watchs = NULL;
340         evtid->id = event_id_counter;
341         pthread_mutex_init(&evtid->mutex, NULL);
342         evtids = evtid;
343         evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
344         evtid->itf = evtid->hookflags ? &afb_evt_hooked_event_itf : &afb_evt_event_itf;
345         if (evtid->hookflags & afb_hook_flag_evt_create)
346                 afb_hook_evt_create(evtid->fullname, evtid->id);
347         pthread_mutex_unlock(&events_mutex);
348
349         /* returns the event */
350         return evtid;
351 error:
352         return NULL;
353 }
354
355 /*
356  * increment the reference count of the event 'evtid'
357  */
358 struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid)
359 {
360         __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED);
361         return evtid;
362 }
363
364 /*
365  * increment the reference count of the event 'evtid'
366  */
367 struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid)
368 {
369         if (evtid->hookflags & afb_hook_flag_evt_addref)
370                 afb_hook_evt_addref(evtid->fullname, evtid->id);
371         return afb_evt_evtid_addref(evtid);
372 }
373
374 /*
375  * decrement the reference count of the event 'evtid'
376  * and destroy it when the count reachs zero
377  */
378 void afb_evt_evtid_unref(struct afb_evtid *evtid)
379 {
380         int found;
381         struct afb_evtid **prv;
382         struct afb_evt_listener *listener;
383
384         if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) {
385                 /* unlinks the event if valid! */
386                 pthread_mutex_lock(&events_mutex);
387                 found = 0;
388                 prv = &evtids;
389                 while (*prv && !(found = (*prv == evtid)))
390                         prv = &(*prv)->next;
391                 if (found)
392                         *prv = evtid->next;
393                 pthread_mutex_unlock(&events_mutex);
394
395                 /* destroys the event */
396                 if (!found)
397                         ERROR("event not found");
398                 else {
399                         /* removes all watchers */
400                         while(evtid->watchs != NULL) {
401                                 listener = evtid->watchs->listener;
402                                 pthread_mutex_lock(&listener->mutex);
403                                 pthread_mutex_lock(&evtid->mutex);
404                                 remove_watch(evtid->watchs);
405                                 pthread_mutex_unlock(&evtid->mutex);
406                                 pthread_mutex_unlock(&listener->mutex);
407                         }
408
409                         /* hook */
410                         if (evtid->hookflags & afb_hook_flag_evt_drop)
411                                 afb_hook_evt_drop(evtid->fullname, evtid->id);
412
413                         /* free */
414                         pthread_mutex_destroy(&evtid->mutex);
415                         free(evtid);
416                 }
417         }
418 }
419
420 /*
421  * decrement the reference count of the event 'evtid'
422  * and destroy it when the count reachs zero
423  */
424 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
425 {
426         if (evtid->hookflags & afb_hook_flag_evt_unref)
427                 afb_hook_evt_unref(evtid->fullname, evtid->id);
428         afb_evt_evtid_unref(evtid);
429 }
430
431 static void evt_hooked_drop(struct afb_evtid *evtid)
432 {
433         if (evtid->hookflags & afb_hook_flag_evt_drop)
434                 afb_hook_evt_drop(evtid->fullname, evtid->id);
435         afb_evt_evtid_unref(evtid);
436 }
437
438 /*
439  * Returns the true name of the 'event'
440  */
441 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
442 {
443         return evtid->fullname;
444 }
445
446 /*
447  * Returns the name of the 'event'
448  */
449 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
450 {
451         const char *name = strchr(evtid->fullname, '/');
452         return name ? name + 1 : evtid->fullname;
453 }
454
455 /*
456  * Returns the name associated to the event 'evtid'.
457  */
458 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
459 {
460         const char *result = afb_evt_evtid_name(evtid);
461         if (evtid->hookflags & afb_hook_flag_evt_name)
462                 afb_hook_evt_name(evtid->fullname, evtid->id, result);
463         return result;
464 }
465
466 /*
467  * Returns the id of the 'event'
468  */
469 int afb_evt_evtid_id(struct afb_evtid *evtid)
470 {
471         return evtid->id;
472 }
473
474 /*
475  * Returns an instance of the listener defined by the 'send' callback
476  * and the 'closure'.
477  * Returns NULL in case of memory depletion.
478  */
479 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
480 {
481         struct afb_evt_listener *listener;
482
483         /* search if an instance already exists */
484         pthread_mutex_lock(&listeners_mutex);
485         listener = listeners;
486         while (listener != NULL) {
487                 if (listener->itf == itf && listener->closure == closure) {
488                         listener = afb_evt_listener_addref(listener);
489                         goto found;
490                 }
491                 listener = listener->next;
492         }
493
494         /* allocates */
495         listener = calloc(1, sizeof *listener);
496         if (listener != NULL) {
497                 /* init */
498                 listener->itf = itf;
499                 listener->closure = closure;
500                 listener->watchs = NULL;
501                 listener->refcount = 1;
502                 pthread_mutex_init(&listener->mutex, NULL);
503                 listener->next = listeners;
504                 listeners = listener;
505         }
506  found:
507         pthread_mutex_unlock(&listeners_mutex);
508         return listener;
509 }
510
511 /*
512  * Increases the reference count of 'listener' and returns it
513  */
514 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
515 {
516         __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
517         return listener;
518 }
519
520 /*
521  * Decreases the reference count of the 'listener' and destroys it
522  * when no more used.
523  */
524 void afb_evt_listener_unref(struct afb_evt_listener *listener)
525 {
526         struct afb_evt_listener **prv;
527         struct afb_evtid *evtid;
528
529         if (!__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
530
531                 /* unlink the listener */
532                 pthread_mutex_lock(&listeners_mutex);
533                 prv = &listeners;
534                 while (*prv != listener)
535                         prv = &(*prv)->next;
536                 *prv = listener->next;
537                 pthread_mutex_unlock(&listeners_mutex);
538
539                 /* remove the watchers */
540                 pthread_mutex_lock(&listener->mutex);
541                 while (listener->watchs != NULL) {
542                         evtid = listener->watchs->evtid;
543                         pthread_mutex_lock(&evtid->mutex);
544                         remove_watch(listener->watchs);
545                         pthread_mutex_unlock(&evtid->mutex);
546                 }
547                 pthread_mutex_unlock(&listener->mutex);
548
549                 /* free the listener */
550                 pthread_mutex_destroy(&listener->mutex);
551                 free(listener);
552         }
553 }
554
555 /*
556  * Makes the 'listener' watching 'evtid'
557  * Returns 0 in case of success or else -1.
558  */
559 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
560 {
561         struct afb_evt_watch *watch;
562
563         /* check parameter */
564         if (listener->itf->push == NULL) {
565                 errno = EINVAL;
566                 return -1;
567         }
568
569         /* search the existing watch for the listener */
570         pthread_mutex_lock(&listener->mutex);
571         watch = listener->watchs;
572         while(watch != NULL) {
573                 if (watch->evtid == evtid)
574                         goto found;
575                 watch = watch->next_by_listener;
576         }
577
578         /* not found, allocate a new */
579         watch = malloc(sizeof *watch);
580         if (watch == NULL) {
581                 pthread_mutex_unlock(&listener->mutex);
582                 errno = ENOMEM;
583                 return -1;
584         }
585
586         /* initialise and link */
587         watch->evtid = evtid;
588         watch->activity = 0;
589         watch->listener = listener;
590         watch->next_by_listener = listener->watchs;
591         listener->watchs = watch;
592         pthread_mutex_lock(&evtid->mutex);
593         watch->next_by_evtid = evtid->watchs;
594         evtid->watchs = watch;
595         pthread_mutex_unlock(&evtid->mutex);
596
597 found:
598         if (watch->activity == 0 && listener->itf->add != NULL)
599                 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
600         watch->activity++;
601         pthread_mutex_unlock(&listener->mutex);
602
603         return 0;
604 }
605
606 /*
607  * Avoids the 'listener' to watch 'evtid'
608  * Returns 0 in case of success or else -1.
609  */
610 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
611 {
612         struct afb_evt_watch *watch;
613
614         /* search the existing watch */
615         pthread_mutex_lock(&listener->mutex);
616         watch = listener->watchs;
617         while(watch != NULL) {
618                 if (watch->evtid == evtid) {
619                         if (watch->activity != 0) {
620                                 watch->activity--;
621                                 if (watch->activity == 0 && listener->itf->remove != NULL)
622                                         listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
623                         }
624                         pthread_mutex_unlock(&listener->mutex);
625                         return 0;
626                 }
627                 watch = watch->next_by_listener;
628         }
629         pthread_mutex_unlock(&listener->mutex);
630         errno = ENOENT;
631         return -1;
632 }
633
634 /*
635  * update the hooks for events
636  */
637 void afb_evt_update_hooks()
638 {
639         struct afb_evtid *evtid;
640
641         pthread_mutex_lock(&events_mutex);
642         for (evtid = evtids ; evtid ; evtid = evtid->next) {
643                 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
644                 evtid->itf = evtid->hookflags ? &afb_evt_hooked_event_itf : &afb_evt_event_itf;
645         }
646         pthread_mutex_unlock(&events_mutex);
647 }
648
649 struct afb_evtid *afb_evt_to_evtid(struct afb_event event)
650 {
651         return (struct afb_evtid*)(event.itf == &afb_evt_hooked_event_itf ? event.closure : NULL);
652 }
653
654 struct afb_event afb_evt_from_evtid(struct afb_evtid *evtid)
655 {
656         return (struct afb_event){ .itf = evtid ? &afb_evt_hooked_event_itf : NULL, .closure = evtid };
657 }
658
659 /*
660  * Creates an event of 'fullname' and returns it.
661  * Returns an event with closure==NULL in case of error.
662  */
663 struct afb_event afb_evt_create_event(const char *fullname)
664 {
665         return afb_evt_from_evtid(afb_evt_evtid_create(fullname));
666 }
667
668 /*
669  * Returns the fullname of the 'event'
670  */
671 const char *afb_evt_event_fullname(struct afb_event event)
672 {
673         struct afb_evtid *evtid = afb_evt_to_evtid(event);
674         return evtid ? evtid->fullname : NULL;
675 }
676
677 /*
678  * Returns the id of the 'event'
679  */
680 int afb_evt_event_id(struct afb_event event)
681 {
682         struct afb_evtid *evtid = afb_evt_to_evtid(event);
683         return evtid ? evtid->id : 0;
684 }
685
686 /*
687  * Makes the 'listener' watching 'event'
688  * Returns 0 in case of success or else -1.
689  */
690 int afb_evt_add_watch(struct afb_evt_listener *listener, struct afb_event event)
691 {
692         struct afb_evtid *evtid = afb_evt_to_evtid(event);
693
694         /* check parameter */
695         if (!evtid) {
696                 errno = EINVAL;
697                 return -1;
698         }
699
700         /* search the existing watch for the listener */
701         return afb_evt_watch_add_evtid(listener, evtid);
702 }
703
704 /*
705  * Avoids the 'listener' to watch 'event'
706  * Returns 0 in case of success or else -1.
707  */
708 int afb_evt_remove_watch(struct afb_evt_listener *listener, struct afb_event event)
709 {
710         struct afb_evtid *evtid = afb_evt_to_evtid(event);
711
712         /* check parameter */
713         if (!evtid) {
714                 errno = EINVAL;
715                 return -1;
716         }
717
718         /* search the existing watch */
719         return afb_evt_watch_sub_evtid(listener, evtid);
720 }
721
722 int afb_evt_push(struct afb_event event, struct json_object *object)
723 {
724         struct afb_evtid *evtid = afb_evt_to_evtid(event);
725         if (evtid)
726                 return afb_evt_evtid_hooked_push(evtid, object);
727         json_object_put(object);
728         return 0;
729 }
730
731 int afb_evt_unhooked_push(struct afb_event event, struct json_object *object)
732 {
733         struct afb_evtid *evtid = afb_evt_to_evtid(event);
734         if (evtid)
735                 return afb_evt_evtid_push(evtid, object);
736         json_object_put(object);
737         return 0;
738 }
739