Introduce 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-eventid-itf.h>
29 #include <afb/afb-event.h>
30
31 #include "afb-evt.h"
32 #include "afb-hook.h"
33 #include "verbose.h"
34
35 struct afb_evt_watch;
36
37 /*
38  * Structure for event listeners
39  */
40 struct afb_evt_listener {
41
42         /* chaining listeners */
43         struct afb_evt_listener *next;
44
45         /* interface for callbacks */
46         const struct afb_evt_itf *itf;
47
48         /* closure for the callback */
49         void *closure;
50
51         /* head of the list of events listened */
52         struct afb_evt_watch *watchs;
53
54         /* mutex of the listener */
55         pthread_mutex_t mutex;
56
57         /* count of reference to the listener */
58         int refcount;
59 };
60
61 /*
62  * Structure for describing events
63  */
64 struct afb_evtid {
65
66         /* interface */
67         struct afb_eventid eventid;
68
69         /* next event */
70         struct afb_evtid *next;
71
72         /* head of the list of listeners watching the event */
73         struct afb_evt_watch *watchs;
74
75         /* mutex of the event */
76         pthread_mutex_t mutex;
77
78         /* hooking */
79         int hookflags;
80
81         /* refcount */
82         int refcount;
83
84         /* id of the event */
85         int id;
86
87         /* fullname of the event */
88         char fullname[1];
89 };
90
91 /*
92  * Structure for associating events and listeners
93  */
94 struct afb_evt_watch {
95
96         /* the evtid */
97         struct afb_evtid *evtid;
98
99         /* link to the next watcher for the same evtid */
100         struct afb_evt_watch *next_by_evtid;
101
102         /* the listener */
103         struct afb_evt_listener *listener;
104
105         /* link to the next watcher for the same listener */
106         struct afb_evt_watch *next_by_listener;
107
108         /* activity */
109         unsigned activity;
110 };
111
112 /* the interface for events */
113 static struct afb_eventid_itf afb_evt_eventid_itf = {
114         .broadcast = (void*)afb_evt_evtid_broadcast,
115         .push = (void*)afb_evt_evtid_push,
116         .unref = (void*)afb_evt_evtid_unref,
117         .name = (void*)afb_evt_evtid_name,
118         .addref = (void*)afb_evt_evtid_addref
119 };
120
121 /* the interface for events */
122 static struct afb_eventid_itf afb_evt_hooked_eventid_itf = {
123         .broadcast = (void*)afb_evt_evtid_hooked_broadcast,
124         .push = (void*)afb_evt_evtid_hooked_push,
125         .unref = (void*)afb_evt_evtid_hooked_unref,
126         .name = (void*)afb_evt_evtid_hooked_name,
127         .addref = (void*)afb_evt_evtid_hooked_addref
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->eventid.itf = evtid->hookflags ? &afb_evt_hooked_eventid_itf : &afb_evt_eventid_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                         /* free */
410                         pthread_mutex_destroy(&evtid->mutex);
411                         free(evtid);
412                 }
413         }
414 }
415
416 /*
417  * decrement the reference count of the event 'evtid'
418  * and destroy it when the count reachs zero
419  */
420 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
421 {
422         if (evtid->hookflags & afb_hook_flag_evt_unref)
423                 afb_hook_evt_unref(evtid->fullname, evtid->id);
424         afb_evt_evtid_unref(evtid);
425 }
426
427 /*
428  * Returns the true name of the 'event'
429  */
430 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
431 {
432         return evtid->fullname;
433 }
434
435 /*
436  * Returns the name of the 'event'
437  */
438 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
439 {
440         const char *name = strchr(evtid->fullname, '/');
441         return name ? name + 1 : evtid->fullname;
442 }
443
444 /*
445  * Returns the name associated to the event 'evtid'.
446  */
447 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
448 {
449         const char *result = afb_evt_evtid_name(evtid);
450         if (evtid->hookflags & afb_hook_flag_evt_name)
451                 afb_hook_evt_name(evtid->fullname, evtid->id, result);
452         return result;
453 }
454
455 /*
456  * Returns the id of the 'event'
457  */
458 int afb_evt_evtid_id(struct afb_evtid *evtid)
459 {
460         return evtid->id;
461 }
462
463 /*
464  * Returns an instance of the listener defined by the 'send' callback
465  * and the 'closure'.
466  * Returns NULL in case of memory depletion.
467  */
468 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
469 {
470         struct afb_evt_listener *listener;
471
472         /* search if an instance already exists */
473         pthread_mutex_lock(&listeners_mutex);
474         listener = listeners;
475         while (listener != NULL) {
476                 if (listener->itf == itf && listener->closure == closure) {
477                         listener = afb_evt_listener_addref(listener);
478                         goto found;
479                 }
480                 listener = listener->next;
481         }
482
483         /* allocates */
484         listener = calloc(1, sizeof *listener);
485         if (listener != NULL) {
486                 /* init */
487                 listener->itf = itf;
488                 listener->closure = closure;
489                 listener->watchs = NULL;
490                 listener->refcount = 1;
491                 pthread_mutex_init(&listener->mutex, NULL);
492                 listener->next = listeners;
493                 listeners = listener;
494         }
495  found:
496         pthread_mutex_unlock(&listeners_mutex);
497         return listener;
498 }
499
500 /*
501  * Increases the reference count of 'listener' and returns it
502  */
503 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
504 {
505         __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
506         return listener;
507 }
508
509 /*
510  * Decreases the reference count of the 'listener' and destroys it
511  * when no more used.
512  */
513 void afb_evt_listener_unref(struct afb_evt_listener *listener)
514 {
515         struct afb_evt_listener **prv;
516         struct afb_evtid *evtid;
517
518         if (!__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
519
520                 /* unlink the listener */
521                 pthread_mutex_lock(&listeners_mutex);
522                 prv = &listeners;
523                 while (*prv != listener)
524                         prv = &(*prv)->next;
525                 *prv = listener->next;
526                 pthread_mutex_unlock(&listeners_mutex);
527
528                 /* remove the watchers */
529                 pthread_mutex_lock(&listener->mutex);
530                 while (listener->watchs != NULL) {
531                         evtid = listener->watchs->evtid;
532                         pthread_mutex_lock(&evtid->mutex);
533                         remove_watch(listener->watchs);
534                         pthread_mutex_unlock(&evtid->mutex);
535                 }
536                 pthread_mutex_unlock(&listener->mutex);
537
538                 /* free the listener */
539                 pthread_mutex_destroy(&listener->mutex);
540                 free(listener);
541         }
542 }
543
544 /*
545  * Makes the 'listener' watching 'evtid'
546  * Returns 0 in case of success or else -1.
547  */
548 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
549 {
550         struct afb_evt_watch *watch;
551
552         /* check parameter */
553         if (listener->itf->push == NULL) {
554                 errno = EINVAL;
555                 return -1;
556         }
557
558         /* search the existing watch for the listener */
559         pthread_mutex_lock(&listener->mutex);
560         watch = listener->watchs;
561         while(watch != NULL) {
562                 if (watch->evtid == evtid)
563                         goto found;
564                 watch = watch->next_by_listener;
565         }
566
567         /* not found, allocate a new */
568         watch = malloc(sizeof *watch);
569         if (watch == NULL) {
570                 pthread_mutex_unlock(&listener->mutex);
571                 errno = ENOMEM;
572                 return -1;
573         }
574
575         /* initialise and link */
576         watch->evtid = evtid;
577         watch->activity = 0;
578         watch->listener = listener;
579         watch->next_by_listener = listener->watchs;
580         listener->watchs = watch;
581         pthread_mutex_lock(&evtid->mutex);
582         watch->next_by_evtid = evtid->watchs;
583         evtid->watchs = watch;
584         pthread_mutex_unlock(&evtid->mutex);
585
586 found:
587         if (watch->activity == 0 && listener->itf->add != NULL)
588                 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
589         watch->activity++;
590         pthread_mutex_unlock(&listener->mutex);
591
592         return 0;
593 }
594
595 /*
596  * Avoids the 'listener' to watch 'evtid'
597  * Returns 0 in case of success or else -1.
598  */
599 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
600 {
601         struct afb_evt_watch *watch;
602
603         /* search the existing watch */
604         pthread_mutex_lock(&listener->mutex);
605         watch = listener->watchs;
606         while(watch != NULL) {
607                 if (watch->evtid == evtid) {
608                         if (watch->activity != 0) {
609                                 watch->activity--;
610                                 if (watch->activity == 0 && listener->itf->remove != NULL)
611                                         listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
612                         }
613                         pthread_mutex_unlock(&listener->mutex);
614                         return 0;
615                 }
616                 watch = watch->next_by_listener;
617         }
618         pthread_mutex_unlock(&listener->mutex);
619         errno = ENOENT;
620         return -1;
621 }
622
623 /*
624  * update the hooks for events
625  */
626 void afb_evt_update_hooks()
627 {
628         struct afb_evtid *evtid;
629
630         pthread_mutex_lock(&events_mutex);
631         for (evtid = evtids ; evtid ; evtid = evtid->next) {
632                 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
633                 evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_eventid_itf : &afb_evt_eventid_itf;
634         }
635         pthread_mutex_unlock(&events_mutex);
636 }
637
638 struct afb_evtid *afb_evt_to_evtid(struct afb_event event)
639 {
640         return (struct afb_evtid*)(event.itf == &afb_evt_hooked_eventid_itf ? event.closure : NULL);
641 }
642
643 struct afb_event afb_evt_from_evtid(struct afb_evtid *evtid)
644 {
645         return (struct afb_event){ .itf = evtid ? &afb_evt_hooked_eventid_itf : NULL, .closure = &evtid->eventid };
646 }
647
648 /*
649  * Creates an event of 'fullname' and returns it.
650  * Returns an event with closure==NULL in case of error.
651  */
652 struct afb_event afb_evt_create_event(const char *fullname)
653 {
654         return afb_evt_from_evtid(afb_evt_evtid_create(fullname));
655 }
656
657 /*
658  * Returns the fullname of the 'event'
659  */
660 const char *afb_evt_event_fullname(struct afb_event event)
661 {
662         struct afb_evtid *evtid = afb_evt_to_evtid(event);
663         return evtid ? evtid->fullname : NULL;
664 }
665
666 /*
667  * Returns the id of the 'event'
668  */
669 int afb_evt_event_id(struct afb_event event)
670 {
671         struct afb_evtid *evtid = afb_evt_to_evtid(event);
672         return evtid ? evtid->id : 0;
673 }
674
675 /*
676  * Makes the 'listener' watching 'event'
677  * Returns 0 in case of success or else -1.
678  */
679 int afb_evt_add_watch(struct afb_evt_listener *listener, struct afb_event event)
680 {
681         struct afb_evtid *evtid = afb_evt_to_evtid(event);
682
683         /* check parameter */
684         if (!evtid) {
685                 errno = EINVAL;
686                 return -1;
687         }
688
689         /* search the existing watch for the listener */
690         return afb_evt_watch_add_evtid(listener, evtid);
691 }
692
693 /*
694  * Avoids the 'listener' to watch 'event'
695  * Returns 0 in case of success or else -1.
696  */
697 int afb_evt_remove_watch(struct afb_evt_listener *listener, struct afb_event event)
698 {
699         struct afb_evtid *evtid = afb_evt_to_evtid(event);
700
701         /* check parameter */
702         if (!evtid) {
703                 errno = EINVAL;
704                 return -1;
705         }
706
707         /* search the existing watch */
708         return afb_evt_watch_sub_evtid(listener, evtid);
709 }
710
711 int afb_evt_push(struct afb_event event, struct json_object *object)
712 {
713         struct afb_evtid *evtid = afb_evt_to_evtid(event);
714         if (evtid)
715                 return afb_evt_evtid_hooked_push(evtid, object);
716         json_object_put(object);
717         return 0;
718 }
719
720 int afb_evt_unhooked_push(struct afb_event event, struct json_object *object)
721 {
722         struct afb_evtid *evtid = afb_evt_to_evtid(event);
723         if (evtid)
724                 return afb_evt_evtid_push(evtid, object);
725         json_object_put(object);
726         return 0;
727 }
728