diff --git a/carrow.c b/carrow.c index 736cc3e..ef459eb 100644 --- a/carrow.c +++ b/carrow.c @@ -34,6 +34,9 @@ static struct sigaction old_action; static unsigned int _openmax; static struct evbag **_evbags; static volatile unsigned int _evbagscount; +static unsigned int _asapsmax; +static struct asapbag **_asapbags; +static volatile unsigned int _asapbagscount; static int _epollfd = -1; @@ -44,6 +47,12 @@ struct evbag { }; +struct asapbag { + carrow_asapfunc func; + void *params; +}; + + #define EVBAG_HAS(fd) (_evbags[fd] != NULL) #define EVBAG_ISNULL(fd) (_evbags[fd] == NULL) @@ -169,8 +178,73 @@ evbags_deinit() { } +static int +asapbags_init() { + _asapbagscount = 0; + _asapbags = calloc(_asapsmax, sizeof(struct asapbag*)); + if (_asapbags == NULL) { + errno = ENOMEM; + return -1; + } + + memset(_asapbags, 0, sizeof(struct asapbag*) * _asapsmax); + return 0; +} + + +static void +asapbags_deinit() { + for (int i = 0; i < _asapsmax; i++) { + if (_asapbags[i] != NULL) { + free(_asapbags[i]); + _asapbags[i] = NULL; + } + } + + free(_asapbags); + _asapbags = NULL; +} + + +static void +asap_trigger() { + for(int i = 0; i < _asapbagscount; i++) { + struct asapbag *bag = _asapbags[i]; + bag->func(bag->params); + } + _asapbagscount = 0; +} + + int -carrow_init(unsigned int openmax) { +carrow_asap_register(carrow_asapfunc func, void *params) { + if (_asapsmax == 0) { + return -1; + } + + if (_asapbagscount >= _asapsmax) { + return -1; + } + struct asapbag *bag = _asapbags[_asapbagscount]; + + if (bag == NULL) { + bag = malloc(sizeof(struct asapbag)); + if (bag == NULL) { + errno = ENOMEM; + return -1; + } + _asapbags[_asapbagscount] = bag; + } + + bag->func = func; + bag->params = params; + _asapbagscount++; + return 0; +} + + +int +carrow_init(unsigned int openmax, unsigned int asapsmax) { if (_epollfd != -1) { return -1; } @@ -191,6 +265,14 @@ carrow_init(unsigned int openmax) { return -1; } + _asapsmax = asapsmax; + if (_asapsmax > 0) { + if (asapbags_init()) { + evbags_deinit(); + return -1; + } + } + _epollfd = epoll_create1(0); if (_epollfd < 0) { return -1; @@ -314,6 +396,10 @@ carrow_evloop(volatile int *status) { evloop: while (_evbagscount && ((status == NULL) || (*status > EXIT_FAILURE))) { + if (_asapbagscount > 0) { + asap_trigger(); + } + errno = 0; nfds = epoll_wait(_epollfd, events, _openmax, -1); if (nfds < 0) { @@ -351,5 +437,10 @@ carrow_deinit() { close(_epollfd); _epollfd = -1; evbags_deinit(); + + if (_asapsmax > 0) { + asapbags_deinit(); + } + errno = 0; } diff --git a/carrow.h b/carrow.h index 156b6b6..2f5c51c 100644 --- a/carrow.h +++ b/carrow.h @@ -124,10 +124,14 @@ struct carrow_generic_coro { typedef void (*carrow_generic_corofunc) (void *coro, void *state); +/* A function pointer type representing a carrow asap function */ +typedef void (*carrow_asapfunc) (void *params); + + /* A function that initializes the event loop with the given maximum number of file descriptors. */ int -carrow_init(unsigned int openmax); +carrow_init(unsigned int openmax, unsigned int asapsmax); /* A function that frees the memory allocated for the _evbags array. */ @@ -165,6 +169,10 @@ int carrow_evloop_unregister(int fd); +int +carrow_asap_register(carrow_asapfunc func, void *params); + + /* A function that runs the event loop until the _evbagscount is zero or the status is set to a value less than or equal to EXIT_FAILURE. It waits for events on the epoll instance and triggers thecorresponding diff --git a/carrow_generic.c b/carrow_generic.c index 20677ad..28a9347 100644 --- a/carrow_generic.c +++ b/carrow_generic.c @@ -100,10 +100,10 @@ CARROW_NAME(evloop_unregister) (int fd) { int CARROW_NAME(forever) (CARROW_NAME(corofunc) f, CARROW_ENTITY *state, - volatile int *status) { + volatile int *status, unsigned int asapsmax) { int ret = EXIT_SUCCESS; - carrow_init(0); + carrow_init(0, asapsmax); CARROW_NAME(coro_create_and_run) (f, state); if (carrow_evloop(status)) { ret = EXIT_FAILURE; diff --git a/carrow_generic.h b/carrow_generic.h index aa88c10..906a1fa 100644 --- a/carrow_generic.h +++ b/carrow_generic.h @@ -79,7 +79,7 @@ CARROW_NAME(evloop_unregister) (int fd); int CARROW_NAME(forever) (CARROW_NAME(corofunc) resolve, CARROW_ENTITY *state, - volatile int *status); + volatile int *status, unsigned int asapsmax); #endif diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f83dfca..1d6432f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -48,6 +48,20 @@ add_custom_target(profile_timer ) +add_executable(timerasap + $ + timerasap.c +) +target_include_directories(timerasap PUBLIC "${CMAKE_SOURCE_DIR}") +target_link_libraries(timerasap PUBLIC clog mrb) +add_custom_target(run_timerasap $) +add_custom_target(profile_timerasap + COMMAND "valgrind" + ${VALGRIND_FLAGS} + $ +) + + add_executable(sleep $ sleep.c diff --git a/examples/sleep.c b/examples/sleep.c index 6f915df..f1bdd2b 100644 --- a/examples/sleep.c +++ b/examples/sleep.c @@ -65,5 +65,5 @@ main() { .counter = 0 }; - return foo_forever(fooA, &state, NULL); + return foo_forever(fooA, &state, NULL, 0); } diff --git a/examples/tcpclient.c b/examples/tcpclient.c index e83ab1f..ba040e2 100644 --- a/examples/tcpclient.c +++ b/examples/tcpclient.c @@ -241,7 +241,7 @@ main() { return EXIT_FAILURE; } - ret = tcpconn_forever(connectA, &conn, NULL); + ret = tcpconn_forever(connectA, &conn, NULL, 0); if (mrb_destroy(conn.inbuff) || mrb_destroy(conn.outbuff)) { ERROR("Cannot dispose buffers."); ret = EXIT_FAILURE; diff --git a/examples/tcpserver.c b/examples/tcpserver.c index b66f351..9f00db2 100644 --- a/examples/tcpserver.c +++ b/examples/tcpserver.c @@ -205,5 +205,5 @@ main() { .backlog = 2, }; - return tcpserver_forever(listenA, &state, NULL); + return tcpserver_forever(listenA, &state, NULL, 0); } diff --git a/examples/timer.c b/examples/timer.c index 452ec66..69f95bd 100644 --- a/examples/timer.c +++ b/examples/timer.c @@ -100,7 +100,7 @@ main() { .value = 0, }; - carrow_init(0); + carrow_init(0, 0); timer_coro_create_and_run(timerA, &state1); timer_coro_create_and_run(timerA, &state2); carrow_evloop(NULL); diff --git a/examples/timerasap.c b/examples/timerasap.c new file mode 100644 index 0000000..2ab670e --- /dev/null +++ b/examples/timerasap.c @@ -0,0 +1,126 @@ +// Copyright 2023 Vahid Mardani +/* + * This file is part of Carrow. + * Carrow is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Carrow is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with Carrow. If not, see . + * + * Author: Vahid Mardani + */ +#include +#include +#include + +#include + +#include "carrow.h" + + +typedef struct timer { + unsigned int interval; + unsigned long value; + const char *title; +} timer; + + +#undef CARROW_ENTITY +#define CARROW_ENTITY timer +#include "carrow_generic.h" +#include "carrow_generic.c" + + +static int +maketimer(unsigned int interval) { + int fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); + if (fd == -1) { + return -1; + } + + struct timespec sec1 = {interval, 0}; + struct itimerspec spec = {sec1, sec1}; + if (timerfd_settime(fd, 0, &spec, NULL) == -1) { + return -1; + } + return fd; +} + + +static void +timer_asapfunc(int value) { + INFO("called from asapfunction value: %d", value); +} + + +static void +timerA(struct timer_coro *self, struct timer *state) { + CORO_START; + unsigned long tmp; + ssize_t bytes; + + self->fd = maketimer(state->interval); + if (self->fd == -1) { + CORO_REJECT("maketimer"); + } + + while (true) { + CORO_WAIT(self->fd, CIN); + bytes = read(self->fd, &tmp, sizeof(tmp)); + state->value += tmp; + INFO("%s, fd: %d, value: %lu", state->title, self->fd, state->value); + + if (state->value % 5 == 0) { + INFO("registering to asap"); + carrow_asap_register((carrow_asapfunc)timer_asapfunc, + (void *) state->value); + } + } + + CORO_FINALLY; + if (self->fd != -1) { + timer_evloop_unregister(self->fd); + close(self->fd); + } + CORO_END; +} + + +int +main() { + clog_verbosity = CLOG_DEBUG; + + if (carrow_handleinterrupts()) { + return EXIT_FAILURE; + } + + struct timer state1 = { + .title = "Foo", + .interval = 1, + .value = 0, + }; + struct timer state2 = { + .title = "Bar", + .interval = 3, + .value = 0, + }; + if (carrow_init(0, 10)) { + ERROR("unable to init carrow"); + return EXIT_FAILURE; + } + + INFO("CARROW INIT"); + timer_coro_create_and_run(timerA, &state1); + //timer_coro_create_and_run(timerA, &state2); + carrow_evloop(NULL); + carrow_deinit(); + + return EXIT_SUCCESS; +}