2 * Copyright (C) 2015-2019 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
26 #include <json-c/json.h>
27 #include <afb/afb-event-x2-itf.h>
28 #include <afb/afb-event-x1.h>
38 * Structure for event listeners
40 struct afb_evt_listener {
42 /* chaining listeners */
43 struct afb_evt_listener *next;
45 /* interface for callbacks */
46 const struct afb_evt_itf *itf;
48 /* closure for the callback */
51 /* head of the list of events listened */
52 struct afb_evt_watch *watchs;
54 /* rwlock of the listener */
55 pthread_rwlock_t rwlock;
57 /* count of reference to the listener */
62 * Structure for describing events
67 struct afb_event_x2 eventid;
70 struct afb_evtid *next;
72 /* head of the list of listeners watching the event */
73 struct afb_evt_watch *watchs;
75 /* rwlock of the event */
76 pthread_rwlock_t rwlock;
92 /* fullname of the event */
97 * Structure for associating events and listeners
99 struct afb_evt_watch {
102 struct afb_evtid *evtid;
104 /* link to the next watcher for the same evtid */
105 struct afb_evt_watch *next_by_evtid;
108 struct afb_evt_listener *listener;
110 /* link to the next watcher for the same listener */
111 struct afb_evt_watch *next_by_listener;
118 * structure for job of broadcasting string events
122 /** object atached to the event */
123 struct json_object *object;
125 /** name of the event to broadcast */
130 * structure for job of broadcasting or pushing events
134 /** the event to broadcast */
135 struct afb_evtid *evtid;
137 /** object atached to the event */
138 struct json_object *object;
141 /* the interface for events */
142 static struct afb_event_x2_itf afb_evt_event_x2_itf = {
143 .broadcast = (void*)afb_evt_evtid_broadcast,
144 .push = (void*)afb_evt_evtid_push,
145 .unref = (void*)afb_evt_evtid_unref,
146 .name = (void*)afb_evt_evtid_name,
147 .addref = (void*)afb_evt_evtid_addref
151 /* the interface for events */
152 static struct afb_event_x2_itf afb_evt_hooked_event_x2_itf = {
153 .broadcast = (void*)afb_evt_evtid_hooked_broadcast,
154 .push = (void*)afb_evt_evtid_hooked_push,
155 .unref = (void*)afb_evt_evtid_hooked_unref,
156 .name = (void*)afb_evt_evtid_hooked_name,
157 .addref = (void*)afb_evt_evtid_hooked_addref
161 /* job groups for events push/broadcast */
162 #define BROADCAST_JOB_GROUP (&afb_evt_event_x2_itf)
163 #define PUSH_JOB_GROUP (&afb_evt_event_x2_itf)
165 /* head of the list of listeners */
166 static pthread_rwlock_t listeners_rwlock = PTHREAD_RWLOCK_INITIALIZER;
167 static struct afb_evt_listener *listeners = NULL;
169 /* handling id of events */
170 static pthread_rwlock_t events_rwlock = PTHREAD_RWLOCK_INITIALIZER;
171 static struct afb_evtid *evtids = NULL;
172 static int event_id_counter = 0;
173 static int event_id_wrapped = 0;
176 * Create structure for job of broadcasting string 'event' with 'object'
177 * Returns the created structure or NULL if out of memory
179 static struct job_string *make_job_string(const char *event, struct json_object *object)
181 size_t sz = 1 + strlen(event);
182 struct job_string *js = malloc(sz + sizeof *js);
185 memcpy(js->event, event, sz);
191 * Destroy structure 'js' for job of broadcasting string events
193 static void destroy_job_string(struct job_string *js)
195 json_object_put(js->object);
200 * Create structure for job of broadcasting or pushing 'evtid' with 'object'
201 * Returns the created structure or NULL if out of memory
203 static struct job_evtid *make_job_evtid(struct afb_evtid *evtid, struct json_object *object)
205 struct job_evtid *je = malloc(sizeof *je);
207 je->evtid = afb_evt_evtid_addref(evtid);
214 * Destroy structure for job of broadcasting or pushing evtid
216 static void destroy_job_evtid(struct job_evtid *je)
218 afb_evt_evtid_unref(je->evtid);
219 json_object_put(je->object);
224 * Broadcasts the 'event' of 'id' with its 'object'
226 static void broadcast(const char *event, struct json_object *object, int id)
228 struct afb_evt_listener *listener;
230 pthread_rwlock_rdlock(&listeners_rwlock);
231 listener = listeners;
233 if (listener->itf->broadcast != NULL)
234 listener->itf->broadcast(listener->closure, event, id, json_object_get(object));
235 listener = listener->next;
237 pthread_rwlock_unlock(&listeners_rwlock);
241 * Jobs callback for broadcasting string asynchronously
243 static void broadcast_job_string(int signum, void *closure)
245 struct job_string *js = closure;
248 broadcast(js->event, js->object, 0);
249 destroy_job_string(js);
253 * Jobs callback for broadcasting evtid asynchronously
255 static void broadcast_job_evtid(int signum, void *closure)
257 struct job_evtid *je = closure;
260 broadcast(je->evtid->fullname, je->object, je->evtid->id);
261 destroy_job_evtid(je);
265 * Broadcasts the string 'event' with its 'object'
267 static int broadcast_string(const char *event, struct json_object *object)
269 struct job_string *js;
272 js = make_job_string(event, object);
274 ERROR("Cant't create broadcast string job item for %s(%s)",
275 event, json_object_to_json_string(object));
276 json_object_put(object);
280 rc = jobs_queue(BROADCAST_JOB_GROUP, 0, broadcast_job_string, js);
282 ERROR("cant't queue broadcast string job item for %s(%s)",
283 event, json_object_to_json_string(object));
284 destroy_job_string(js);
290 * Broadcasts the 'evtid' with its 'object'
292 static int broadcast_evtid(struct afb_evtid *evtid, struct json_object *object)
294 struct job_evtid *je;
297 je = make_job_evtid(evtid, object);
299 ERROR("Cant't create broadcast evtid job item for %s(%s)",
300 evtid->fullname, json_object_to_json_string(object));
301 json_object_put(object);
305 rc = jobs_queue(BROADCAST_JOB_GROUP, 0, broadcast_job_evtid, je);
307 ERROR("cant't queue broadcast evtid job item for %s(%s)",
308 evtid->fullname, json_object_to_json_string(object));
309 destroy_job_evtid(je);
315 * Broadcasts the event 'evtid' with its 'object'
316 * 'object' is released (like json_object_put)
317 * Returns the count of listener that received the event.
319 int afb_evt_evtid_broadcast(struct afb_evtid *evtid, struct json_object *object)
321 return broadcast_evtid(evtid, object);
326 * Broadcasts the event 'evtid' with its 'object'
327 * 'object' is released (like json_object_put)
328 * Returns the count of listener that received the event.
330 int afb_evt_evtid_hooked_broadcast(struct afb_evtid *evtid, struct json_object *object)
334 json_object_get(object);
336 if (evtid->hookflags & afb_hook_flag_evt_broadcast_before)
337 afb_hook_evt_broadcast_before(evtid->fullname, evtid->id, object);
339 result = broadcast_evtid(evtid, object);
341 if (evtid->hookflags & afb_hook_flag_evt_broadcast_after)
342 afb_hook_evt_broadcast_after(evtid->fullname, evtid->id, object, result);
344 json_object_put(object);
351 * Broadcasts the 'event' with its 'object'
352 * 'object' is released (like json_object_put)
353 * Returns the count of listener having receive the event.
355 int afb_evt_broadcast(const char *event, struct json_object *object)
360 json_object_get(object);
362 afb_hook_evt_broadcast_before(event, 0, object);
363 result = broadcast_string(event, object);
364 afb_hook_evt_broadcast_after(event, 0, object, result);
366 json_object_put(object);
370 return broadcast_string(event, object);
375 * Pushes the event 'evtid' with 'obj' to its listeners
376 * Returns the count of listener that received the event.
378 static void push_evtid(struct afb_evtid *evtid, struct json_object *object)
381 struct afb_evt_watch *watch;
382 struct afb_evt_listener *listener;
385 pthread_rwlock_rdlock(&evtid->rwlock);
386 watch = evtid->watchs;
388 listener = watch->listener;
389 assert(listener->itf->push != NULL);
390 if (watch->activity != 0) {
391 listener->itf->push(listener->closure, evtid->fullname, evtid->id, json_object_get(object));
394 watch = watch->next_by_evtid;
396 evtid->has_client = has_client;
397 pthread_rwlock_unlock(&evtid->rwlock);
401 * Jobs callback for pushing evtid asynchronously
403 static void push_job_evtid(int signum, void *closure)
405 struct job_evtid *je = closure;
408 push_evtid(je->evtid, je->object);
409 destroy_job_evtid(je);
413 * Pushes the event 'evtid' with 'obj' to its listeners
414 * 'obj' is released (like json_object_put)
415 * Returns 1 if at least one listener exists or 0 if no listener exists or
416 * -1 in case of error and the event can't be delivered
418 int afb_evt_evtid_push(struct afb_evtid *evtid, struct json_object *object)
420 struct job_evtid *je;
423 je = make_job_evtid(evtid, object);
425 ERROR("Cant't create push evtid job item for %s(%s)",
426 evtid->fullname, json_object_to_json_string(object));
427 json_object_put(object);
431 rc = jobs_queue(PUSH_JOB_GROUP, 0, push_job_evtid, je);
433 rc = evtid->has_client;
435 ERROR("cant't queue push evtid job item for %s(%s)",
436 evtid->fullname, json_object_to_json_string(object));
437 destroy_job_evtid(je);
445 * Pushes the event 'evtid' with 'obj' to its listeners
446 * 'obj' is released (like json_object_put)
447 * Emits calls to hooks.
448 * Returns the count of listener taht received the event.
450 int afb_evt_evtid_hooked_push(struct afb_evtid *evtid, struct json_object *obj)
455 /* lease the object */
456 json_object_get(obj);
458 /* hook before push */
459 if (evtid->hookflags & afb_hook_flag_evt_push_before)
460 afb_hook_evt_push_before(evtid->fullname, evtid->id, obj);
463 result = afb_evt_evtid_push(evtid, obj);
465 /* hook after push */
466 if (evtid->hookflags & afb_hook_flag_evt_push_after)
467 afb_hook_evt_push_after(evtid->fullname, evtid->id, obj, result);
469 /* release the object */
470 json_object_put(obj);
478 static void remove_watch(struct afb_evt_watch *watch)
480 struct afb_evt_watch **prv;
481 struct afb_evtid *evtid;
482 struct afb_evt_listener *listener;
484 /* notify listener if needed */
485 evtid = watch->evtid;
486 listener = watch->listener;
487 if (watch->activity != 0 && listener->itf->remove != NULL)
488 listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
490 /* unlink the watch for its event */
491 prv = &evtid->watchs;
493 prv = &(*prv)->next_by_evtid;
494 *prv = watch->next_by_evtid;
496 /* unlink the watch for its listener */
497 prv = &listener->watchs;
499 prv = &(*prv)->next_by_listener;
500 *prv = watch->next_by_listener;
507 * Creates an event of name 'fullname' and returns it or NULL on error.
509 struct afb_evtid *afb_evt_evtid_create(const char *fullname)
512 struct afb_evtid *evtid, *oevt;
514 /* allocates the event */
515 len = strlen(fullname);
516 evtid = malloc(len + 1 + sizeof * evtid);
520 /* allocates the id */
521 pthread_rwlock_wrlock(&events_rwlock);
523 if (++event_id_counter < 0) {
524 event_id_wrapped = 1;
525 event_id_counter = 1024; /* heuristic: small numbers are not destroyed */
527 if (!event_id_wrapped)
530 while(oevt != NULL && oevt->id != event_id_counter)
532 } while (oevt != NULL);
534 /* initialize the event */
535 memcpy(evtid->fullname, fullname, len + 1);
536 evtid->next = evtids;
538 evtid->watchs = NULL;
539 evtid->id = event_id_counter;
540 evtid->has_client = 0;
541 pthread_rwlock_init(&evtid->rwlock, NULL);
544 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
545 evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf;
546 if (evtid->hookflags & afb_hook_flag_evt_create)
547 afb_hook_evt_create(evtid->fullname, evtid->id);
549 evtid->eventid.itf = &afb_evt_event_x2_itf;
551 pthread_rwlock_unlock(&events_rwlock);
553 /* returns the event */
560 * Creates an event of name 'prefix'/'name' and returns it or NULL on error.
562 struct afb_evtid *afb_evt_evtid_create2(const char *prefix, const char *name)
564 size_t prelen, postlen;
567 /* makes the event fullname */
568 prelen = strlen(prefix);
569 postlen = strlen(name);
570 fullname = alloca(prelen + postlen + 2);
571 memcpy(fullname, prefix, prelen);
572 fullname[prelen] = '/';
573 memcpy(fullname + prelen + 1, name, postlen + 1);
575 /* create the event */
576 return afb_evt_evtid_create(fullname);
580 * increment the reference count of the event 'evtid'
582 struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid)
584 __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED);
590 * increment the reference count of the event 'evtid'
592 struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid)
594 if (evtid->hookflags & afb_hook_flag_evt_addref)
595 afb_hook_evt_addref(evtid->fullname, evtid->id);
596 return afb_evt_evtid_addref(evtid);
601 * decrement the reference count of the event 'evtid'
602 * and destroy it when the count reachs zero
604 void afb_evt_evtid_unref(struct afb_evtid *evtid)
607 struct afb_evtid **prv;
608 struct afb_evt_listener *listener;
610 if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) {
611 /* unlinks the event if valid! */
612 pthread_rwlock_wrlock(&events_rwlock);
615 while (*prv && !(found = (*prv == evtid)))
619 pthread_rwlock_unlock(&events_rwlock);
621 /* destroys the event */
623 ERROR("event not found");
625 /* removes all watchers */
626 while(evtid->watchs != NULL) {
627 listener = evtid->watchs->listener;
628 pthread_rwlock_wrlock(&listener->rwlock);
629 pthread_rwlock_wrlock(&evtid->rwlock);
630 remove_watch(evtid->watchs);
631 pthread_rwlock_unlock(&evtid->rwlock);
632 pthread_rwlock_unlock(&listener->rwlock);
636 pthread_rwlock_destroy(&evtid->rwlock);
644 * decrement the reference count of the event 'evtid'
645 * and destroy it when the count reachs zero
647 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
649 if (evtid->hookflags & afb_hook_flag_evt_unref)
650 afb_hook_evt_unref(evtid->fullname, evtid->id);
651 afb_evt_evtid_unref(evtid);
656 * Returns the true name of the 'event'
658 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
660 return evtid->fullname;
664 * Returns the name of the 'event'
666 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
668 const char *name = strchr(evtid->fullname, '/');
669 return name ? name + 1 : evtid->fullname;
674 * Returns the name associated to the event 'evtid'.
676 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
678 const char *result = afb_evt_evtid_name(evtid);
679 if (evtid->hookflags & afb_hook_flag_evt_name)
680 afb_hook_evt_name(evtid->fullname, evtid->id, result);
686 * Returns the id of the 'event'
688 int afb_evt_evtid_id(struct afb_evtid *evtid)
694 * Returns an instance of the listener defined by the 'send' callback
696 * Returns NULL in case of memory depletion.
698 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
700 struct afb_evt_listener *listener;
702 /* search if an instance already exists */
703 pthread_rwlock_wrlock(&listeners_rwlock);
704 listener = listeners;
705 while (listener != NULL) {
706 if (listener->itf == itf && listener->closure == closure) {
707 listener = afb_evt_listener_addref(listener);
710 listener = listener->next;
714 listener = calloc(1, sizeof *listener);
715 if (listener != NULL) {
718 listener->closure = closure;
719 listener->watchs = NULL;
720 listener->refcount = 1;
721 pthread_rwlock_init(&listener->rwlock, NULL);
722 listener->next = listeners;
723 listeners = listener;
726 pthread_rwlock_unlock(&listeners_rwlock);
731 * Increases the reference count of 'listener' and returns it
733 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
735 __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
740 * Decreases the reference count of the 'listener' and destroys it
743 void afb_evt_listener_unref(struct afb_evt_listener *listener)
745 struct afb_evt_listener **prv;
746 struct afb_evtid *evtid;
748 if (listener && !__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
750 /* unlink the listener */
751 pthread_rwlock_wrlock(&listeners_rwlock);
753 while (*prv != listener)
755 *prv = listener->next;
756 pthread_rwlock_unlock(&listeners_rwlock);
758 /* remove the watchers */
759 pthread_rwlock_wrlock(&listener->rwlock);
760 while (listener->watchs != NULL) {
761 evtid = listener->watchs->evtid;
762 pthread_rwlock_wrlock(&evtid->rwlock);
763 remove_watch(listener->watchs);
764 pthread_rwlock_unlock(&evtid->rwlock);
766 pthread_rwlock_unlock(&listener->rwlock);
768 /* free the listener */
769 pthread_rwlock_destroy(&listener->rwlock);
775 * Makes the 'listener' watching 'evtid'
776 * Returns 0 in case of success or else -1.
778 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
780 struct afb_evt_watch *watch;
782 /* check parameter */
783 if (listener->itf->push == NULL) {
788 /* search the existing watch for the listener */
789 pthread_rwlock_wrlock(&listener->rwlock);
790 watch = listener->watchs;
791 while(watch != NULL) {
792 if (watch->evtid == evtid)
794 watch = watch->next_by_listener;
797 /* not found, allocate a new */
798 watch = malloc(sizeof *watch);
800 pthread_rwlock_unlock(&listener->rwlock);
805 /* initialise and link */
806 watch->evtid = evtid;
808 watch->listener = listener;
809 watch->next_by_listener = listener->watchs;
810 listener->watchs = watch;
811 pthread_rwlock_wrlock(&evtid->rwlock);
812 watch->next_by_evtid = evtid->watchs;
813 evtid->watchs = watch;
814 pthread_rwlock_unlock(&evtid->rwlock);
817 if (watch->activity == 0 && listener->itf->add != NULL)
818 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
820 evtid->has_client = 1;
821 pthread_rwlock_unlock(&listener->rwlock);
827 * Avoids the 'listener' to watch 'evtid'
828 * Returns 0 in case of success or else -1.
830 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
832 struct afb_evt_watch *watch;
834 /* search the existing watch */
835 pthread_rwlock_wrlock(&listener->rwlock);
836 watch = listener->watchs;
837 while(watch != NULL) {
838 if (watch->evtid == evtid) {
839 if (watch->activity != 0) {
841 if (watch->activity == 0 && listener->itf->remove != NULL)
842 listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
844 pthread_rwlock_unlock(&listener->rwlock);
847 watch = watch->next_by_listener;
849 pthread_rwlock_unlock(&listener->rwlock);
856 * update the hooks for events
858 void afb_evt_update_hooks()
860 struct afb_evtid *evtid;
862 pthread_rwlock_rdlock(&events_rwlock);
863 for (evtid = evtids ; evtid ; evtid = evtid->next) {
864 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
865 evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf;
867 pthread_rwlock_unlock(&events_rwlock);
871 inline struct afb_evtid *afb_evt_event_x2_to_evtid(struct afb_event_x2 *eventid)
873 return (struct afb_evtid*)eventid;
876 inline struct afb_event_x2 *afb_evt_event_x2_from_evtid(struct afb_evtid *evtid)
878 return &evtid->eventid;
882 * Creates an event of 'fullname' and returns it.
883 * Returns an event with closure==NULL in case of error.
885 struct afb_event_x2 *afb_evt_event_x2_create(const char *fullname)
887 return afb_evt_event_x2_from_evtid(afb_evt_evtid_create(fullname));
891 * Creates an event of name 'prefix'/'name' and returns it.
892 * Returns an event with closure==NULL in case of error.
894 struct afb_event_x2 *afb_evt_event_x2_create2(const char *prefix, const char *name)
896 return afb_evt_event_x2_from_evtid(afb_evt_evtid_create2(prefix, name));
900 * Returns the fullname of the 'eventid'
902 const char *afb_evt_event_x2_fullname(struct afb_event_x2 *eventid)
904 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
905 return evtid ? evtid->fullname : NULL;
909 * Returns the id of the 'eventid'
911 int afb_evt_event_x2_id(struct afb_event_x2 *eventid)
913 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
914 return evtid ? evtid->id : 0;
918 * Makes the 'listener' watching 'eventid'
919 * Returns 0 in case of success or else -1.
921 int afb_evt_event_x2_add_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
923 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
925 /* check parameter */
931 /* search the existing watch for the listener */
932 return afb_evt_watch_add_evtid(listener, evtid);
936 * Avoids the 'listener' to watch 'eventid'
937 * Returns 0 in case of success or else -1.
939 int afb_evt_event_x2_remove_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
941 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
943 /* check parameter */
949 /* search the existing watch */
950 return afb_evt_watch_sub_evtid(listener, evtid);
953 int afb_evt_event_x2_push(struct afb_event_x2 *eventid, struct json_object *object)
956 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
958 return afb_evt_evtid_hooked_push(evtid, object);
959 json_object_put(object);
963 __attribute__((alias("afb_evt_event_x2_unhooked_push")));
966 int afb_evt_event_x2_unhooked_push(struct afb_event_x2 *eventid, struct json_object *object)
968 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
970 return afb_evt_evtid_push(evtid, object);
971 json_object_put(object);
975 #if WITH_LEGACY_BINDING_V1 || WITH_LEGACY_BINDING_V2
976 struct afb_event_x1 afb_evt_event_from_evtid(struct afb_evtid *evtid)
980 ? (struct afb_event_x1){ .itf = &afb_evt_hooked_event_x2_itf, .closure = &evtid->eventid }
982 ? (struct afb_event_x1){ .itf = &afb_evt_event_x2_itf, .closure = &evtid->eventid }
984 : (struct afb_event_x1){ .itf = NULL, .closure = NULL };
988 void afb_evt_event_x2_unref(struct afb_event_x2 *eventid)
990 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
992 afb_evt_evtid_unref(evtid);
995 struct afb_event_x2 *afb_evt_event_x2_addref(struct afb_event_x2 *eventid)
997 struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
999 afb_evt_evtid_addref(evtid);