I am beginner to intermediate and wanted to write a little event code in c. It's one header file with 108 line pure code and it does what I was thinking it should be like for me.
EventListener.h
#ifndef EVENTLISTENER2_H
#define EVENTLISTENER2_H
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
struct Event;
struct EventListener {
pthread_t tid;
struct Event *event;
void *(*callback)(void*);
void *arg;
int trigger;
struct EventListener *next;
};
typedef struct EventListener EventListener;
struct Event{
pthread_mutex_t mtx;
EventListener *listeners;
};
typedef struct Event Event;
void malloc_exit_on_failure(void *p)
{
if(p == NULL)
{
perror("memory allocation failed");
exit(EXIT_FAILURE);
}
}
EventListener *EventListener_Create(void *(*callback)(void*), void *arg)
{
EventListener *listener = (EventListener *) malloc(sizeof(*listener));
malloc_exit_on_failure(listener);
listener->callback = callback;
listener->arg = arg;
listener->tid = 0;
listener->event = NULL;
listener->next = NULL;
listener->trigger = 0;
return listener;
}
void EventListenerWait(EventListener *listener)
{
while(listener->trigger == 0);
}
void EventListenerRun(EventListener *listener)
{
listener->callback(listener->arg);
listener->trigger = 0;
}
void *EventListenerThread(void *listener_arg)
{
EventListener *listener = (EventListener*) listener_arg;
while(1)
{
EventListenerWait(listener);
EventListenerRun(listener);
}
}
Event *Event_Create()
{
Event *event = (Event *) malloc(sizeof(Event));
malloc_exit_on_failure(event);
event->listeners = NULL;
pthread_mutex_init(&event->mtx, NULL);
return event;
}
void Event_RegisterRaw(Event *event, void *(*callback)(void *), void *arg);
void Event_Register(Event *event, EventListener *listener)
{
if(event->listeners == NULL)
event->listeners = listener;
else
{
struct EventListener *current = event->listeners;
while(current->next != NULL)
current = current->next;
current->next = listener;
}
listener->event = event;
if(pthread_create(&listener->tid, NULL, EventListenerThread, listener) != 0)
{
perror("cannot create thread");
exit(EXIT_FAILURE);
}
}
void Event_Trigger(Event *event){
EventListener *current = event->listeners;
while(current != NULL){
pthread_mutex_lock(&event->mtx);
current->trigger = 1;
current = current->next;
pthread_mutex_unlock(&event->mtx);
}
}
void Event_memory_usage(Event *event)
{
size_t size;
int n_listeners = 0;
size = sizeof(*event);
EventListener *current = event->listeners;
while(current != NULL)
{
size = size + sizeof(*current);
current = current->next;
n_listeners++;
}
printf("Event size with %d listeners: %lu bytes.\n", n_listeners, size);
}
#endif
event_test.c
#include <stdio.h>
#include <unistd.h>
#include "EventListener.h"
void *event_trigger_test(void *arg)
{
printf("%s\n", (char *) arg);
}
char *arg = "Event triggered!";
char *arg2 = "Event 2 triggered!";
void *event_trigger_loop_test(void *arg)
{
printf("invoke\n");
while(1)
{
sleep(2);
printf("i am running\n");
}
}
int main(void)
{
EventListener *l = EventListener_Create(event_trigger_test, arg);
EventListener *l2 = EventListener_Create(event_trigger_test, arg2);
EventListener *l3 = EventListener_Create(event_trigger_loop_test, NULL);
Event *TEST_EVENT = Event_Create();
Event_Register(TEST_EVENT, l);
Event_Register(TEST_EVENT, l2);
Event_Register(TEST_EVENT, l3);
printf("EventListener Size: %lu\n", sizeof(EventListener));
printf("Event Size: %lu\n", sizeof(Event));
Event_memory_usage(TEST_EVENT);
for(int i = 0; i < 5; i++) {
sleep(i);
Event_Trigger(TEST_EVENT);
printf("\n"); // debug
}
sleep(10);
}
What should happen to listeners from an event that are running in an infinite loop after the event is triggered?
Is it okay/normal/best practice to create one thread per listener?
I just want to hear what you think about it and I ask for constructive criticism like improvements, hint and so on what I missed, what I should do better and if there is a better approach.
For future questions of mine I just wanted to ask if it is okay to ask for review of a header file like this tiny one or where I should be looking for that?
%lu
might not be the correct format specifier forsize_t
(returned bysizeof
). Use%zu
instead. See also: stackoverflow.com/q/2125845/20017547 \$\endgroup\$void*
, notvoid
? Should these returnNULL
? Are there other callbacks that are supposed to return a pointer to somestatic
data structure? \$\endgroup\$