diff --git a/src/fastevent.c b/src/fastevent.c index be8e05b2..5022c698 100644 --- a/src/fastevent.c +++ b/src/fastevent.c @@ -14,8 +14,201 @@ #include #endif +#include +#include + +#include "common/thread/thread.h" + #include "fastevent.h" #include "logging.h" #define CATMODULE "fastevent" +#ifdef FASTEVENT_ENABLED + +struct registration { + refobject_base_t __base; + + fastevent_type_t type; + fastevent_cb_t cb; + fastevent_freecb_t freecb; + void *userdata; +}; + +struct eventrow { + size_t length; + size_t used; + struct registration **registrations; +}; + +static struct eventrow fastevent_registrations[FASTEVENT_TYPE__END]; +static rwlock_t fastevent_lock; + +static inline struct eventrow * __get_row(fastevent_type_t type) +{ + size_t idx = type; + + if (idx >= FASTEVENT_TYPE__END) + return NULL; + + return &(fastevent_registrations[idx]); +} + +static int __add_to_row(struct eventrow * row, struct registration *registration) +{ + struct registration **n; + + if (row == NULL) + return -1; + + /* Check if we need to reallocate row space */ + if (row->length == row->used) { + n = realloc(row->registrations, sizeof(*n)*(row->length + 4)); + if (n == NULL) { + ICECAST_LOG_ERROR("Can not allocate row space."); + return -1; + } + + row->registrations = n; + row->length += 4; + } + + row->registrations[row->used++] = registration; + return 0; +} + +static int __remove_from_row(struct eventrow * row, struct registration *registration) +{ + size_t i; + + if (row == NULL) + return -1; + + for (i = 0; i < row->used; i++) { + if (row->registrations[i] == registration) { + memmove(&(row->registrations[i]), &(row->registrations[i+1]), sizeof(*(row->registrations))*(row->used - i - 1)); + row->used--; + return 0; + } + } + + return -1; +} + + +static void __unregister(refobject_t self, void **userdata) +{ + struct registration *registration = REFOBJECT_TO_TYPE(self, struct registration *); + struct eventrow * row; + + (void)userdata; + + thread_rwlock_wlock(&fastevent_lock); + row = __get_row(registration->type); + if (__remove_from_row(row, registration) != 0) { + ICECAST_LOG_ERROR("Can not remove fast event from row. BUG."); + } + thread_rwlock_unlock(&fastevent_lock); + + if (registration->freecb) + registration->freecb(&(registration->userdata)); + + if (registration->userdata != NULL) + free(registration->userdata); +} + +int fastevent_initialize(void) +{ + thread_rwlock_create(&fastevent_lock); + return 0; +} + +int fastevent_shutdown(void) +{ + size_t i; + + thread_rwlock_wlock(&fastevent_lock); + for (i = 0; i < FASTEVENT_TYPE__END; i++) { + if (fastevent_registrations[i].used) { + ICECAST_LOG_ERROR("Subsystem shutdown but elements still in use. BUG."); + continue; + } + + free(fastevent_registrations[i].registrations); + fastevent_registrations[i].registrations = NULL; + } + thread_rwlock_unlock(&fastevent_lock); + thread_rwlock_destroy(&fastevent_lock); + + return 0; +} + +refobject_t fastevent_register(fastevent_type_t type, fastevent_cb_t cb, fastevent_freecb_t freecb, void *userdata) +{ + struct eventrow * row; + struct registration *registration; + refobject_t ret; + + if (cb == NULL) + return REFOBJECT_NULL; + + thread_rwlock_wlock(&fastevent_lock); + row = __get_row(type); + + if (row == NULL) { + thread_rwlock_unlock(&fastevent_lock); + return REFOBJECT_NULL; + } + + ret = refobject_new(sizeof(struct registration), __unregister, NULL, NULL, NULL); + + if (REFOBJECT_IS_NULL(ret)) { + thread_rwlock_unlock(&fastevent_lock); + return REFOBJECT_NULL; + } + + registration = REFOBJECT_TO_TYPE(ret, struct registration *); + + registration->type = type; + registration->cb = cb; + registration->freecb = freecb; + registration->userdata = userdata; + + if (__add_to_row(row, registration) != 0) { + thread_rwlock_unlock(&fastevent_lock); + refobject_unref(ret); + return REFOBJECT_NULL; + } + + thread_rwlock_unlock(&fastevent_lock); + return ret; +} + +void fastevent_emit(fastevent_type_t type, fastevent_flag_t flags, fastevent_datatype_t datatype, ...) +{ + struct eventrow * row; + va_list ap, apx; + size_t i; + + ICECAST_LOG_DEBUG("event: type=%i, flags=%i, datatype=%i, ...", (int)type, (int)flags, (int)datatype); + + thread_rwlock_rlock(&fastevent_lock); + row = __get_row(type); + if (row == NULL || row->used == 0) { + thread_rwlock_unlock(&fastevent_lock); + return; + } + + va_start(ap, datatype); + + for (i = 0; i < row->used; i++) { + va_copy(apx, ap); + row->registrations[i]->cb(row->registrations[i]->userdata, type, flags, datatype, apx); + va_end(apx); + } + + thread_rwlock_unlock(&fastevent_lock); + + va_end(ap); +} +#endif diff --git a/src/fastevent.h b/src/fastevent.h index 6b07a5ec..594f1d9d 100644 --- a/src/fastevent.h +++ b/src/fastevent.h @@ -9,4 +9,43 @@ #ifndef __FASTEVENT_H__ #define __FASTEVENT_H__ +/* Add all conditions when to enable fast events here. */ +#if 1 +#define FASTEVENT_ENABLED +#endif + +#include +#include + +typedef enum { + FASTEVENT_TYPE_SLOWEVENT = 0, + FASTEVENT_TYPE__END /* must be last element */ +} fastevent_type_t; + +typedef enum { + FASTEVENT_DATATYPE_NONE = 0, + FASTEVENT_DATATYPE_EVENT, + FASTEVENT_DATATYPE_CLIENT, + FASTEVENT_DATATYPE_CONNECTION +} fastevent_datatype_t; + +typedef int fastevent_flag_t; +#define FASTEVENT_FLAG_NONE ((fastevent_flag_t)0x0000) +#define FASTEVENT_FLAG_MODIFICATION_ALLOWED ((fastevent_flag_t)0x0001) + +typedef void (*fastevent_cb_t)(const void *userdata, fastevent_type_t type, fastevent_flag_t flags, fastevent_datatype_t datatype, va_list ap); +typedef void (*fastevent_freecb_t)(void **userdata); + +#ifdef FASTEVENT_ENABLED +int fastevent_initialize(void); +int fastevent_shutdown(void); +refobject_t fastevent_register(fastevent_type_t type, fastevent_cb_t cb, fastevent_freecb_t freecb, void *userdata); +void fastevent_emit(fastevent_type_t type, fastevent_flag_t flags, fastevent_datatype_t datatype, ...); +#else +#define fastevent_initialize() 0 +#define fastevent_shutdown() 0 +#define fastevent_register(type,cb,freecb,userdata) REFOBJECT_NULL +#define fastevent_emit(type,flags,datatype,...) +#endif + #endif