X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=blobdiff_plain;f=src%2Fafb-evt.c;h=f75997f69cb200101b11e300d4c3f3ae28b7b8c9;hp=d80af04524675437d9db67980fd57e16556943b0;hb=65353dce81a629e042800bb7b86fcd869a76727e;hpb=b67e18b39830a01750721787bf3bdc5d71983144 diff --git a/src/afb-evt.c b/src/afb-evt.c index d80af045..f75997f6 100644 --- a/src/afb-evt.c +++ b/src/afb-evt.c @@ -1,6 +1,5 @@ /* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" - * Author "Fulup Ar Foll" + * Copyright (C) 2015-2020 "IoT.bzh" * Author José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,11 +24,14 @@ #include #include -#include +#include +#include #include "afb-evt.h" #include "afb-hook.h" #include "verbose.h" +#include "jobs.h" +#include "uuid.h" struct afb_evt_watch; @@ -50,35 +52,43 @@ struct afb_evt_listener { /* head of the list of events listened */ struct afb_evt_watch *watchs; - /* mutex of the listener */ - pthread_mutex_t mutex; + /* rwlock of the listener */ + pthread_rwlock_t rwlock; /* count of reference to the listener */ - int refcount; + uint16_t refcount; }; /* * Structure for describing events */ -struct afb_evt_event { +struct afb_evtid { + + /* interface */ + struct afb_event_x2 eventid; /* next event */ - struct afb_evt_event *next; + struct afb_evtid *next; /* head of the list of listeners watching the event */ struct afb_evt_watch *watchs; - /* id of the event */ - int id; + /* rwlock of the event */ + pthread_rwlock_t rwlock; +#if WITH_AFB_HOOK /* hooking */ int hookflags; +#endif - /* mutex of the event */ - pthread_mutex_t mutex; + /* refcount */ + uint16_t refcount; + + /* id of the event */ + uint16_t id; - /* name of the event */ - char name[1]; + /* fullname of the event */ + char fullname[]; }; /* @@ -86,84 +96,298 @@ struct afb_evt_event { */ struct afb_evt_watch { - /* the event */ - struct afb_evt_event *event; + /* the evtid */ + struct afb_evtid *evtid; - /* link to the next listener for the same event */ - struct afb_evt_watch *next_by_event; + /* link to the next watcher for the same evtid */ + struct afb_evt_watch *next_by_evtid; /* the listener */ struct afb_evt_listener *listener; - /* link to the next event for the same listener */ + /* link to the next watcher for the same listener */ struct afb_evt_watch *next_by_listener; +}; + +/* + * structure for job of broadcasting events + */ +struct job_broadcast +{ + /** object atached to the event */ + struct json_object *object; + + /** the uuid of the event */ + uuid_binary_t uuid; + + /** remaining hop */ + uint8_t hop; - /* activity */ - unsigned activity; + /** name of the event to broadcast */ + char event[]; }; -/* declare functions */ -static int evt_broadcast(struct afb_evt_event *evt, struct json_object *obj); -static int evt_push(struct afb_evt_event *evt, struct json_object *obj); -static void evt_destroy(struct afb_evt_event *evt); -static const char *evt_name(struct afb_evt_event *evt); +/* + * structure for job of broadcasting or pushing events + */ +struct job_evtid +{ + /** the event to broadcast */ + struct afb_evtid *evtid; + + /** object atached to the event */ + struct json_object *object; +}; /* the interface for events */ -static struct afb_event_itf afb_evt_event_itf = { - .broadcast = (void*)evt_broadcast, - .push = (void*)evt_push, - .drop = (void*)evt_destroy, - .name = (void*)evt_name +static struct afb_event_x2_itf afb_evt_event_x2_itf = { + .broadcast = (void*)afb_evt_evtid_broadcast, + .push = (void*)afb_evt_evtid_push, + .unref = (void*)afb_evt_evtid_unref, + .name = (void*)afb_evt_evtid_name, + .addref = (void*)afb_evt_evtid_addref }; +#if WITH_AFB_HOOK +/* the interface for events */ +static struct afb_event_x2_itf afb_evt_hooked_event_x2_itf = { + .broadcast = (void*)afb_evt_evtid_hooked_broadcast, + .push = (void*)afb_evt_evtid_hooked_push, + .unref = (void*)afb_evt_evtid_hooked_unref, + .name = (void*)afb_evt_evtid_hooked_name, + .addref = (void*)afb_evt_evtid_hooked_addref +}; +#endif + +/* job groups for events push/broadcast */ +#define BROADCAST_JOB_GROUP (&afb_evt_event_x2_itf) +#define PUSH_JOB_GROUP (&afb_evt_event_x2_itf) + /* head of the list of listeners */ -static pthread_mutex_t listeners_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_rwlock_t listeners_rwlock = PTHREAD_RWLOCK_INITIALIZER; static struct afb_evt_listener *listeners = NULL; /* handling id of events */ -static pthread_mutex_t events_mutex = PTHREAD_MUTEX_INITIALIZER; -static struct afb_evt_event *events = NULL; -static int event_id_counter = 0; -static int event_id_wrapped = 0; +static pthread_rwlock_t events_rwlock = PTHREAD_RWLOCK_INITIALIZER; +static struct afb_evtid *evtids = NULL; +static uint16_t event_genid = 0; +static uint16_t event_count = 0; + +/* head of uniqueness of events */ +#if !defined(EVENT_BROADCAST_HOP_MAX) +# define EVENT_BROADCAST_HOP_MAX 10 +#endif +#if !defined(EVENT_BROADCAST_MEMORY_COUNT) +# define EVENT_BROADCAST_MEMORY_COUNT 8 +#endif + +#if EVENT_BROADCAST_MEMORY_COUNT +static struct { + pthread_mutex_t mutex; + uint8_t base; + uint8_t count; + uuid_binary_t uuids[EVENT_BROADCAST_MEMORY_COUNT]; +} uniqueness = { + .mutex = PTHREAD_MUTEX_INITIALIZER, + .base = 0, + .count = 0 +}; +#endif /* - * Broadcasts the 'event' of 'id' with its 'obj' - * 'obj' is released (like json_object_put) - * calls hooks if hookflags isn't 0 - * Returns the count of listener having receive the event. + * Create structure for job of broadcasting string 'event' with 'object' + * Returns the created structure or NULL if out of memory */ -static int broadcast(const char *event, struct json_object *obj, int id, int hookflags) +static struct job_broadcast *make_job_broadcast(const char *event, struct json_object *object, const uuid_binary_t uuid, uint8_t hop) +{ + size_t sz = 1 + strlen(event); + struct job_broadcast *jb = malloc(sz + sizeof *jb); + if (jb) { + jb->object = object; + memcpy(jb->uuid, uuid, sizeof jb->uuid); + jb->hop = hop; + memcpy(jb->event, event, sz); + } + return jb; +} + +/* + * Destroy structure 'jb' for job of broadcasting string events + */ +static void destroy_job_broadcast(struct job_broadcast *jb) +{ + json_object_put(jb->object); + free(jb); +} + +/* + * Create structure for job of broadcasting or pushing 'evtid' with 'object' + * Returns the created structure or NULL if out of memory + */ +static struct job_evtid *make_job_evtid(struct afb_evtid *evtid, struct json_object *object) +{ + struct job_evtid *je = malloc(sizeof *je); + if (je) { + je->evtid = afb_evt_evtid_addref(evtid); + je->object = object; + } + return je; +} + +/* + * Destroy structure for job of broadcasting or pushing evtid + */ +static void destroy_job_evtid(struct job_evtid *je) +{ + afb_evt_evtid_unref(je->evtid); + json_object_put(je->object); + free(je); +} + +/* + * Broadcasts the 'event' of 'id' with its 'object' + */ +static void broadcast(struct job_broadcast *jb) { - int result; struct afb_evt_listener *listener; - if (hookflags & afb_hook_flag_evt_broadcast_before) - afb_hook_evt_broadcast_before(event, id, obj); - result = 0; - pthread_mutex_lock(&listeners_mutex); + pthread_rwlock_rdlock(&listeners_rwlock); listener = listeners; while(listener) { - if (listener->itf->broadcast != NULL) { - listener->itf->broadcast(listener->closure, event, id, json_object_get(obj)); - result++; - } + if (listener->itf->broadcast != NULL) + listener->itf->broadcast(listener->closure, jb->event, json_object_get(jb->object), jb->uuid, jb->hop); listener = listener->next; } - pthread_mutex_unlock(&listeners_mutex); - if (hookflags & afb_hook_flag_evt_broadcast_after) - afb_hook_evt_broadcast_after(event, id, obj, result); - json_object_put(obj); - return result; + pthread_rwlock_unlock(&listeners_rwlock); +} + +/* + * Jobs callback for broadcasting string asynchronously + */ +static void broadcast_job(int signum, void *closure) +{ + struct job_broadcast *jb = closure; + + if (signum == 0) + broadcast(jb); + destroy_job_broadcast(jb); +} + +/* + * Broadcasts the string 'event' with its 'object' + */ +static int unhooked_broadcast(const char *event, struct json_object *object, const uuid_binary_t uuid, uint8_t hop) +{ + uuid_binary_t local_uuid; + struct job_broadcast *jb; + int rc; +#if EVENT_BROADCAST_MEMORY_COUNT + int iter, count; +#endif + + /* check if lately sent */ + if (!uuid) { + uuid_new_binary(local_uuid); + uuid = local_uuid; + hop = EVENT_BROADCAST_HOP_MAX; +#if EVENT_BROADCAST_MEMORY_COUNT + pthread_mutex_lock(&uniqueness.mutex); + } else { + pthread_mutex_lock(&uniqueness.mutex); + iter = (int)uniqueness.base; + count = (int)uniqueness.count; + while (count) { + if (0 == memcmp(uuid, uniqueness.uuids[iter], sizeof(uuid_binary_t))) { + pthread_mutex_unlock(&uniqueness.mutex); + return 0; + } + if (++iter == EVENT_BROADCAST_MEMORY_COUNT) + iter = 0; + count--; + } + } + iter = (int)uniqueness.base; + if (uniqueness.count < EVENT_BROADCAST_MEMORY_COUNT) + iter += (int)(uniqueness.count++); + else if (++uniqueness.base == EVENT_BROADCAST_MEMORY_COUNT) + uniqueness.base = 0; + memcpy(uniqueness.uuids[iter], uuid, sizeof(uuid_binary_t)); + pthread_mutex_unlock(&uniqueness.mutex); +#else + } +#endif + + /* create the structure for the job */ + jb = make_job_broadcast(event, object, uuid, hop); + if (jb == NULL) { + ERROR("Cant't create broadcast string job item for %s(%s)", + event, json_object_to_json_string(object)); + json_object_put(object); + return -1; + } + + /* queue the job */ + rc = jobs_queue(BROADCAST_JOB_GROUP, 0, broadcast_job, jb); + if (rc) { + ERROR("cant't queue broadcast string job item for %s(%s)", + event, json_object_to_json_string(object)); + destroy_job_broadcast(jb); + } + return rc; +} + +/* + * Broadcasts the event 'evtid' with its 'object' + * 'object' is released (like json_object_put) + * Returns the count of listener that received the event. + */ +int afb_evt_evtid_broadcast(struct afb_evtid *evtid, struct json_object *object) +{ + return unhooked_broadcast(evtid->fullname, object, NULL, 0); } +#if WITH_AFB_HOOK /* - * Broadcasts the event 'evt' with its 'object' + * Broadcasts the event 'evtid' with its 'object' * 'object' is released (like json_object_put) * Returns the count of listener that received the event. */ -static int evt_broadcast(struct afb_evt_event *evt, struct json_object *object) +int afb_evt_evtid_hooked_broadcast(struct afb_evtid *evtid, struct json_object *object) +{ + int result; + + json_object_get(object); + + if (evtid->hookflags & afb_hook_flag_evt_broadcast_before) + afb_hook_evt_broadcast_before(evtid->fullname, evtid->id, object); + + result = afb_evt_evtid_broadcast(evtid, object); + + if (evtid->hookflags & afb_hook_flag_evt_broadcast_after) + afb_hook_evt_broadcast_after(evtid->fullname, evtid->id, object, result); + + json_object_put(object); + + return result; +} +#endif + +int afb_evt_rebroadcast(const char *event, struct json_object *object, const uuid_binary_t uuid, uint8_t hop) { - return broadcast(evt->name, object, evt->id, evt->hookflags); + int result; + +#if WITH_AFB_HOOK + json_object_get(object); + afb_hook_evt_broadcast_before(event, 0, object); +#endif + + result = unhooked_broadcast(event, object, uuid, hop); + +#if WITH_AFB_HOOK + afb_hook_evt_broadcast_after(event, 0, object, result); + json_object_put(object); +#endif + return result; } /* @@ -173,190 +397,353 @@ static int evt_broadcast(struct afb_evt_event *evt, struct json_object *object) */ int afb_evt_broadcast(const char *event, struct json_object *object) { - return broadcast(event, object, 0, -1); + return afb_evt_rebroadcast(event, object, NULL, 0); } /* - * Pushes the event 'evt' with 'obj' to its listeners - * 'obj' is released (like json_object_put) - * Returns the count of listener taht received the event. + * Pushes the event 'evtid' with 'obj' to its listeners + * Returns the count of listener that received the event. */ -static int evt_push(struct afb_evt_event *evt, struct json_object *obj) +static void push_evtid(struct afb_evtid *evtid, struct json_object *object) { - int result; struct afb_evt_watch *watch; struct afb_evt_listener *listener; - result = 0; - pthread_mutex_lock(&evt->mutex); - if (evt->hookflags & afb_hook_flag_evt_push_before) - afb_hook_evt_push_before(evt->name, evt->id, obj); - watch = evt->watchs; + pthread_rwlock_rdlock(&evtid->rwlock); + watch = evtid->watchs; while(watch) { listener = watch->listener; assert(listener->itf->push != NULL); - if (watch->activity != 0) { - listener->itf->push(listener->closure, evt->name, evt->id, json_object_get(obj)); - result++; - } - watch = watch->next_by_event; + listener->itf->push(listener->closure, evtid->fullname, evtid->id, json_object_get(object)); + watch = watch->next_by_evtid; } - if (evt->hookflags & afb_hook_flag_evt_push_after) - afb_hook_evt_push_after(evt->name, evt->id, obj, result); - pthread_mutex_unlock(&evt->mutex); - json_object_put(obj); - return result; + pthread_rwlock_unlock(&evtid->rwlock); } /* - * Returns the name associated to the event 'evt'. + * Jobs callback for pushing evtid asynchronously */ -static const char *evt_name(struct afb_evt_event *evt) +static void push_job_evtid(int signum, void *closure) { - if (evt->hookflags & afb_hook_flag_evt_name) - afb_hook_evt_name(evt->name, evt->id); - return evt->name; + struct job_evtid *je = closure; + + if (signum == 0) + push_evtid(je->evtid, je->object); + destroy_job_evtid(je); } /* - * remove the 'watch' + * Pushes the event 'evtid' with 'obj' to its listeners + * 'obj' is released (like json_object_put) + * Returns 1 if at least one listener exists or 0 if no listener exists or + * -1 in case of error and the event can't be delivered */ -static void remove_watch(struct afb_evt_watch *watch) +int afb_evt_evtid_push(struct afb_evtid *evtid, struct json_object *object) +{ + struct job_evtid *je; + int rc; + + if (!evtid->watchs) + return 0; + + je = make_job_evtid(evtid, object); + if (je == NULL) { + ERROR("Cant't create push evtid job item for %s(%s)", + evtid->fullname, json_object_to_json_string(object)); + json_object_put(object); + return -1; + } + + rc = jobs_queue(PUSH_JOB_GROUP, 0, push_job_evtid, je); + if (rc == 0) + rc = 1; + else { + ERROR("cant't queue push evtid job item for %s(%s)", + evtid->fullname, json_object_to_json_string(object)); + destroy_job_evtid(je); + } + + return rc; +} + +#if WITH_AFB_HOOK +/* + * Pushes the event 'evtid' with 'obj' to its listeners + * 'obj' is released (like json_object_put) + * Emits calls to hooks. + * Returns the count of listener taht received the event. + */ +int afb_evt_evtid_hooked_push(struct afb_evtid *evtid, struct json_object *obj) +{ + + int result; + + /* lease the object */ + json_object_get(obj); + + /* hook before push */ + if (evtid->hookflags & afb_hook_flag_evt_push_before) + afb_hook_evt_push_before(evtid->fullname, evtid->id, obj); + + /* push */ + result = afb_evt_evtid_push(evtid, obj); + + /* hook after push */ + if (evtid->hookflags & afb_hook_flag_evt_push_after) + afb_hook_evt_push_after(evtid->fullname, evtid->id, obj, result); + + /* release the object */ + json_object_put(obj); + return result; +} +#endif + +static void unwatch(struct afb_evt_listener *listener, struct afb_evtid *evtid, int remove) +{ + /* notify listener if needed */ + if (remove && listener->itf->remove != NULL) + listener->itf->remove(listener->closure, evtid->fullname, evtid->id); +} + +static void evtid_unwatch(struct afb_evtid *evtid, struct afb_evt_listener *listener, struct afb_evt_watch *watch, int remove) { struct afb_evt_watch **prv; - struct afb_evt_event *evt; - struct afb_evt_listener *listener; /* notify listener if needed */ - evt = watch->event; - listener = watch->listener; - if (watch->activity != 0 && listener->itf->remove != NULL) - listener->itf->remove(listener->closure, evt->name, evt->id); + unwatch(listener, evtid, remove); /* unlink the watch for its event */ - prv = &evt->watchs; - while(*prv != watch) - prv = &(*prv)->next_by_event; - *prv = watch->next_by_event; - - /* unlink the watch for its listener */ + pthread_rwlock_wrlock(&listener->rwlock); prv = &listener->watchs; - while(*prv != watch) + while(*prv) { + if (*prv == watch) { + *prv = watch->next_by_listener; + break; + } prv = &(*prv)->next_by_listener; - *prv = watch->next_by_listener; + } + pthread_rwlock_unlock(&listener->rwlock); /* recycle memory */ free(watch); } -/* - * Destroys the event 'evt' - */ -static void evt_destroy(struct afb_evt_event *evt) +static void listener_unwatch(struct afb_evt_listener *listener, struct afb_evtid *evtid, struct afb_evt_watch *watch, int remove) { - int found; - struct afb_evt_event **prv; - struct afb_evt_listener *listener; - - if (evt != NULL) { - /* unlinks the event if valid! */ - pthread_mutex_lock(&events_mutex); - found = 0; - prv = &events; - while (*prv && !(found = (*prv == evt))) - prv = &(*prv)->next; - if (found) - *prv = evt->next; - pthread_mutex_unlock(&events_mutex); - - /* destroys the event */ - if (found) { - /* removes all watchers */ - while(evt->watchs != NULL) { - listener = evt->watchs->listener; - pthread_mutex_lock(&listener->mutex); - pthread_mutex_lock(&evt->mutex); - remove_watch(evt->watchs); - pthread_mutex_unlock(&evt->mutex); - pthread_mutex_unlock(&listener->mutex); - } + struct afb_evt_watch **prv; - /* hook */ - if (evt->hookflags & afb_hook_flag_evt_drop) - afb_hook_evt_drop(evt->name, evt->id); + /* notify listener if needed */ + unwatch(listener, evtid, remove); - /* free */ - pthread_mutex_destroy(&evt->mutex); - free(evt); + /* unlink the watch for its event */ + pthread_rwlock_wrlock(&evtid->rwlock); + prv = &evtid->watchs; + while(*prv) { + if (*prv == watch) { + *prv = watch->next_by_evtid; + break; } + prv = &(*prv)->next_by_evtid; } + pthread_rwlock_unlock(&evtid->rwlock); + + /* recycle memory */ + free(watch); } /* - * Creates an event of 'name' and returns it. - * Returns an event with closure==NULL in case of error. + * Creates an event of name 'fullname' and returns it or NULL on error. */ -struct afb_event afb_evt_create_event(const char *name) +struct afb_evtid *afb_evt_evtid_create(const char *fullname) { size_t len; - struct afb_evt_event *evt; + struct afb_evtid *evtid, *oevt; + uint16_t id; /* allocates the event */ - len = strlen(name); - evt = malloc(len + sizeof * evt); - if (evt == NULL) + len = strlen(fullname); + evtid = malloc(len + 1 + sizeof * evtid); + if (evtid == NULL) goto error; - /* initialize the event */ - evt->watchs = NULL; - memcpy(evt->name, name, len + 1); - /* allocates the id */ - pthread_mutex_lock(&events_mutex); + pthread_rwlock_wrlock(&events_rwlock); + if (event_count == UINT16_MAX) { + pthread_rwlock_unlock(&events_rwlock); + free(evtid); + ERROR("Can't create more events"); + return NULL; + } + event_count++; do { - if (++event_id_counter < 0) { - event_id_wrapped = 1; - event_id_counter = 1024; /* heuristic: small numbers are not destroyed */ - } - if (!event_id_wrapped) - break; - evt = events; - while(evt != NULL && evt->id != event_id_counter) - evt = evt->next; - } while (evt != NULL); + /* TODO add a guard (counting number of event created) */ + id = ++event_genid; + if (!id) + id = event_genid = 1; + oevt = evtids; + while(oevt != NULL && oevt->id != id) + oevt = oevt->next; + } while (oevt != NULL); /* initialize the event */ - memcpy(evt->name, name, len + 1); - evt->next = events; - evt->watchs = NULL; - evt->id = event_id_counter; - pthread_mutex_init(&evt->mutex, NULL); - events = evt; - evt->hookflags = afb_hook_flags_evt(evt->name); - if (evt->hookflags & afb_hook_flag_evt_create) - afb_hook_evt_create(evt->name, evt->id); - pthread_mutex_unlock(&events_mutex); + memcpy(evtid->fullname, fullname, len + 1); + evtid->next = evtids; + evtid->refcount = 1; + evtid->watchs = NULL; + evtid->id = id; + pthread_rwlock_init(&evtid->rwlock, NULL); + evtids = evtid; +#if WITH_AFB_HOOK + evtid->hookflags = afb_hook_flags_evt(evtid->fullname); + evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf; + if (evtid->hookflags & afb_hook_flag_evt_create) + afb_hook_evt_create(evtid->fullname, evtid->id); +#else + evtid->eventid.itf = &afb_evt_event_x2_itf; +#endif + pthread_rwlock_unlock(&events_rwlock); /* returns the event */ - return (struct afb_event){ .itf = &afb_evt_event_itf, .closure = evt }; + return evtid; error: - return (struct afb_event){ .itf = NULL, .closure = NULL }; + return NULL; +} + +/* + * Creates an event of name 'prefix'/'name' and returns it or NULL on error. + */ +struct afb_evtid *afb_evt_evtid_create2(const char *prefix, const char *name) +{ + size_t prelen, postlen; + char *fullname; + + /* makes the event fullname */ + prelen = strlen(prefix); + postlen = strlen(name); + fullname = alloca(prelen + postlen + 2); + memcpy(fullname, prefix, prelen); + fullname[prelen] = '/'; + memcpy(fullname + prelen + 1, name, postlen + 1); + + /* create the event */ + return afb_evt_evtid_create(fullname); +} + +/* + * increment the reference count of the event 'evtid' + */ +struct afb_evtid *afb_evt_evtid_addref(struct afb_evtid *evtid) +{ + __atomic_add_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED); + return evtid; +} + +#if WITH_AFB_HOOK +/* + * increment the reference count of the event 'evtid' + */ +struct afb_evtid *afb_evt_evtid_hooked_addref(struct afb_evtid *evtid) +{ + if (evtid->hookflags & afb_hook_flag_evt_addref) + afb_hook_evt_addref(evtid->fullname, evtid->id); + return afb_evt_evtid_addref(evtid); +} +#endif + +/* + * decrement the reference count of the event 'evtid' + * and destroy it when the count reachs zero + */ +void afb_evt_evtid_unref(struct afb_evtid *evtid) +{ + struct afb_evtid **prv, *oev; + struct afb_evt_watch *watch, *nwatch; + + if (!__atomic_sub_fetch(&evtid->refcount, 1, __ATOMIC_RELAXED)) { + /* unlinks the event if valid! */ + pthread_rwlock_wrlock(&events_rwlock); + prv = &evtids; + for(;;) { + oev = *prv; + if (oev == evtid) + break; + if (!oev) { + ERROR("unexpected event"); + pthread_rwlock_unlock(&events_rwlock); + return; + } + prv = &oev->next; + } + event_count--; + *prv = evtid->next; + pthread_rwlock_unlock(&events_rwlock); + + /* removes all watchers */ + pthread_rwlock_wrlock(&evtid->rwlock); + watch = evtid->watchs; + evtid->watchs = NULL; + pthread_rwlock_unlock(&evtid->rwlock); + while(watch) { + nwatch = watch->next_by_evtid; + evtid_unwatch(evtid, watch->listener, watch, 1); + watch = nwatch; + } + + /* free */ + pthread_rwlock_destroy(&evtid->rwlock); + free(evtid); + } +} + +#if WITH_AFB_HOOK +/* + * decrement the reference count of the event 'evtid' + * and destroy it when the count reachs zero + */ +void afb_evt_evtid_hooked_unref(struct afb_evtid *evtid) +{ + if (evtid->hookflags & afb_hook_flag_evt_unref) + afb_hook_evt_unref(evtid->fullname, evtid->id); + afb_evt_evtid_unref(evtid); +} +#endif + +/* + * Returns the true name of the 'event' + */ +const char *afb_evt_evtid_fullname(struct afb_evtid *evtid) +{ + return evtid->fullname; } /* * Returns the name of the 'event' */ -const char *afb_evt_event_name(struct afb_event event) +const char *afb_evt_evtid_name(struct afb_evtid *evtid) +{ + const char *name = strchr(evtid->fullname, '/'); + return name ? name + 1 : evtid->fullname; +} + +#if WITH_AFB_HOOK +/* + * Returns the name associated to the event 'evtid'. + */ +const char *afb_evt_evtid_hooked_name(struct afb_evtid *evtid) { - return (event.itf != &afb_evt_event_itf) ? NULL : ((struct afb_evt_event *)event.closure)->name; + const char *result = afb_evt_evtid_name(evtid); + if (evtid->hookflags & afb_hook_flag_evt_name) + afb_hook_evt_name(evtid->fullname, evtid->id, result); + return result; } +#endif /* * Returns the id of the 'event' */ -int afb_evt_event_id(struct afb_event event) +uint16_t afb_evt_evtid_id(struct afb_evtid *evtid) { - return (event.itf != &afb_evt_event_itf) ? 0 : ((struct afb_evt_event *)event.closure)->id; + return evtid->id; } /* @@ -369,7 +756,7 @@ struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, struct afb_evt_listener *listener; /* search if an instance already exists */ - pthread_mutex_lock(&listeners_mutex); + pthread_rwlock_wrlock(&listeners_rwlock); listener = listeners; while (listener != NULL) { if (listener->itf == itf && listener->closure == closure) { @@ -387,12 +774,12 @@ struct afb_evt_listener *afb_evt_listener_create(const struct afb_evt_itf *itf, listener->closure = closure; listener->watchs = NULL; listener->refcount = 1; - pthread_mutex_init(&listener->mutex, NULL); + pthread_rwlock_init(&listener->rwlock, NULL); listener->next = listeners; listeners = listener; } found: - pthread_mutex_unlock(&listeners_mutex); + pthread_rwlock_unlock(&listeners_rwlock); return listener; } @@ -411,134 +798,308 @@ struct afb_evt_listener *afb_evt_listener_addref(struct afb_evt_listener *listen */ void afb_evt_listener_unref(struct afb_evt_listener *listener) { - struct afb_evt_listener **prv; - struct afb_evt_event *evt; + struct afb_evt_listener **prv, *olis; - if (!__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) { + if (listener && !__atomic_sub_fetch(&listener->refcount, 1, __ATOMIC_RELAXED)) { /* unlink the listener */ - pthread_mutex_lock(&listeners_mutex); + pthread_rwlock_wrlock(&listeners_rwlock); prv = &listeners; - while (*prv != listener) - prv = &(*prv)->next; + for(;;) { + olis = *prv; + if (olis == listener) + break; + if (!olis) { + ERROR("unexpected listener"); + pthread_rwlock_unlock(&listeners_rwlock); + return; + } + prv = &olis->next; + } *prv = listener->next; - pthread_mutex_unlock(&listeners_mutex); + pthread_rwlock_unlock(&listeners_rwlock); /* remove the watchers */ - pthread_mutex_lock(&listener->mutex); - while (listener->watchs != NULL) { - evt = listener->watchs->event; - pthread_mutex_lock(&evt->mutex); - remove_watch(listener->watchs); - pthread_mutex_unlock(&evt->mutex); - } - pthread_mutex_unlock(&listener->mutex); + afb_evt_listener_unwatch_all(listener, 0); /* free the listener */ - pthread_mutex_destroy(&listener->mutex); + pthread_rwlock_destroy(&listener->rwlock); free(listener); } } /* - * Makes the 'listener' watching 'event' + * Makes the 'listener' watching 'evtid' * Returns 0 in case of success or else -1. */ -int afb_evt_add_watch(struct afb_evt_listener *listener, struct afb_event event) +int afb_evt_listener_watch_evt(struct afb_evt_listener *listener, struct afb_evtid *evtid) { struct afb_evt_watch *watch; - struct afb_evt_event *evt; /* check parameter */ - if (event.itf != &afb_evt_event_itf || listener->itf->push == NULL) { + if (listener->itf->push == NULL) { errno = EINVAL; return -1; } /* search the existing watch for the listener */ - evt = event.closure; - pthread_mutex_lock(&listener->mutex); + pthread_rwlock_wrlock(&listener->rwlock); watch = listener->watchs; while(watch != NULL) { - if (watch->event == evt) - goto found; + if (watch->evtid == evtid) + goto end; watch = watch->next_by_listener; } /* not found, allocate a new */ watch = malloc(sizeof *watch); if (watch == NULL) { - pthread_mutex_unlock(&listener->mutex); + pthread_rwlock_unlock(&listener->rwlock); errno = ENOMEM; return -1; } /* initialise and link */ - watch->event = evt; - watch->activity = 0; + watch->evtid = evtid; watch->listener = listener; watch->next_by_listener = listener->watchs; listener->watchs = watch; - pthread_mutex_lock(&evt->mutex); - watch->next_by_event = evt->watchs; - evt->watchs = watch; - pthread_mutex_unlock(&evt->mutex); - -found: - if (watch->activity == 0 && listener->itf->add != NULL) - listener->itf->add(listener->closure, evt->name, evt->id); - watch->activity++; - pthread_mutex_unlock(&listener->mutex); - + pthread_rwlock_wrlock(&evtid->rwlock); + watch->next_by_evtid = evtid->watchs; + evtid->watchs = watch; + pthread_rwlock_unlock(&evtid->rwlock); + + if (listener->itf->add != NULL) + listener->itf->add(listener->closure, evtid->fullname, evtid->id); +end: + pthread_rwlock_unlock(&listener->rwlock); return 0; } /* - * Avoids the 'listener' to watch 'event' + * Avoids the 'listener' to watch 'evtid' * Returns 0 in case of success or else -1. */ -int afb_evt_remove_watch(struct afb_evt_listener *listener, struct afb_event event) +int afb_evt_listener_unwatch_evt(struct afb_evt_listener *listener, struct afb_evtid *evtid) { - struct afb_evt_watch *watch; - struct afb_evt_event *evt; + struct afb_evt_watch *watch, **pwatch; - /* check parameter */ - if (event.itf != &afb_evt_event_itf) { - errno = EINVAL; - return -1; + /* search the existing watch */ + pthread_rwlock_wrlock(&listener->rwlock); + pwatch = &listener->watchs; + for (;;) { + watch = *pwatch; + if (!watch) { + pthread_rwlock_unlock(&listener->rwlock); + errno = ENOENT; + return -1; + } + if (evtid == watch->evtid) { + *pwatch = watch->next_by_listener; + pthread_rwlock_unlock(&listener->rwlock); + listener_unwatch(listener, evtid, watch, 1); + return 0; + } + pwatch = &watch->next_by_listener; } +} + +/* + * Avoids the 'listener' to watch 'eventid' + * Returns 0 in case of success or else -1. + */ +int afb_evt_listener_unwatch_id(struct afb_evt_listener *listener, uint16_t eventid) +{ + struct afb_evt_watch *watch, **pwatch; + struct afb_evtid *evtid; /* search the existing watch */ - evt = event.closure; - pthread_mutex_lock(&listener->mutex); - watch = listener->watchs; - while(watch != NULL) { - if (watch->event == evt) { - if (watch->activity != 0) { - watch->activity--; - if (watch->activity == 0 && listener->itf->remove != NULL) - listener->itf->remove(listener->closure, evt->name, evt->id); - } - pthread_mutex_unlock(&listener->mutex); + pthread_rwlock_wrlock(&listener->rwlock); + pwatch = &listener->watchs; + for (;;) { + watch = *pwatch; + if (!watch) { + pthread_rwlock_unlock(&listener->rwlock); + errno = ENOENT; + return -1; + } + evtid = watch->evtid; + if (evtid->id == eventid) { + *pwatch = watch->next_by_listener; + pthread_rwlock_unlock(&listener->rwlock); + listener_unwatch(listener, evtid, watch, 1); return 0; } - watch = watch->next_by_listener; + pwatch = &watch->next_by_listener; } - pthread_mutex_unlock(&listener->mutex); - errno = ENOENT; - return -1; } +/* + * Avoids the 'listener' to watch any event, calling the callback + * 'remove' of the interface if 'remoe' is not zero. + */ +void afb_evt_listener_unwatch_all(struct afb_evt_listener *listener, int remove) +{ + struct afb_evt_watch *watch, *nwatch; + + /* search the existing watch */ + pthread_rwlock_wrlock(&listener->rwlock); + watch = listener->watchs; + listener->watchs = NULL; + pthread_rwlock_unlock(&listener->rwlock); + while(watch) { + nwatch = watch->next_by_listener; + listener_unwatch(listener, watch->evtid, watch, remove); + watch = nwatch; + } +} + +#if WITH_AFB_HOOK /* * update the hooks for events */ void afb_evt_update_hooks() { - struct afb_evt_event *evt; + struct afb_evtid *evtid; + + pthread_rwlock_rdlock(&events_rwlock); + for (evtid = evtids ; evtid ; evtid = evtid->next) { + evtid->hookflags = afb_hook_flags_evt(evtid->fullname); + evtid->eventid.itf = evtid->hookflags ? &afb_evt_hooked_event_x2_itf : &afb_evt_event_x2_itf; + } + pthread_rwlock_unlock(&events_rwlock); +} +#endif + +inline struct afb_evtid *afb_evt_event_x2_to_evtid(struct afb_event_x2 *eventid) +{ + return (struct afb_evtid*)eventid; +} + +inline struct afb_event_x2 *afb_evt_event_x2_from_evtid(struct afb_evtid *evtid) +{ + return &evtid->eventid; +} + +/* + * Creates an event of 'fullname' and returns it. + * Returns an event with closure==NULL in case of error. + */ +struct afb_event_x2 *afb_evt_event_x2_create(const char *fullname) +{ + return afb_evt_event_x2_from_evtid(afb_evt_evtid_create(fullname)); +} + +/* + * Creates an event of name 'prefix'/'name' and returns it. + * Returns an event with closure==NULL in case of error. + */ +struct afb_event_x2 *afb_evt_event_x2_create2(const char *prefix, const char *name) +{ + return afb_evt_event_x2_from_evtid(afb_evt_evtid_create2(prefix, name)); +} + +/* + * Returns the fullname of the 'eventid' + */ +const char *afb_evt_event_x2_fullname(struct afb_event_x2 *eventid) +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + return evtid ? evtid->fullname : NULL; +} + +/* + * Returns the id of the 'eventid' + */ +uint16_t afb_evt_event_x2_id(struct afb_event_x2 *eventid) +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + return evtid ? evtid->id : 0; +} + +/* + * Makes the 'listener' watching 'eventid' + * Returns 0 in case of success or else -1. + */ +int afb_evt_listener_watch_x2(struct afb_evt_listener *listener, struct afb_event_x2 *eventid) +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + + /* check parameter */ + if (!evtid) { + errno = EINVAL; + return -1; + } + + /* search the existing watch for the listener */ + return afb_evt_listener_watch_evt(listener, evtid); +} + +/* + * Avoids the 'listener' to watch 'eventid' + * Returns 0 in case of success or else -1. + */ +int afb_evt_listener_unwatch_x2(struct afb_evt_listener *listener, struct afb_event_x2 *eventid) +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + + /* check parameter */ + if (!evtid) { + errno = EINVAL; + return -1; + } + + /* search the existing watch */ + return afb_evt_listener_unwatch_evt(listener, evtid); +} + +int afb_evt_event_x2_push(struct afb_event_x2 *eventid, struct json_object *object) +#if WITH_AFB_HOOK +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + if (evtid) + return afb_evt_evtid_hooked_push(evtid, object); + json_object_put(object); + return 0; +} +#else + __attribute__((alias("afb_evt_event_x2_unhooked_push"))); +#endif - pthread_mutex_lock(&events_mutex); - for (evt = events ; evt ; evt = evt->next) - evt->hookflags = afb_hook_flags_evt(evt->name); - pthread_mutex_unlock(&events_mutex); +int afb_evt_event_x2_unhooked_push(struct afb_event_x2 *eventid, struct json_object *object) +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + if (evtid) + return afb_evt_evtid_push(evtid, object); + json_object_put(object); + return 0; +} + +#if WITH_LEGACY_BINDING_V1 || WITH_LEGACY_BINDING_V2 +struct afb_event_x1 afb_evt_event_from_evtid(struct afb_evtid *evtid) +{ + return evtid +#if WITH_AFB_HOOK + ? (struct afb_event_x1){ .itf = &afb_evt_hooked_event_x2_itf, .closure = &evtid->eventid } +#else + ? (struct afb_event_x1){ .itf = &afb_evt_event_x2_itf, .closure = &evtid->eventid } +#endif + : (struct afb_event_x1){ .itf = NULL, .closure = NULL }; +} +#endif + +void afb_evt_event_x2_unref(struct afb_event_x2 *eventid) +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + if (evtid) + afb_evt_evtid_unref(evtid); +} + +struct afb_event_x2 *afb_evt_event_x2_addref(struct afb_event_x2 *eventid) +{ + struct afb_evtid *evtid = afb_evt_event_x2_to_evtid(eventid); + if (evtid) + afb_evt_evtid_addref(evtid); + return eventid; }