Update date of copyright notices
[src/app-framework-binder.git] / src / afb-evt.c
1 /*
2  * Copyright (C) 2015-2018 "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  * Creates an event of name 'prefix'/'name' and returns it or NULL on error.
356  */
357 struct afb_evtid *afb_evt_evtid_create2(const char *prefix, const char *name)
358 {
359         size_t prelen, postlen;
360         char *fullname;
361
362         /* makes the event fullname */
363         prelen = strlen(prefix);
364         postlen = strlen(name);
365         fullname = alloca(prelen + postlen + 2);
366         memcpy(fullname, prefix, prelen);
367         fullname[prelen] = '/';
368         memcpy(fullname + prelen + 1, name, postlen + 1);
369
370         /* create the event */
371         return afb_evt_evtid_create(fullname);
372 }
373
374 /*
375  * increment the reference count of the event 'evtid'
376  */
377 struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid)
378 {
379         __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED);
380         return evtid;
381 }
382
383 /*
384  * increment the reference count of the event 'evtid'
385  */
386 struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid)
387 {
388         if (evtid->hookflags & afb_hook_flag_evt_addref)
389                 afb_hook_evt_addref(evtid->fullname, evtid->id);
390         return afb_evt_evtid_addref(evtid);
391 }
392
393 /*
394  * decrement the reference count of the event 'evtid'
395  * and destroy it when the count reachs zero
396  */
397 void afb_evt_evtid_unref(struct afb_evtid *evtid)
398 {
399         int found;
400         struct afb_evtid **prv;
401         struct afb_evt_listener *listener;
402
403         if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) {
404                 /* unlinks the event if valid! */
405                 pthread_mutex_lock(&events_mutex);
406                 found = 0;
407                 prv = &evtids;
408                 while (*prv && !(found = (*prv == evtid)))
409                         prv = &(*prv)->next;
410                 if (found)
411                         *prv = evtid->next;
412                 pthread_mutex_unlock(&events_mutex);
413
414                 /* destroys the event */
415                 if (!found)
416                         ERROR("event not found");
417                 else {
418                         /* removes all watchers */
419                         while(evtid->watchs != NULL) {
420                                 listener = evtid->watchs->listener;
421                                 pthread_mutex_lock(&listener->mutex);
422                                 pthread_mutex_lock(&evtid->mutex);
423                                 remove_watch(evtid->watchs);
424                                 pthread_mutex_unlock(&evtid->mutex);
425                                 pthread_mutex_unlock(&listener->mutex);
426                         }
427
428                         /* free */
429                         pthread_mutex_destroy(&evtid->mutex);
430                         free(evtid);
431                 }
432         }
433 }
434
435 /*
436  * decrement the reference count of the event 'evtid'
437  * and destroy it when the count reachs zero
438  */
439 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
440 {
441         if (evtid->hookflags & afb_hook_flag_evt_unref)
442                 afb_hook_evt_unref(evtid->fullname, evtid->id);
443         afb_evt_evtid_unref(evtid);
444 }
445
446 /*
447  * Returns the true name of the 'event'
448  */
449 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
450 {
451         return evtid->fullname;
452 }
453
454 /*
455  * Returns the name of the 'event'
456  */
457 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
458 {
459         const char *name = strchr(evtid->fullname, '/');
460         return name ? name + 1 : evtid->fullname;
461 }
462
463 /*
464  * Returns the name associated to the event 'evtid'.
465  */
466 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
467 {
468         const char *result = afb_evt_evtid_name(evtid);
469         if (evtid->hookflags & afb_hook_flag_evt_name)
470                 afb_hook_evt_name(evtid->fullname, evtid->id, result);
471         return result;
472 }
473
474 /*
475  * Returns the id of the 'event'
476  */
477 int afb_evt_evtid_id(struct afb_evtid *evtid)
478 {
479         return evtid->id;
480 }
481
482 /*
483  * Returns an instance of the listener defined by the 'send' callback
484  * and the 'closure'.
485  * Returns NULL in case of memory depletion.
486  */
487 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
488 {
489         struct afb_evt_listener *listener;
490
491         /* search if an instance already exists */
492         pthread_mutex_lock(&listeners_mutex);
493         listener = listeners;
494         while (listener != NULL) {
495                 if (listener->itf == itf && listener->closure == closure) {
496                         listener = afb_evt_listener_addref(listener);
497                         goto found;
498                 }
499                 listener = listener->next;
500         }
501
502         /* allocates */
503         listener = calloc(1, sizeof *listener);
504         if (listener != NULL) {
505                 /* init */
506                 listener->itf = itf;
507                 listener->closure = closure;
508                 listener->watchs = NULL;
509                 listener->refcount = 1;
510                 pthread_mutex_init(&listener->mutex, NULL);
511                 listener->next = listeners;
512                 listeners = listener;
513         }
514  found:
515         pthread_mutex_unlock(&listeners_mutex);
516         return listener;
517 }
518
519 /*
520  * Increases the reference count of 'listener' and returns it
521  */
522 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
523 {
524         __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
525         return listener;
526 }
527
528 /*
529  * Decreases the reference count of the 'listener' and destroys it
530  * when no more used.
531  */
532 void afb_evt_listener_unref(struct afb_evt_listener *listener)
533 {
534         struct afb_evt_listener **prv;
535         struct afb_evtid *evtid;
536
537         if (!__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
538
539                 /* unlink the listener */
540                 pthread_mutex_lock(&listeners_mutex);
541                 prv = &listeners;
542                 while (*prv != listener)
543                         prv = &(*prv)->next;
544                 *prv = listener->next;
545                 pthread_mutex_unlock(&listeners_mutex);
546
547                 /* remove the watchers */
548                 pthread_mutex_lock(&listener->mutex);
549                 while (listener->watchs != NULL) {
550                         evtid = listener->watchs->evtid;
551                         pthread_mutex_lock(&evtid->mutex);
552                         remove_watch(listener->watchs);
553                         pthread_mutex_unlock(&evtid->mutex);
554                 }
555                 pthread_mutex_unlock(&listener->mutex);
556
557                 /* free the listener */
558                 pthread_mutex_destroy(&listener->mutex);
559                 free(listener);
560         }
561 }
562
563 /*
564  * Makes the 'listener' watching 'evtid'
565  * Returns 0 in case of success or else -1.
566  */
567 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
568 {
569         struct afb_evt_watch *watch;
570
571         /* check parameter */
572         if (listener->itf->push == NULL) {
573                 errno = EINVAL;
574                 return -1;
575         }
576
577         /* search the existing watch for the listener */
578         pthread_mutex_lock(&listener->mutex);
579         watch = listener->watchs;
580         while(watch != NULL) {
581                 if (watch->evtid == evtid)
582                         goto found;
583                 watch = watch->next_by_listener;
584         }
585
586         /* not found, allocate a new */
587         watch = malloc(sizeof *watch);
588         if (watch == NULL) {
589                 pthread_mutex_unlock(&listener->mutex);
590                 errno = ENOMEM;
591                 return -1;
592         }
593
594         /* initialise and link */
595         watch->evtid = evtid;
596         watch->activity = 0;
597         watch->listener = listener;
598         watch->next_by_listener = listener->watchs;
599         listener->watchs = watch;
600         pthread_mutex_lock(&evtid->mutex);
601         watch->next_by_evtid = evtid->watchs;
602         evtid->watchs = watch;
603         pthread_mutex_unlock(&evtid->mutex);
604
605 found:
606         if (watch->activity == 0 && listener->itf->add != NULL)
607                 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
608         watch->activity++;
609         pthread_mutex_unlock(&listener->mutex);
610
611         return 0;
612 }
613
614 /*
615  * Avoids the 'listener' to watch 'evtid'
616  * Returns 0 in case of success or else -1.
617  */
618 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
619 {
620         struct afb_evt_watch *watch;
621
622         /* search the existing watch */
623         pthread_mutex_lock(&listener->mutex);
624         watch = listener->watchs;
625         while(watch != NULL) {
626                 if (watch->evtid == evtid) {
627                         if (watch->activity != 0) {
628                                 watch->activity--;
629                                 if (watch->activity == 0 && listener->itf->remove != NULL)
630                                         listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
631                         }
632                         pthread_mutex_unlock(&listener->mutex);
633                         return 0;
634                 }
635                 watch = watch->next_by_listener;
636         }
637         pthread_mutex_unlock(&listener->mutex);
638         errno = ENOENT;
639         return -1;
640 }
641
642 /*
643  * update the hooks for events
644  */
645 void afb_evt_update_hooks()
646 {
647         struct afb_evtid *evtid;
648
649         pthread_mutex_lock(&events_mutex);
650         for (evtid = evtids ; evtid ; evtid = evtid->next) {
651                 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
652                 evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_eventid_itf : &afb_evt_eventid_itf;
653         }
654         pthread_mutex_unlock(&events_mutex);
655 }
656
657 inline struct afb_evtid *afb_evt_eventid_to_evtid(struct afb_eventid *eventid)
658 {
659         return (struct afb_evtid*)eventid;
660 }
661
662 inline struct afb_eventid *afb_evt_eventid_from_evtid(struct afb_evtid *evtid)
663 {
664         return &evtid->eventid;
665 }
666
667 /*
668  * Creates an event of 'fullname' and returns it.
669  * Returns an event with closure==NULL in case of error.
670  */
671 struct afb_eventid *afb_evt_eventid_create(const char *fullname)
672 {
673         return afb_evt_eventid_from_evtid(afb_evt_evtid_create(fullname));
674 }
675
676 /*
677  * Creates an event of name 'prefix'/'name' and returns it.
678  * Returns an event with closure==NULL in case of error.
679  */
680 struct afb_eventid *afb_evt_eventid_create2(const char *prefix, const char *name)
681 {
682         return afb_evt_eventid_from_evtid(afb_evt_evtid_create2(prefix, name));
683 }
684
685 /*
686  * Returns the fullname of the 'eventid'
687  */
688 const char *afb_evt_eventid_fullname(struct afb_eventid *eventid)
689 {
690         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
691         return evtid ? evtid->fullname : NULL;
692 }
693
694 /*
695  * Returns the id of the 'eventid'
696  */
697 int afb_evt_eventid_id(struct afb_eventid *eventid)
698 {
699         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
700         return evtid ? evtid->id : 0;
701 }
702
703 /*
704  * Makes the 'listener' watching 'eventid'
705  * Returns 0 in case of success or else -1.
706  */
707 int afb_evt_eventid_add_watch(struct afb_evt_listener *listener, struct afb_eventid *eventid)
708 {
709         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
710
711         /* check parameter */
712         if (!evtid) {
713                 errno = EINVAL;
714                 return -1;
715         }
716
717         /* search the existing watch for the listener */
718         return afb_evt_watch_add_evtid(listener, evtid);
719 }
720
721 /*
722  * Avoids the 'listener' to watch 'eventid'
723  * Returns 0 in case of success or else -1.
724  */
725 int afb_evt_eventid_remove_watch(struct afb_evt_listener *listener, struct afb_eventid *eventid)
726 {
727         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
728
729         /* check parameter */
730         if (!evtid) {
731                 errno = EINVAL;
732                 return -1;
733         }
734
735         /* search the existing watch */
736         return afb_evt_watch_sub_evtid(listener, evtid);
737 }
738
739 int afb_evt_eventid_push(struct afb_eventid *eventid, struct json_object *object)
740 {
741         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
742         if (evtid)
743                 return afb_evt_evtid_hooked_push(evtid, object);
744         json_object_put(object);
745         return 0;
746 }
747
748 int afb_evt_eventid_unhooked_push(struct afb_eventid *eventid, struct json_object *object)
749 {
750         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
751         if (evtid)
752                 return afb_evt_evtid_push(evtid, object);
753         json_object_put(object);
754         return 0;
755 }
756
757 struct afb_event afb_evt_event_from_evtid(struct afb_evtid *evtid)
758 {
759         return evtid
760                 ? (struct afb_event){ .itf = &afb_evt_hooked_eventid_itf, .closure = &evtid->eventid }
761                 : (struct afb_event){ .itf = NULL, .closure = NULL };
762 }
763
764 void afb_evt_eventid_unref(struct afb_eventid *eventid)
765 {
766         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
767         if (evtid)
768                 afb_evt_evtid_unref(evtid);
769 }
770
771 struct afb_eventid *afb_evt_eventid_addref(struct afb_eventid *eventid)
772 {
773         struct afb_evtid *evtid = afb_evt_eventid_to_evtid(eventid);
774         if (evtid)
775                 afb_evt_evtid_addref(evtid);
776         return eventid;
777 }
778