Fedora 30 packaging fix issu
[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-event-x2-itf.h>
28 #include <afb/afb-event-x1.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         /* rwlock of the listener */
54         pthread_rwlock_t rwlock;
55
56         /* count of reference to the listener */
57         int refcount;
58 };
59
60 /*
61  * Structure for describing events
62  */
63 struct afb_evtid {
64
65         /* interface */
66         struct afb_event_x2 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         /* rwlock of the event */
75         pthread_rwlock_t rwlock;
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[];
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_event_x2_itf afb_evt_event_x2_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_event_x2_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_rwlock_t listeners_rwlock = PTHREAD_RWLOCK_INITIALIZER;
131 static struct afb_evt_listener *listeners = NULL;
132
133 /* handling id of events */
134 static pthread_rwlock_t events_rwlock = PTHREAD_RWLOCK_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
151         pthread_rwlock_rdlock(&listeners_rwlock);
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_rwlock_unlock(&listeners_rwlock);
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_rwlock_rdlock(&evtid->rwlock);
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_rwlock_unlock(&evtid->rwlock);
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
257         int result;
258
259         /* lease the object */
260         json_object_get(obj);
261
262         /* hook before push */
263         if (evtid->hookflags & afb_hook_flag_evt_push_before)
264                 afb_hook_evt_push_before(evtid->fullname, evtid->id, obj);
265
266         /* push */
267         result = afb_evt_evtid_push(evtid, obj);
268
269         /* hook after push */
270         if (evtid->hookflags & afb_hook_flag_evt_push_after)
271                 afb_hook_evt_push_after(evtid->fullname, evtid->id, obj, result);
272
273         /* release the object */
274         json_object_put(obj);
275         return result;
276 }
277
278 /*
279  * remove the 'watch'
280  */
281 static void remove_watch(struct afb_evt_watch *watch)
282 {
283         struct afb_evt_watch **prv;
284         struct afb_evtid *evtid;
285         struct afb_evt_listener *listener;
286
287         /* notify listener if needed */
288         evtid = watch->evtid;
289         listener = watch->listener;
290         if (watch->activity != 0 && listener->itf->remove != NULL)
291                 listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
292
293         /* unlink the watch for its event */
294         prv = &evtid->watchs;
295         while(*prv != watch)
296                 prv = &(*prv)->next_by_evtid;
297         *prv = watch->next_by_evtid;
298
299         /* unlink the watch for its listener */
300         prv = &listener->watchs;
301         while(*prv != watch)
302                 prv = &(*prv)->next_by_listener;
303         *prv = watch->next_by_listener;
304
305         /* recycle memory */
306         free(watch);
307 }
308
309 /*
310  * Creates an event of name 'fullname' and returns it or NULL on error.
311  */
312 struct afb_evtid *afb_evt_evtid_create(const char *fullname)
313 {
314         size_t len;
315         struct afb_evtid *evtid, *oevt;
316
317         /* allocates the event */
318         len = strlen(fullname);
319         evtid = malloc(len + 1 + sizeof * evtid);
320         if (evtid == NULL)
321                 goto error;
322
323         /* allocates the id */
324         pthread_rwlock_wrlock(&events_rwlock);
325         do {
326                 if (++event_id_counter < 0) {
327                         event_id_wrapped = 1;
328                         event_id_counter = 1024; /* heuristic: small numbers are not destroyed */
329                 }
330                 if (!event_id_wrapped)
331                         break;
332                 oevt = evtids;
333                 while(oevt != NULL && oevt->id != event_id_counter)
334                         oevt = oevt->next;
335         } while (oevt != NULL);
336
337         /* initialize the event */
338         memcpy(evtid->fullname, fullname, len + 1);
339         evtid->next = evtids;
340         evtid->refcount = 1;
341         evtid->watchs = NULL;
342         evtid->id = event_id_counter;
343         pthread_rwlock_init(&evtid->rwlock, NULL);
344         evtids = evtid;
345         evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
346         evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_eventid_itf : &afb_evt_event_x2_itf;
347         if (evtid->hookflags & afb_hook_flag_evt_create)
348                 afb_hook_evt_create(evtid->fullname, evtid->id);
349         pthread_rwlock_unlock(&events_rwlock);
350
351         /* returns the event */
352         return evtid;
353 error:
354         return NULL;
355 }
356
357 /*
358  * Creates an event of name 'prefix'/'name' and returns it or NULL on error.
359  */
360 struct afb_evtid *afb_evt_evtid_create2(const char *prefix, const char *name)
361 {
362         size_t prelen, postlen;
363         char *fullname;
364
365         /* makes the event fullname */
366         prelen = strlen(prefix);
367         postlen = strlen(name);
368         fullname = alloca(prelen + postlen + 2);
369         memcpy(fullname, prefix, prelen);
370         fullname[prelen] = '/';
371         memcpy(fullname + prelen + 1, name, postlen + 1);
372
373         /* create the event */
374         return afb_evt_evtid_create(fullname);
375 }
376
377 /*
378  * increment the reference count of the event 'evtid'
379  */
380 struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid)
381 {
382         __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED);
383         return evtid;
384 }
385
386 /*
387  * increment the reference count of the event 'evtid'
388  */
389 struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid)
390 {
391         if (evtid->hookflags & afb_hook_flag_evt_addref)
392                 afb_hook_evt_addref(evtid->fullname, evtid->id);
393         return afb_evt_evtid_addref(evtid);
394 }
395
396 /*
397  * decrement the reference count of the event 'evtid'
398  * and destroy it when the count reachs zero
399  */
400 void afb_evt_evtid_unref(struct afb_evtid *evtid)
401 {
402         int found;
403         struct afb_evtid **prv;
404         struct afb_evt_listener *listener;
405
406         if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) {
407                 /* unlinks the event if valid! */
408                 pthread_rwlock_wrlock(&events_rwlock);
409                 found = 0;
410                 prv = &evtids;
411                 while (*prv && !(found = (*prv == evtid)))
412                         prv = &(*prv)->next;
413                 if (found)
414                         *prv = evtid->next;
415                 pthread_rwlock_unlock(&events_rwlock);
416
417                 /* destroys the event */
418                 if (!found)
419                         ERROR("event not found");
420                 else {
421                         /* removes all watchers */
422                         while(evtid->watchs != NULL) {
423                                 listener = evtid->watchs->listener;
424                                 pthread_rwlock_wrlock(&listener->rwlock);
425                                 pthread_rwlock_wrlock(&evtid->rwlock);
426                                 remove_watch(evtid->watchs);
427                                 pthread_rwlock_unlock(&evtid->rwlock);
428                                 pthread_rwlock_unlock(&listener->rwlock);
429                         }
430
431                         /* free */
432                         pthread_rwlock_destroy(&evtid->rwlock);
433                         free(evtid);
434                 }
435         }
436 }
437
438 /*
439  * decrement the reference count of the event 'evtid'
440  * and destroy it when the count reachs zero
441  */
442 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
443 {
444         if (evtid->hookflags & afb_hook_flag_evt_unref)
445                 afb_hook_evt_unref(evtid->fullname, evtid->id);
446         afb_evt_evtid_unref(evtid);
447 }
448
449 /*
450  * Returns the true name of the 'event'
451  */
452 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
453 {
454         return evtid->fullname;
455 }
456
457 /*
458  * Returns the name of the 'event'
459  */
460 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
461 {
462         const char *name = strchr(evtid->fullname, '/');
463         return name ? name + 1 : evtid->fullname;
464 }
465
466 /*
467  * Returns the name associated to the event 'evtid'.
468  */
469 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
470 {
471         const char *result = afb_evt_evtid_name(evtid);
472         if (evtid->hookflags & afb_hook_flag_evt_name)
473                 afb_hook_evt_name(evtid->fullname, evtid->id, result);
474         return result;
475 }
476
477 /*
478  * Returns the id of the 'event'
479  */
480 int afb_evt_evtid_id(struct afb_evtid *evtid)
481 {
482         return evtid->id;
483 }
484
485 /*
486  * Returns an instance of the listener defined by the 'send' callback
487  * and the 'closure'.
488  * Returns NULL in case of memory depletion.
489  */
490 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
491 {
492         struct afb_evt_listener *listener;
493
494         /* search if an instance already exists */
495         pthread_rwlock_wrlock(&listeners_rwlock);
496         listener = listeners;
497         while (listener != NULL) {
498                 if (listener->itf == itf && listener->closure == closure) {
499                         listener = afb_evt_listener_addref(listener);
500                         goto found;
501                 }
502                 listener = listener->next;
503         }
504
505         /* allocates */
506         listener = calloc(1, sizeof *listener);
507         if (listener != NULL) {
508                 /* init */
509                 listener->itf = itf;
510                 listener->closure = closure;
511                 listener->watchs = NULL;
512                 listener->refcount = 1;
513                 pthread_rwlock_init(&listener->rwlock, NULL);
514                 listener->next = listeners;
515                 listeners = listener;
516         }
517  found:
518         pthread_rwlock_unlock(&listeners_rwlock);
519         return listener;
520 }
521
522 /*
523  * Increases the reference count of 'listener' and returns it
524  */
525 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
526 {
527         __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
528         return listener;
529 }
530
531 /*
532  * Decreases the reference count of the 'listener' and destroys it
533  * when no more used.
534  */
535 void afb_evt_listener_unref(struct afb_evt_listener *listener)
536 {
537         struct afb_evt_listener **prv;
538         struct afb_evtid *evtid;
539
540         if (listener && !__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
541
542                 /* unlink the listener */
543                 pthread_rwlock_wrlock(&listeners_rwlock);
544                 prv = &listeners;
545                 while (*prv != listener)
546                         prv = &(*prv)->next;
547                 *prv = listener->next;
548                 pthread_rwlock_unlock(&listeners_rwlock);
549
550                 /* remove the watchers */
551                 pthread_rwlock_wrlock(&listener->rwlock);
552                 while (listener->watchs != NULL) {
553                         evtid = listener->watchs->evtid;
554                         pthread_rwlock_wrlock(&evtid->rwlock);
555                         remove_watch(listener->watchs);
556                         pthread_rwlock_unlock(&evtid->rwlock);
557                 }
558                 pthread_rwlock_unlock(&listener->rwlock);
559
560                 /* free the listener */
561                 pthread_rwlock_destroy(&listener->rwlock);
562                 free(listener);
563         }
564 }
565
566 /*
567  * Makes the 'listener' watching 'evtid'
568  * Returns 0 in case of success or else -1.
569  */
570 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
571 {
572         struct afb_evt_watch *watch;
573
574         /* check parameter */
575         if (listener->itf->push == NULL) {
576                 errno = EINVAL;
577                 return -1;
578         }
579
580         /* search the existing watch for the listener */
581         pthread_rwlock_wrlock(&listener->rwlock);
582         watch = listener->watchs;
583         while(watch != NULL) {
584                 if (watch->evtid == evtid)
585                         goto found;
586                 watch = watch->next_by_listener;
587         }
588
589         /* not found, allocate a new */
590         watch = malloc(sizeof *watch);
591         if (watch == NULL) {
592                 pthread_rwlock_unlock(&listener->rwlock);
593                 errno = ENOMEM;
594                 return -1;
595         }
596
597         /* initialise and link */
598         watch->evtid = evtid;
599         watch->activity = 0;
600         watch->listener = listener;
601         watch->next_by_listener = listener->watchs;
602         listener->watchs = watch;
603         pthread_rwlock_wrlock(&evtid->rwlock);
604         watch->next_by_evtid = evtid->watchs;
605         evtid->watchs = watch;
606         pthread_rwlock_unlock(&evtid->rwlock);
607
608 found:
609         if (watch->activity == 0 && listener->itf->add != NULL)
610                 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
611         watch->activity++;
612         pthread_rwlock_unlock(&listener->rwlock);
613
614         return 0;
615 }
616
617 /*
618  * Avoids the 'listener' to watch 'evtid'
619  * Returns 0 in case of success or else -1.
620  */
621 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
622 {
623         struct afb_evt_watch *watch;
624
625         /* search the existing watch */
626         pthread_rwlock_wrlock(&listener->rwlock);
627         watch = listener->watchs;
628         while(watch != NULL) {
629                 if (watch->evtid == evtid) {
630                         if (watch->activity != 0) {
631                                 watch->activity--;
632                                 if (watch->activity == 0 && listener->itf->remove != NULL)
633                                         listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
634                         }
635                         pthread_rwlock_unlock(&listener->rwlock);
636                         return 0;
637                 }
638                 watch = watch->next_by_listener;
639         }
640         pthread_rwlock_unlock(&listener->rwlock);
641         errno = ENOENT;
642         return -1;
643 }
644
645 /*
646  * update the hooks for events
647  */
648 void afb_evt_update_hooks()
649 {
650         struct afb_evtid *evtid;
651
652         pthread_rwlock_rdlock(&events_rwlock);
653         for (evtid = evtids ; evtid ; evtid = evtid->next) {
654                 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
655                 evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_eventid_itf : &afb_evt_event_x2_itf;
656         }
657         pthread_rwlock_unlock(&events_rwlock);
658 }
659
660 inline struct afb_evtid *afb_evt_event_x2_to_evtid(struct afb_event_x2 *eventid)
661 {
662         return (struct afb_evtid*)eventid;
663 }
664
665 inline struct afb_event_x2 *afb_evt_event_x2_from_evtid(struct afb_evtid *evtid)
666 {
667         return &evtid->eventid;
668 }
669
670 /*
671  * Creates an event of 'fullname' and returns it.
672  * Returns an event with closure==NULL in case of error.
673  */
674 struct afb_event_x2 *afb_evt_event_x2_create(const char *fullname)
675 {
676         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create(fullname));
677 }
678
679 /*
680  * Creates an event of name 'prefix'/'name' and returns it.
681  * Returns an event with closure==NULL in case of error.
682  */
683 struct afb_event_x2 *afb_evt_event_x2_create2(const char *prefix, const char *name)
684 {
685         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create2(prefix, name));
686 }
687
688 /*
689  * Returns the fullname of the 'eventid'
690  */
691 const char *afb_evt_event_x2_fullname(struct afb_event_x2 *eventid)
692 {
693         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
694         return evtid ? evtid->fullname : NULL;
695 }
696
697 /*
698  * Returns the id of the 'eventid'
699  */
700 int afb_evt_event_x2_id(struct afb_event_x2 *eventid)
701 {
702         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
703         return evtid ? evtid->id : 0;
704 }
705
706 /*
707  * Makes the 'listener' watching 'eventid'
708  * Returns 0 in case of success or else -1.
709  */
710 int afb_evt_event_x2_add_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
711 {
712         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
713
714         /* check parameter */
715         if (!evtid) {
716                 errno = EINVAL;
717                 return -1;
718         }
719
720         /* search the existing watch for the listener */
721         return afb_evt_watch_add_evtid(listener, evtid);
722 }
723
724 /*
725  * Avoids the 'listener' to watch 'eventid'
726  * Returns 0 in case of success or else -1.
727  */
728 int afb_evt_event_x2_remove_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
729 {
730         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
731
732         /* check parameter */
733         if (!evtid) {
734                 errno = EINVAL;
735                 return -1;
736         }
737
738         /* search the existing watch */
739         return afb_evt_watch_sub_evtid(listener, evtid);
740 }
741
742 int afb_evt_event_x2_push(struct afb_event_x2 *eventid, struct json_object *object)
743 {
744         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
745         if (evtid)
746                 return afb_evt_evtid_hooked_push(evtid, object);
747         json_object_put(object);
748         return 0;
749 }
750
751 int afb_evt_event_x2_unhooked_push(struct afb_event_x2 *eventid, struct json_object *object)
752 {
753         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
754         if (evtid)
755                 return afb_evt_evtid_push(evtid, object);
756         json_object_put(object);
757         return 0;
758 }
759
760 struct afb_event_x1 afb_evt_event_from_evtid(struct afb_evtid *evtid)
761 {
762         return evtid
763                 ? (struct afb_event_x1){ .itf = &afb_evt_hooked_eventid_itf, .closure = &evtid->eventid }
764                 : (struct afb_event_x1){ .itf = NULL, .closure = NULL };
765 }
766
767 void afb_evt_event_x2_unref(struct afb_event_x2 *eventid)
768 {
769         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
770         if (evtid)
771                 afb_evt_evtid_unref(evtid);
772 }
773
774 struct afb_event_x2 *afb_evt_event_x2_addref(struct afb_event_x2 *eventid)
775 {
776         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
777         if (evtid)
778                 afb_evt_evtid_addref(evtid);
779         return eventid;
780 }
781