/* Icecast * * This program is distributed under the GNU General Public License, version 2. * A copy of this license is included with this source. * * Copyright 2018, Philipp "ph3-der-loewe" Schafft , */ /** * Special fast event functions */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "common/thread/thread.h" #include "fastevent.h" #include "logging.h" #define CATMODULE "fastevent" #ifdef FASTEVENT_ENABLED typedef struct { refobject_base_t __base; fastevent_type_t type; fastevent_cb_t cb; fastevent_freecb_t freecb; void *userdata; } fastevent_registration_t; struct eventrow { size_t length; size_t used; fastevent_registration_t **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, fastevent_registration_t *registration) { fastevent_registration_t **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, fastevent_registration_t *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) { fastevent_registration_t *registration = REFOBJECT_TO_TYPE(self, fastevent_registration_t *); 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_DEFINE_PRIVATE_TYPE(fastevent_registration_t, REFOBJECT_DEFINE_TYPE_FREE(__unregister) ); refobject_t fastevent_register(fastevent_type_t type, fastevent_cb_t cb, fastevent_freecb_t freecb, void *userdata) { struct eventrow * row; fastevent_registration_t *registration; 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; } registration = refobject_new__new(fastevent_registration_t, NULL, NULL, NULL); if (!registration) { thread_rwlock_unlock(&fastevent_lock); return REFOBJECT_NULL; } 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(REFOBJECT_FROM_TYPE(registration)); return REFOBJECT_NULL; } thread_rwlock_unlock(&fastevent_lock); return REFOBJECT_FROM_TYPE(registration); } 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_DDEBUG("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