fastevent.c 5.13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* 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 <lion@lion.leolix.org>,
 */

/**
 * Special fast event functions
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

17 18 19 20 21
#include <stdlib.h>
#include <string.h>

#include "common/thread/thread.h"

22 23 24 25 26
#include "fastevent.h"

#include "logging.h"
#define CATMODULE "fastevent"

27 28
#ifdef FASTEVENT_ENABLED

29
typedef struct {
30 31 32 33 34 35
    refobject_base_t __base;

    fastevent_type_t type;
    fastevent_cb_t cb;
    fastevent_freecb_t freecb;
    void *userdata;
36
} fastevent_registration_t;
37 38 39 40

struct eventrow {
    size_t length;
    size_t used;
41
    fastevent_registration_t **registrations;
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
};

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]);
}

57
static int __add_to_row(struct eventrow * row, fastevent_registration_t *registration)
58
{
59
    fastevent_registration_t **n;
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

    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;
}

80
static int __remove_from_row(struct eventrow * row, fastevent_registration_t *registration)
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
{
    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)
{
101
    fastevent_registration_t *registration = REFOBJECT_TO_TYPE(self, fastevent_registration_t *);
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    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;
}

146 147 148 149
REFOBJECT_DEFINE_PRIVATE_TYPE(fastevent_registration_t,
        REFOBJECT_DEFINE_TYPE_FREE(__unregister)
        );

150 151 152
refobject_t fastevent_register(fastevent_type_t type, fastevent_cb_t cb, fastevent_freecb_t freecb, void *userdata)
{
    struct eventrow * row;
153
    fastevent_registration_t *registration;
154 155 156 157 158 159 160 161 162 163 164 165

    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;
    }

166
    registration = refobject_new__new(fastevent_registration_t, NULL, NULL, NULL);
167

168
    if (!registration) {
169 170 171 172 173 174 175 176 177 178 179
        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);
180
        refobject_unref(REFOBJECT_FROM_TYPE(registration));
181 182 183 184
        return REFOBJECT_NULL;
    }

    thread_rwlock_unlock(&fastevent_lock);
185
    return REFOBJECT_FROM_TYPE(registration);
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
}

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