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