afb-evt: send events in separate job
[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 #include "jobs.h"
34
35 struct afb_evt_watch;
36
37 /*
38  * Structure for event listeners
39  */
40 struct afb_evt_listener {
41
42         /* chaining listeners */
43         struct afb_evt_listener *next;
44
45         /* interface for callbacks */
46         const struct afb_evt_itf *itf;
47
48         /* closure for the callback */
49         void *closure;
50
51         /* head of the list of events listened */
52         struct afb_evt_watch *watchs;
53
54         /* rwlock of the listener */
55         pthread_rwlock_t rwlock;
56
57         /* count of reference to the listener */
58         int refcount;
59 };
60
61 /*
62  * Structure for describing events
63  */
64 struct afb_evtid {
65
66         /* interface */
67         struct afb_event_x2 eventid;
68
69         /* next event */
70         struct afb_evtid *next;
71
72         /* head of the list of listeners watching the event */
73         struct afb_evt_watch *watchs;
74
75         /* rwlock of the event */
76         pthread_rwlock_t rwlock;
77
78 #if WITH_AFB_HOOK
79         /* hooking */
80         int hookflags;
81 #endif
82
83         /* refcount */
84         int refcount;
85
86         /* id of the event */
87         int id;
88
89         /* has client? */
90         int has_client;
91
92         /* fullname of the event */
93         char fullname[];
94 };
95
96 /*
97  * Structure for associating events and listeners
98  */
99 struct afb_evt_watch {
100
101         /* the evtid */
102         struct afb_evtid *evtid;
103
104         /* link to the next watcher for the same evtid */
105         struct afb_evt_watch *next_by_evtid;
106
107         /* the listener */
108         struct afb_evt_listener *listener;
109
110         /* link to the next watcher for the same listener */
111         struct afb_evt_watch *next_by_listener;
112
113         /* activity */
114         unsigned activity;
115 };
116
117 /*
118  * structure for job of broadcasting string events
119  */
120 struct job_string
121 {
122         /** object atached to the event */
123         struct json_object *object;
124
125         /** name of the event to broadcast */
126         char event[];
127 };
128
129 /*
130  * structure for job of broadcasting or pushing events
131  */
132 struct job_evtid
133 {
134         /** the event to broadcast */
135         struct afb_evtid *evtid;
136
137         /** object atached to the event */
138         struct json_object *object;
139 };
140
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
148 };
149
150 #if WITH_AFB_HOOK
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
158 };
159 #endif
160
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)
164
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;
168
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;
174
175 /*
176  * Create structure for job of broadcasting string 'event' with 'object'
177  * Returns the created structure or NULL if out of memory
178  */
179 static struct job_string *make_job_string(const char *event, struct json_object *object)
180 {
181         size_t sz = 1 + strlen(event);
182         struct job_string *js = malloc(sz + sizeof *js);
183         if (js) {
184                 js->object = object;
185                 memcpy(js->event, event, sz);
186         }
187         return js;
188 }
189
190 /*
191  * Destroy structure 'js' for job of broadcasting string events
192  */
193 static void destroy_job_string(struct job_string *js)
194 {
195         json_object_put(js->object);
196         free(js);
197 }
198
199 /*
200  * Create structure for job of broadcasting or pushing 'evtid' with 'object'
201  * Returns the created structure or NULL if out of memory
202  */
203 static struct job_evtid *make_job_evtid(struct afb_evtid *evtid, struct json_object *object)
204 {
205         struct job_evtid *je = malloc(sizeof *je);
206         if (je) {
207                 je->evtid = afb_evt_evtid_addref(evtid);
208                 je->object = object;
209         }
210         return je;
211 }
212
213 /*
214  * Destroy structure for job of broadcasting or pushing evtid
215  */
216 static void destroy_job_evtid(struct job_evtid *je)
217 {
218         afb_evt_evtid_unref(je->evtid);
219         json_object_put(je->object);
220         free(je);
221 }
222
223 /*
224  * Broadcasts the 'event' of 'id' with its 'object'
225  */
226 static void broadcast(const char *event, struct json_object *object, int id)
227 {
228         struct afb_evt_listener *listener;
229
230         pthread_rwlock_rdlock(&listeners_rwlock);
231         listener = listeners;
232         while(listener) {
233                 if (listener->itf->broadcast != NULL)
234                         listener->itf->broadcast(listener->closure, event, id, json_object_get(object));
235                 listener = listener->next;
236         }
237         pthread_rwlock_unlock(&listeners_rwlock);
238 }
239
240 /*
241  * Jobs callback for broadcasting string asynchronously
242  */
243 static void broadcast_job_string(int signum, void *closure)
244 {
245         struct job_string *js = closure;
246
247         if (signum == 0)
248                 broadcast(js->event, js->object, 0);
249         destroy_job_string(js);
250 }
251
252 /*
253  * Jobs callback for broadcasting evtid asynchronously
254  */
255 static void broadcast_job_evtid(int signum, void *closure)
256 {
257         struct job_evtid *je = closure;
258
259         if (signum == 0)
260                 broadcast(je->evtid->fullname, je->object, je->evtid->id);
261         destroy_job_evtid(je);
262 }
263
264 /*
265  * Broadcasts the string 'event' with its 'object'
266  */
267 static int broadcast_string(const char *event, struct json_object *object)
268 {
269         struct job_string *js;
270         int rc;
271
272         js = make_job_string(event, object);
273         if (js == NULL) {
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);
277                 return -1;
278         }
279
280         rc = jobs_queue(BROADCAST_JOB_GROUP, 0, broadcast_job_string, js);
281         if (rc) {
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);
285         }
286         return rc;
287 }
288
289 /*
290  * Broadcasts the 'evtid' with its 'object'
291  */
292 static int broadcast_evtid(struct afb_evtid *evtid, struct json_object *object)
293 {
294         struct job_evtid *je;
295         int rc;
296
297         je = make_job_evtid(evtid, object);
298         if (je == NULL) {
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);
302                 return -1;
303         }
304
305         rc = jobs_queue(BROADCAST_JOB_GROUP, 0, broadcast_job_evtid, je);
306         if (rc) {
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);
310         }
311         return rc;
312 }
313
314 /*
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.
318  */
319 int afb_evt_evtid_broadcast(struct afb_evtid *evtid, struct json_object *object)
320 {
321         return broadcast_evtid(evtid, object);
322 }
323
324 #if WITH_AFB_HOOK
325 /*
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.
329  */
330 int afb_evt_evtid_hooked_broadcast(struct afb_evtid *evtid, struct json_object *object)
331 {
332         int result;
333
334         json_object_get(object);
335
336         if (evtid->hookflags & afb_hook_flag_evt_broadcast_before)
337                 afb_hook_evt_broadcast_before(evtid->fullname, evtid->id, object);
338
339         result = broadcast_evtid(evtid, object);
340
341         if (evtid->hookflags & afb_hook_flag_evt_broadcast_after)
342                 afb_hook_evt_broadcast_after(evtid->fullname, evtid->id, object, result);
343
344         json_object_put(object);
345
346         return result;
347 }
348 #endif
349
350 /*
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.
354  */
355 int afb_evt_broadcast(const char *event, struct json_object *object)
356 {
357 #if WITH_AFB_HOOK
358         int result;
359
360         json_object_get(object);
361
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);
365
366         json_object_put(object);
367
368         return result;
369 #else
370         return broadcast_string(event, object);
371 #endif
372 }
373
374 /*
375  * Pushes the event 'evtid' with 'obj' to its listeners
376  * Returns the count of listener that received the event.
377  */
378 static void push_evtid(struct afb_evtid *evtid, struct json_object *object)
379 {
380         int has_client;
381         struct afb_evt_watch *watch;
382         struct afb_evt_listener *listener;
383
384         has_client = 0;
385         pthread_rwlock_rdlock(&evtid->rwlock);
386         watch = evtid->watchs;
387         while(watch) {
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));
392                         has_client = 1;
393                 }
394                 watch = watch->next_by_evtid;
395         }
396         evtid->has_client = has_client;
397         pthread_rwlock_unlock(&evtid->rwlock);
398 }
399
400 /*
401  * Jobs callback for pushing evtid asynchronously
402  */
403 static void push_job_evtid(int signum, void *closure)
404 {
405         struct job_evtid *je = closure;
406
407         if (signum == 0)
408                 push_evtid(je->evtid, je->object);
409         destroy_job_evtid(je);
410 }
411
412 /*
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
417  */
418 int afb_evt_evtid_push(struct afb_evtid *evtid, struct json_object *object)
419 {
420         struct job_evtid *je;
421         int rc;
422
423         je = make_job_evtid(evtid, object);
424         if (je == NULL) {
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);
428                 return -1;
429         }
430
431         rc = jobs_queue(PUSH_JOB_GROUP, 0, push_job_evtid, je);
432         if (rc == 0)
433                 rc = evtid->has_client;
434         else {
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);
438         }
439
440         return rc;
441 }
442
443 #if WITH_AFB_HOOK
444 /*
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.
449  */
450 int afb_evt_evtid_hooked_push(struct afb_evtid *evtid, struct json_object *obj)
451 {
452
453         int result;
454
455         /* lease the object */
456         json_object_get(obj);
457
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);
461
462         /* push */
463         result = afb_evt_evtid_push(evtid, obj);
464
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);
468
469         /* release the object */
470         json_object_put(obj);
471         return result;
472 }
473 #endif
474
475 /*
476  * remove the 'watch'
477  */
478 static void remove_watch(struct afb_evt_watch *watch)
479 {
480         struct afb_evt_watch **prv;
481         struct afb_evtid *evtid;
482         struct afb_evt_listener *listener;
483
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);
489
490         /* unlink the watch for its event */
491         prv = &evtid->watchs;
492         while(*prv != watch)
493                 prv = &(*prv)->next_by_evtid;
494         *prv = watch->next_by_evtid;
495
496         /* unlink the watch for its listener */
497         prv = &listener->watchs;
498         while(*prv != watch)
499                 prv = &(*prv)->next_by_listener;
500         *prv = watch->next_by_listener;
501
502         /* recycle memory */
503         free(watch);
504 }
505
506 /*
507  * Creates an event of name 'fullname' and returns it or NULL on error.
508  */
509 struct afb_evtid *afb_evt_evtid_create(const char *fullname)
510 {
511         size_t len;
512         struct afb_evtid *evtid, *oevt;
513
514         /* allocates the event */
515         len = strlen(fullname);
516         evtid = malloc(len + 1 + sizeof * evtid);
517         if (evtid == NULL)
518                 goto error;
519
520         /* allocates the id */
521         pthread_rwlock_wrlock(&events_rwlock);
522         do {
523                 if (++event_id_counter < 0) {
524                         event_id_wrapped = 1;
525                         event_id_counter = 1024; /* heuristic: small numbers are not destroyed */
526                 }
527                 if (!event_id_wrapped)
528                         break;
529                 oevt = evtids;
530                 while(oevt != NULL && oevt->id != event_id_counter)
531                         oevt = oevt->next;
532         } while (oevt != NULL);
533
534         /* initialize the event */
535         memcpy(evtid->fullname, fullname, len + 1);
536         evtid->next = evtids;
537         evtid->refcount = 1;
538         evtid->watchs = NULL;
539         evtid->id = event_id_counter;
540         evtid->has_client = 0;
541         pthread_rwlock_init(&evtid->rwlock, NULL);
542         evtids = evtid;
543 #if WITH_AFB_HOOK
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);
548 #else
549         evtid->eventid.itf = &afb_evt_event_x2_itf;
550 #endif
551         pthread_rwlock_unlock(&events_rwlock);
552
553         /* returns the event */
554         return evtid;
555 error:
556         return NULL;
557 }
558
559 /*
560  * Creates an event of name 'prefix'/'name' and returns it or NULL on error.
561  */
562 struct afb_evtid *afb_evt_evtid_create2(const char *prefix, const char *name)
563 {
564         size_t prelen, postlen;
565         char *fullname;
566
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);
574
575         /* create the event */
576         return afb_evt_evtid_create(fullname);
577 }
578
579 /*
580  * increment the reference count of the event 'evtid'
581  */
582 struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid)
583 {
584         __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED);
585         return evtid;
586 }
587
588 #if WITH_AFB_HOOK
589 /*
590  * increment the reference count of the event 'evtid'
591  */
592 struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid)
593 {
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);
597 }
598 #endif
599
600 /*
601  * decrement the reference count of the event 'evtid'
602  * and destroy it when the count reachs zero
603  */
604 void afb_evt_evtid_unref(struct afb_evtid *evtid)
605 {
606         int found;
607         struct afb_evtid **prv;
608         struct afb_evt_listener *listener;
609
610         if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) {
611                 /* unlinks the event if valid! */
612                 pthread_rwlock_wrlock(&events_rwlock);
613                 found = 0;
614                 prv = &evtids;
615                 while (*prv && !(found = (*prv == evtid)))
616                         prv = &(*prv)->next;
617                 if (found)
618                         *prv = evtid->next;
619                 pthread_rwlock_unlock(&events_rwlock);
620
621                 /* destroys the event */
622                 if (!found)
623                         ERROR("event not found");
624                 else {
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);
633                         }
634
635                         /* free */
636                         pthread_rwlock_destroy(&evtid->rwlock);
637                         free(evtid);
638                 }
639         }
640 }
641
642 #if WITH_AFB_HOOK
643 /*
644  * decrement the reference count of the event 'evtid'
645  * and destroy it when the count reachs zero
646  */
647 void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid)
648 {
649         if (evtid->hookflags & afb_hook_flag_evt_unref)
650                 afb_hook_evt_unref(evtid->fullname, evtid->id);
651         afb_evt_evtid_unref(evtid);
652 }
653 #endif
654
655 /*
656  * Returns the true name of the 'event'
657  */
658 const char *afb_evt_evtid_fullname(struct afb_evtid *evtid)
659 {
660         return evtid->fullname;
661 }
662
663 /*
664  * Returns the name of the 'event'
665  */
666 const char *afb_evt_evtid_name(struct afb_evtid *evtid)
667 {
668         const char *name = strchr(evtid->fullname, '/');
669         return name ? name + 1 : evtid->fullname;
670 }
671
672 #if WITH_AFB_HOOK
673 /*
674  * Returns the name associated to the event 'evtid'.
675  */
676 const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid)
677 {
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);
681         return result;
682 }
683 #endif
684
685 /*
686  * Returns the id of the 'event'
687  */
688 int afb_evt_evtid_id(struct afb_evtid *evtid)
689 {
690         return evtid->id;
691 }
692
693 /*
694  * Returns an instance of the listener defined by the 'send' callback
695  * and the 'closure'.
696  * Returns NULL in case of memory depletion.
697  */
698 struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, void *closure)
699 {
700         struct afb_evt_listener *listener;
701
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);
708                         goto found;
709                 }
710                 listener = listener->next;
711         }
712
713         /* allocates */
714         listener = calloc(1, sizeof *listener);
715         if (listener != NULL) {
716                 /* init */
717                 listener->itf = itf;
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;
724         }
725  found:
726         pthread_rwlock_unlock(&listeners_rwlock);
727         return listener;
728 }
729
730 /*
731  * Increases the reference count of 'listener' and returns it
732  */
733 struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listener)
734 {
735         __atomic_add_fetch(&listener->refcount, 1, __ATOMIC_RELAXED);
736         return listener;
737 }
738
739 /*
740  * Decreases the reference count of the 'listener' and destroys it
741  * when no more used.
742  */
743 void afb_evt_listener_unref(struct afb_evt_listener *listener)
744 {
745         struct afb_evt_listener **prv;
746         struct afb_evtid *evtid;
747
748         if (listener && !__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) {
749
750                 /* unlink the listener */
751                 pthread_rwlock_wrlock(&listeners_rwlock);
752                 prv = &listeners;
753                 while (*prv != listener)
754                         prv = &(*prv)->next;
755                 *prv = listener->next;
756                 pthread_rwlock_unlock(&listeners_rwlock);
757
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);
765                 }
766                 pthread_rwlock_unlock(&listener->rwlock);
767
768                 /* free the listener */
769                 pthread_rwlock_destroy(&listener->rwlock);
770                 free(listener);
771         }
772 }
773
774 /*
775  * Makes the 'listener' watching 'evtid'
776  * Returns 0 in case of success or else -1.
777  */
778 int afb_evt_watch_add_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
779 {
780         struct afb_evt_watch *watch;
781
782         /* check parameter */
783         if (listener->itf->push == NULL) {
784                 errno = EINVAL;
785                 return -1;
786         }
787
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)
793                         goto found;
794                 watch = watch->next_by_listener;
795         }
796
797         /* not found, allocate a new */
798         watch = malloc(sizeof *watch);
799         if (watch == NULL) {
800                 pthread_rwlock_unlock(&listener->rwlock);
801                 errno = ENOMEM;
802                 return -1;
803         }
804
805         /* initialise and link */
806         watch->evtid = evtid;
807         watch->activity = 0;
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);
815
816 found:
817         if (watch->activity == 0 && listener->itf->add != NULL)
818                 listener->itf->add(listener->closure, evtid->fullname, evtid->id);
819         watch->activity++;
820         evtid->has_client = 1;
821         pthread_rwlock_unlock(&listener->rwlock);
822
823         return 0;
824 }
825
826 /*
827  * Avoids the 'listener' to watch 'evtid'
828  * Returns 0 in case of success or else -1.
829  */
830 int afb_evt_watch_sub_evtid(struct afb_evt_listener *listener, struct afb_evtid *evtid)
831 {
832         struct afb_evt_watch *watch;
833
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) {
840                                 watch->activity--;
841                                 if (watch->activity == 0 && listener->itf->remove != NULL)
842                                         listener->itf->remove(listener->closure, evtid->fullname, evtid->id);
843                         }
844                         pthread_rwlock_unlock(&listener->rwlock);
845                         return 0;
846                 }
847                 watch = watch->next_by_listener;
848         }
849         pthread_rwlock_unlock(&listener->rwlock);
850         errno = ENOENT;
851         return -1;
852 }
853
854 #if WITH_AFB_HOOK
855 /*
856  * update the hooks for events
857  */
858 void afb_evt_update_hooks()
859 {
860         struct afb_evtid *evtid;
861
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;
866         }
867         pthread_rwlock_unlock(&events_rwlock);
868 }
869 #endif
870
871 inline struct afb_evtid *afb_evt_event_x2_to_evtid(struct afb_event_x2 *eventid)
872 {
873         return (struct afb_evtid*)eventid;
874 }
875
876 inline struct afb_event_x2 *afb_evt_event_x2_from_evtid(struct afb_evtid *evtid)
877 {
878         return &evtid->eventid;
879 }
880
881 /*
882  * Creates an event of 'fullname' and returns it.
883  * Returns an event with closure==NULL in case of error.
884  */
885 struct afb_event_x2 *afb_evt_event_x2_create(const char *fullname)
886 {
887         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create(fullname));
888 }
889
890 /*
891  * Creates an event of name 'prefix'/'name' and returns it.
892  * Returns an event with closure==NULL in case of error.
893  */
894 struct afb_event_x2 *afb_evt_event_x2_create2(const char *prefix, const char *name)
895 {
896         return afb_evt_event_x2_from_evtid(afb_evt_evtid_create2(prefix, name));
897 }
898
899 /*
900  * Returns the fullname of the 'eventid'
901  */
902 const char *afb_evt_event_x2_fullname(struct afb_event_x2 *eventid)
903 {
904         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
905         return evtid ? evtid->fullname : NULL;
906 }
907
908 /*
909  * Returns the id of the 'eventid'
910  */
911 int afb_evt_event_x2_id(struct afb_event_x2 *eventid)
912 {
913         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
914         return evtid ? evtid->id : 0;
915 }
916
917 /*
918  * Makes the 'listener' watching 'eventid'
919  * Returns 0 in case of success or else -1.
920  */
921 int afb_evt_event_x2_add_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
922 {
923         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
924
925         /* check parameter */
926         if (!evtid) {
927                 errno = EINVAL;
928                 return -1;
929         }
930
931         /* search the existing watch for the listener */
932         return afb_evt_watch_add_evtid(listener, evtid);
933 }
934
935 /*
936  * Avoids the 'listener' to watch 'eventid'
937  * Returns 0 in case of success or else -1.
938  */
939 int afb_evt_event_x2_remove_watch(struct afb_evt_listener *listener, struct afb_event_x2 *eventid)
940 {
941         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
942
943         /* check parameter */
944         if (!evtid) {
945                 errno = EINVAL;
946                 return -1;
947         }
948
949         /* search the existing watch */
950         return afb_evt_watch_sub_evtid(listener, evtid);
951 }
952
953 int afb_evt_event_x2_push(struct afb_event_x2 *eventid, struct json_object *object)
954 #if WITH_AFB_HOOK
955 {
956         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
957         if (evtid)
958                 return afb_evt_evtid_hooked_push(evtid, object);
959         json_object_put(object);
960         return 0;
961 }
962 #else
963         __attribute__((alias("afb_evt_event_x2_unhooked_push")));
964 #endif
965
966 int afb_evt_event_x2_unhooked_push(struct afb_event_x2 *eventid, struct json_object *object)
967 {
968         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
969         if (evtid)
970                 return afb_evt_evtid_push(evtid, object);
971         json_object_put(object);
972         return 0;
973 }
974
975 #if WITH_LEGACY_BINDING_V1 || WITH_LEGACY_BINDING_V2
976 struct afb_event_x1 afb_evt_event_from_evtid(struct afb_evtid *evtid)
977 {
978         return evtid
979 #if WITH_AFB_HOOK
980                 ? (struct afb_event_x1){ .itf = &afb_evt_hooked_event_x2_itf, .closure = &evtid->eventid }
981 #else
982                 ? (struct afb_event_x1){ .itf = &afb_evt_event_x2_itf, .closure = &evtid->eventid }
983 #endif
984                 : (struct afb_event_x1){ .itf = NULL, .closure = NULL };
985 }
986 #endif
987
988 void afb_evt_event_x2_unref(struct afb_event_x2 *eventid)
989 {
990         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
991         if (evtid)
992                 afb_evt_evtid_unref(evtid);
993 }
994
995 struct afb_event_x2 *afb_evt_event_x2_addref(struct afb_event_x2 *eventid)
996 {
997         struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid);
998         if (evtid)
999                 afb_evt_evtid_addref(evtid);
1000         return eventid;
1001 }
1002