afb-evt: Improve compatibility to guppy
[src/app-framework-binder.git] / src / afb-evt.c
1 /*
2  * Copyright (C) 2015-2019 "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 #if WITH_AFB_HOOK
78         /* hooking */
79         int hookflags;
80 #endif
81
82         /* refcount */
83         int refcount;
84
85         /* id of the event */
86         int id;
87
88         /* fullname of the event */
89         char fullname[];
90 };
91
92 /*
93  * Structure for associating events and listeners
94  */
95 struct afb_evt_watch {
96
97         /* the evtid */
98         struct afb_evtid *evtid;
99
100         /* link to the next watcher for the same evtid */
101         struct afb_evt_watch *next_by_evtid;
102
103         /* the listener */
104         struct afb_evt_listener *listener;
105
106         /* link to the next watcher for the same listener */
107         struct afb_evt_watch *next_by_listener;
108
109         /* activity */
110         unsigned activity;
111 };
112
113 /* the interface for events */
114 static struct afb_event_x2_itf afb_evt_event_x2_itf = {
115         .broadcast = (void*)afb_evt_evtid_broadcast,
116         .push = (void*)afb_evt_evtid_push,
117         .unref = (void*)afb_evt_evtid_unref,
118         .name = (void*)afb_evt_evtid_name,
119         .addref = (void*)afb_evt_evtid_addref
120 };
121
122 #if WITH_AFB_HOOK
123 /* the interface for events */
124 static struct afb_event_x2_itf afb_evt_hooked_event_x2_itf = {
125         .broadcast = (void*)afb_evt_evtid_hooked_broadcast,
126         .push = (void*)afb_evt_evtid_hooked_push,
127         .unref = (void*)afb_evt_evtid_hooked_unref,
128         .name = (void*)afb_evt_evtid_hooked_name,
129         .addref = (void*)afb_evt_evtid_hooked_addref
130 };
131 #endif
132
133 /* head of the list of listeners */
134 static pthread_rwlock_t listeners_rwlock = PTHREAD_RWLOCK_INITIALIZER;
135 static struct afb_evt_listener *listeners = NULL;
136
137 /* handling id of events */
138 static pthread_rwlock_t events_rwlock = PTHREAD_RWLOCK_INITIALIZER;
139 static struct afb_evtid *evtids = NULL;
140 static int event_id_counter = 0;
141 static int event_id_wrapped = 0;
142
143 /*
144  * Broadcasts the 'event' of 'id' with its 'obj'
145  * 'obj' is released (like json_object_put)
146  * Returns the count of listener having receive the event.
147  */
148 static int broadcast(const char *event, struct json_object *obj, int id)
149 {
150         int result;
151         struct afb_evt_listener *listener;
152
153         result = 0;
154
155         pthread_rwlock_rdlock(&listeners_rwlock);
156         listener = listeners;
157         while(listener) {
158                 if (listener->itf->broadcast != NULL) {
159                         listener->itf->broadcast(listener->closure, event, id, json_object_get(obj));
160                         result++;
161                 }
162                 listener = listener->next;
163         }
164         pthread_rwlock_unlock(&listeners_rwlock);
165         json_object_put(obj);
166         return result;
167 }
168
169 #if WITH_AFB_HOOK
170 /*
171  * Broadcasts the 'event' of 'id' with its 'obj'
172  * 'obj' is released (like json_object_put)
173  * calls hooks if hookflags isn't 0
174  * Returns the count of listener having receive the event.
175  */
176 static int hooked_broadcast(const char *event, struct json_object *obj, int id, int hookflags)
177 {
178         int result;
179
180         json_object_get(obj);
181
182         if (hookflags & afb_hook_flag_evt_broadcast_before)
183                 afb_hook_evt_broadcast_before(event, id, obj);
184
185         result = broadcast(event, obj, id);
186
187         if (hookflags & afb_hook_flag_evt_broadcast_after)
188                 afb_hook_evt_broadcast_after(event, id, obj, result);
189
190         json_object_put(obj);
191
192         return result;
193 }
194 #endif
195
196 /*
197  * Broadcasts the event 'evtid' with its 'object'
198  * 'object' is released (like json_object_put)
199  * Returns the count of listener that received the event.
200  */
201 int afb_evt_evtid_broadcast(struct afb_evtid *evtid, struct json_object *object)
202 {
203         return broadcast(evtid->fullname, object, evtid->id);
204 }
205
206 #if WITH_AFB_HOOK
207 /*
208  * Broadcasts the event 'evtid' with its 'object'
209  * 'object' is released (like json_object_put)
210  * Returns the count of listener that received the event.
211  */
212 int afb_evt_evtid_hooked_broadcast(struct afb_evtid *evtid, struct json_object *object)
213 {
214         return hooked_broadcast(evtid->fullname, object, evtid->id, evtid->hookflags);
215 }
216 #endif
217
218 /*
219  * Broadcasts the 'event' with its 'object'
220  * 'object' is released (like json_object_put)
221  * Returns the count of listener having receive the event.
222  */
223 int afb_evt_broadcast(const char *event, struct json_object *object)
224 {
225 #if WITH_AFB_HOOK
226         return hooked_broadcast(event, object, 0, -1);
227 #else
228         return broadcast(event, object, 0);
229 #endif
230 }
231
232 /*
233  * Pushes the event 'evtid' with 'obj' to its listeners
234  * 'obj' is released (like json_object_put)
235  * Returns the count of listener that received the event.
236  */
237 int afb_evt_evtid_push(struct afb_evtid *evtid, struct json_object *obj)
238 {
239         int result;
240         struct afb_evt_watch *watch;
241         struct afb_evt_listener *listener;
242
243         result = 0;
244         pthread_rwlock_rdlock(&evtid->rwlock);
245         watch = evtid->watchs;
246         while(watch) {
247                 listener = watch->listener;
248                 assert(listener->itf->push != NULL);
249                 if (watch->activity != 0) {
250                         listener->itf->push(listener->closure, evtid->fullname, evtid->id, json_object_get(obj));
251                         result++;
252                 }
253                 watch = watch->next_by_evtid;
254         }
255         pthread_rwlock_unlock(&evtid->rwlock);
256         json_object_put(obj);
257         return result;
258 }
259
260 #if WITH_AFB_HOOK
261 /*
262  * Pushes the event 'evtid' with 'obj' to its listeners
263  * 'obj' is released (like json_object_put)
264  * Emits calls to hooks.
265  * Returns the count of listener taht received the event.
266  */
267 int afb_evt_evtid_hooked_push(struct afb_evtid *evtid, struct json_object *obj)
268 {
269
270         int result;
271
272         /* lease the object */
273         json_object_get(obj);
274
275         /* hook before push */
276         if (evtid->hookflags & afb_hook_flag_evt_push_before)
277                 afb_hook_evt_push_before(evtid->fullname, evtid->id, obj);
278
279         /* push */
280         result = afb_evt_evtid_push(evtid, obj);
281
282         /* hook after push */
283         if (evtid->hookflags & afb_hook_flag_evt_push_after)
284                 afb_hook_evt_push_after(evtid->fullname, evtid->id, obj, result);
285
286         /* release the object */
287         json_object_put(obj);
288         return result;
289 }
290 #endif
291
292 /*
293  * remove the 'watch'
294  */
295 static void remove_watch(struct afb_evt_watch *watch)
296 {
297         struct afb_evt_watch **prv;
298         struct afb_evtid *evtid;
299         struct afb_evt_listener *listener;
300
301         /* notify listener if needed */
302         evtid = watch->evtid;
303         listener = watch->listener;
304         if (watch->activity != 0 && listener->itf->remove != NULL)
305                 listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
306
307         /* unlink the watch for its event */
308         prv = &evtid->watchs;
309         while(*prv != watch)
310                 prv = &(*prv)->next_by_evtid;
311         *prv = watch->next_by_evtid;
312
313         /* unlink the watch for its listener */
314         prv = &listener->watchs;
315         while(*prv != watch)
316                 prv = &(*prv)->next_by_listener;
317         *prv = watch->next_by_listener;
318
319         /* recycle memory */
320         free(watch);
321 }
322
323 /*
324  * Creates an event of name 'fullname' and returns it or NULL on error.
325  */
326 struct afb_evtid *afb_evt_evtid_create(const char *fullname)
327 {
328         size_t len;
329         struct afb_evtid *evtid, *oevt;
330
331         /* allocates the event */
332         len = strlen(fullname);
333         evtid = malloc(len + 1 + sizeof * evtid);
334         if (evtid == NULL)
335                 goto error;
336
337         /* allocates the id */
338         pthread_rwlock_wrlock(&events_rwlock);
339         do {
340                 if (++event_id_counter < 0) {
341                         event_id_wrapped = 1;
342                         event_id_counter = 1024; /* heuristic: small numbers are not destroyed */
343                 }
344                 if (!event_id_wrapped)
345                         break;
346                 oevt = evtids;
347                 while(oevt != NULL && oevt->id != event_id_counter)
348                         oevt = oevt->next;
349         } while (oevt != NULL);
350
351         /* initialize the event */
352         memcpy(evtid->fullname, fullname, len + 1);
353         evtid->next = evtids;
354         evtid->refcount = 1;
355         evtid->watchs = NULL;
356         evtid->id = event_id_counter;
357         pthread_rwlock_init(&evtid->rwlock, NULL);
358         evtids = evtid;
359 #if WITH_AFB_HOOK
360         evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
361         evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf;
362         if (evtid->hookflags & afb_hook_flag_evt_create)
363                 afb_hook_evt_create(evtid->fullname, evtid->id);
364 #else
365         evtid->eventid.itf = &afb_evt_event_x2_itf;
366 #endif
367         pthread_rwlock_unlock(&events_rwlock);
368
369         /* returns the event */
370         return evtid;
371 error:
372         return NULL;
373 }
374
375 /*
376  * Creates an event of name 'prefix'/'name' and returns it or NULL on error.
377  */
378 struct afb_evtid *afb_evt_evtid_create2(const char *prefix, const char *name)
379 {
380         size_t prelen, postlen;
381         char *fullname;
382
383         /* makes the event fullname */
384         prelen = strlen(prefix);
385         postlen = strlen(name);
386         fullname = alloca(prelen + postlen + 2);
387         memcpy(fullname, prefix, prelen);
388         fullname[prelen] = '/';
389         memcpy(fullname + prelen + 1, name, postlen + 1);
390
391         /* create the event */
392         return afb_evt_evtid_create(fullname);
393 }
394
395 /*
396  * increment the reference count of the event 'evtid'
397  */
398 struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid)
399 {
400         __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED);
401         return evtid;
402 }
403
404 #if WITH_AFB_HOOK
405 /*
406  * increment the reference count of the event 'evtid'
407  */
408 struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid)
409 {
410         if (evtid->hookflags & afb_hook_flag_evt_addref)
411                 afb_hook_evt_addref(evtid->fullname, evtid->id);
412         return afb_evt_evtid_addref(evtid);
413 }
414 #endif
415
416 /*
417  * decrement the reference count of the event 'evtid'
418  * and destroy it when the count reachs zero
419  */
420 void afb_evt_evtid_unref(struct afb_evtid *evtid)
421 {
422         int found;
423         struct afb_evtid **prv;
424         struct afb_evt_listener *listener;
425
426         if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) {
427                 /* unlinks the event if valid! */
428                 pthread_rwlock_wrlock(&events_rwlock);
429                 found = 0;
430                 prv = &evtids;
431                 while (*prv && !(found = (*prv == evtid)))
432                         prv = &(*prv)->next;
433                 if (found)
434                         *prv = evtid->next;
435                 pthread_rwlock_unlock(&events_rwlock);
436
437                 /* destroys the event */
438                 if (!found)
439                         ERROR("event not found");
440                 else {
441                         /* removes all watchers */
442                         while(evtid->watchs != NULL) {
443                                 listener = evtid->watchs->listener;
444                                 pthread_rwlock_wrlock(&listener->rwlock);
445                                 pthread_rwlock_wrlock(&evtid->rwlock);
446                                 remove_watch(evtid->watchs);
447                                 pthread_rwlock_unlock(&evtid->rwlock);
448                                 pthread_rwlock_unlock(&listener->rwlock);
449                         }
450
451                         /* free */
452                         pthread_rwlock_destroy(&evtid->rwlock);
453                         free(evtid);
454                 }
455         }
456 }
457
458 #if WITH_AFB_HOOK
459 /*
460  * decrement the reference count of the event 'evtid'
461  * and destroy it when the count reachs zero
462  */
463 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
464 {
465         if (evtid->hookflags & afb_hook_flag_evt_unref)
466                 afb_hook_evt_unref(evtid->fullname, evtid->id);
467         afb_evt_evtid_unref(evtid);
468 }
469 #endif
470
471 /*
472  * Returns the true name of the 'event'
473  */
474 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
475 {
476         return evtid->fullname;
477 }
478
479 /*
480  * Returns the name of the 'event'
481  */
482 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
483 {
484         const char *name = strchr(evtid->fullname, '/');
485         return name ? name + 1 : evtid->fullname;
486 }
487
488 #if WITH_AFB_HOOK
489 /*
490  * Returns the name associated to the event 'evtid'.
491  */
492 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
493 {
494         const char *result = afb_evt_evtid_name(evtid);
495         if (evtid->hookflags & afb_hook_flag_evt_name)
496                 afb_hook_evt_name(evtid->fullname, evtid->id, result);
497         return result;
498 }
499 #endif
500
501 /*
502  * Returns the id of the 'event'
503  */
504 int afb_evt_evtid_id(struct afb_evtid *evtid)
505 {
506         return evtid->id;
507 }
508
509 /*
510  * Returns an instance of the listener defined by the 'send' callback
511  * and the 'closure'.
512  * Returns NULL in case of memory depletion.
513  */
514 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
515 {
516         struct afb_evt_listener *listener;
517
518         /* search if an instance already exists */
519         pthread_rwlock_wrlock(&listeners_rwlock);
520         listener = listeners;
521         while (listener != NULL) {
522                 if (listener->itf == itf && listener->closure == closure) {
523                         listener = afb_evt_listener_addref(listener);
524                         goto found;
525                 }
526                 listener = listener->next;
527         }
528
529         /* allocates */
530         listener = calloc(1, sizeof *listener);
531         if (listener != NULL) {
532                 /* init */
533                 listener->itf = itf;
534                 listener->closure = closure;
535                 listener->watchs = NULL;
536                 listener->refcount = 1;
537                 pthread_rwlock_init(&listener->rwlock, NULL);
538                 listener->next = listeners;
539                 listeners = listener;
540         }
541  found:
542         pthread_rwlock_unlock(&listeners_rwlock);
543         return listener;
544 }
545
546 /*
547  * Increases the reference count of 'listener' and returns it
548  */
549 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
550 {
551         __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
552         return listener;
553 }
554
555 /*
556  * Decreases the reference count of the 'listener' and destroys it
557  * when no more used.
558  */
559 void afb_evt_listener_unref(struct afb_evt_listener *listener)
560 {
561         struct afb_evt_listener **prv;
562         struct afb_evtid *evtid;
563
564         if (listener && !__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
565
566                 /* unlink the listener */
567                 pthread_rwlock_wrlock(&listeners_rwlock);
568                 prv = &listeners;
569                 while (*prv != listener)
570                         prv = &(*prv)->next;
571                 *prv = listener->next;
572                 pthread_rwlock_unlock(&listeners_rwlock);
573
574                 /* remove the watchers */
575                 pthread_rwlock_wrlock(&listener->rwlock);
576                 while (listener->watchs != NULL) {
577                         evtid = listener->watchs->evtid;
578                         pthread_rwlock_wrlock(&evtid->rwlock);
579                         remove_watch(listener->watchs);
580                         pthread_rwlock_unlock(&evtid->rwlock);
581                 }
582                 pthread_rwlock_unlock(&listener->rwlock);
583
584                 /* free the listener */
585                 pthread_rwlock_destroy(&listener->rwlock);
586                 free(listener);
587         }
588 }
589
590 /*
591  * Makes the 'listener' watching 'evtid'
592  * Returns 0 in case of success or else -1.
593  */
594 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
595 {
596         struct afb_evt_watch *watch;
597
598         /* check parameter */
599         if (listener->itf->push == NULL) {
600                 errno = EINVAL;
601                 return -1;
602         }
603
604         /* search the existing watch for the listener */
605         pthread_rwlock_wrlock(&listener->rwlock);
606         watch = listener->watchs;
607         while(watch != NULL) {
608                 if (watch->evtid == evtid)
609                         goto found;
610                 watch = watch->next_by_listener;
611         }
612
613         /* not found, allocate a new */
614         watch = malloc(sizeof *watch);
615         if (watch == NULL) {
616                 pthread_rwlock_unlock(&listener->rwlock);
617                 errno = ENOMEM;
618                 return -1;
619         }
620
621         /* initialise and link */
622         watch->evtid = evtid;
623         watch->activity = 0;
624         watch->listener = listener;
625         watch->next_by_listener = listener->watchs;
626         listener->watchs = watch;
627         pthread_rwlock_wrlock(&evtid->rwlock);
628         watch->next_by_evtid = evtid->watchs;
629         evtid->watchs = watch;
630         pthread_rwlock_unlock(&evtid->rwlock);
631
632 found:
633         if (watch->activity == 0 && listener->itf->add != NULL)
634                 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
635         watch->activity++;
636         pthread_rwlock_unlock(&listener->rwlock);
637
638         return 0;
639 }
640
641 /*
642  * Avoids the 'listener' to watch 'evtid'
643  * Returns 0 in case of success or else -1.
644  */
645 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
646 {
647         struct afb_evt_watch *watch;
648
649         /* search the existing watch */
650         pthread_rwlock_wrlock(&listener->rwlock);
651         watch = listener->watchs;
652         while(watch != NULL) {
653                 if (watch->evtid == evtid) {
654                         if (watch->activity != 0) {
655                                 watch->activity--;
656                                 if (watch->activity == 0 && listener->itf->remove != NULL)
657                                         listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
658                         }
659                         pthread_rwlock_unlock(&listener->rwlock);
660                         return 0;
661                 }
662                 watch = watch->next_by_listener;
663         }
664         pthread_rwlock_unlock(&listener->rwlock);
665         errno = ENOENT;
666         return -1;
667 }
668
669 #if WITH_AFB_HOOK
670 /*
671  * update the hooks for events
672  */
673 void afb_evt_update_hooks()
674 {
675         struct afb_evtid *evtid;
676
677         pthread_rwlock_rdlock(&events_rwlock);
678         for (evtid = evtids ; evtid ; evtid = evtid->next) {
679                 evtid->hookflags = afb_hook_flags_evt(evtid->fullname);
680                 evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf;
681         }
682         pthread_rwlock_unlock(&events_rwlock);
683 }
684 #endif
685
686 inline struct afb_evtid *afb_evt_event_x2_to_evtid(struct afb_event_x2 *eventid)
687 {
688         return (struct afb_evtid*)eventid;
689 }
690
691 inline struct afb_event_x2 *afb_evt_event_x2_from_evtid(struct afb_evtid *evtid)
692 {
693         return &evtid->eventid;
694 }
695
696 /*
697  * Creates an event of 'fullname' and returns it.
698  * Returns an event with closure==NULL in case of error.
699  */
700 struct afb_event_x2 *afb_evt_event_x2_create(const char *fullname)
701 {
702         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create(fullname));
703 }
704
705 /*
706  * Creates an event of name 'prefix'/'name' and returns it.
707  * Returns an event with closure==NULL in case of error.
708  */
709 struct afb_event_x2 *afb_evt_event_x2_create2(const char *prefix, const char *name)
710 {
711         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create2(prefix, name));
712 }
713
714 /*
715  * Returns the fullname of the 'eventid'
716  */
717 const char *afb_evt_event_x2_fullname(struct afb_event_x2 *eventid)
718 {
719         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
720         return evtid ? evtid->fullname : NULL;
721 }
722
723 /*
724  * Returns the id of the 'eventid'
725  */
726 int afb_evt_event_x2_id(struct afb_event_x2 *eventid)
727 {
728         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
729         return evtid ? evtid->id : 0;
730 }
731
732 /*
733  * Makes the 'listener' watching 'eventid'
734  * Returns 0 in case of success or else -1.
735  */
736 int afb_evt_event_x2_add_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
737 {
738         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
739
740         /* check parameter */
741         if (!evtid) {
742                 errno = EINVAL;
743                 return -1;
744         }
745
746         /* search the existing watch for the listener */
747         return afb_evt_watch_add_evtid(listener, evtid);
748 }
749
750 /*
751  * Avoids the 'listener' to watch 'eventid'
752  * Returns 0 in case of success or else -1.
753  */
754 int afb_evt_event_x2_remove_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
755 {
756         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
757
758         /* check parameter */
759         if (!evtid) {
760                 errno = EINVAL;
761                 return -1;
762         }
763
764         /* search the existing watch */
765         return afb_evt_watch_sub_evtid(listener, evtid);
766 }
767
768 int afb_evt_event_x2_push(struct afb_event_x2 *eventid, struct json_object *object)
769 #if WITH_AFB_HOOK
770 {
771         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
772         if (evtid)
773                 return afb_evt_evtid_hooked_push(evtid, object);
774         json_object_put(object);
775         return 0;
776 }
777 #else
778         __attribute__((alias("afb_evt_event_x2_unhooked_push")));
779 #endif
780
781 int afb_evt_event_x2_unhooked_push(struct afb_event_x2 *eventid, struct json_object *object)
782 {
783         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
784         if (evtid)
785                 return afb_evt_evtid_push(evtid, object);
786         json_object_put(object);
787         return 0;
788 }
789
790 #if WITH_LEGACY_BINDING_V1 || WITH_LEGACY_BINDING_V2
791 struct afb_event_x1 afb_evt_event_from_evtid(struct afb_evtid *evtid)
792 {
793         return evtid
794 #if WITH_AFB_HOOK
795                 ? (struct afb_event_x1){ .itf = &afb_evt_hooked_event_x2_itf, .closure = &evtid->eventid }
796 #else
797                 ? (struct afb_event_x1){ .itf = &afb_evt_event_x2_itf, .closure = &evtid->eventid }
798 #endif
799                 : (struct afb_event_x1){ .itf = NULL, .closure = NULL };
800 }
801 #endif
802
803 void afb_evt_event_x2_unref(struct afb_event_x2 *eventid)
804 {
805         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
806         if (evtid)
807                 afb_evt_evtid_unref(evtid);
808 }
809
810 struct afb_event_x2 *afb_evt_event_x2_addref(struct afb_event_x2 *eventid)
811 {
812         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
813         if (evtid)
814                 afb_evt_evtid_addref(evtid);
815         return eventid;
816 }
817