libksr/src/promises.c

169 lines
4.8 KiB
C

#include "ksr/arrays.h"
#include <ksr/promises.h>
#include <signal.h>
#include <unistd.h>
// global variable indicating if the program has been interrupted or not.
bool interrupted = false;
/**
* Function launched on signal reception.
* @param signal - the received signal.
*/
void onsigint(/*int signal*/)
{
//if (signal == SIGINT)
// the program is now interrupted.
interrupted = true;
}
/**
* Promise thread structure.
*/
typedef struct {
ksrpromise *promise;
ksrpromise_execution_f exec;
void *data;
} promise_thread_data;
/**
* Promise then user callback.
*/
typedef struct {
ksrpromise_then_callback_f callback;
void *userdata;
} promise_then_user_callback;
/**
* Promise catch user callback.
*/
typedef struct {
ksrpromise_catch_callback_f callback;
void *userdata;
} promise_catch_user_callback;
/**
* The main promise thread function.
* @param data - thread data.
*/
void* promise_thread(void *data)
{
// reading thread data.
promise_thread_data *thread_data = data;
// executing the promise.
thread_data->exec(thread_data->promise, thread_data->data);
return NULL; // this thread has no result.
}
ksrpromise* ksrpromise_new(ksrpromise_execution_f exec, void *data)
{
// allocate the promise.
ksrpromise *promise = malloc(sizeof(ksrpromise));
promise->then_callbacks = ksrarray_new_tiny();
promise->catch_callbacks = ksrarray_new_tiny();
promise->successful = false;
promise->result = NULL;
promise->error_code = 0;
promise->error_message = NULL;
// allocate thread data.
promise_thread_data *thread_data = malloc(sizeof(promise_thread_data));
// assign thread data.
thread_data->promise = promise;
thread_data->exec = exec;
thread_data->data = data;
// initialize and run a new thread.
pthread_create(&promise->thread, NULL, promise_thread, thread_data);
return promise; // returning the created promise.
}
/**
* Run then callbacks for the given promise.
* @param promise - the promise for which to run then callbacks.
*/
void ksrpromise_run_then_callbacks(ksrpromise *promise)
{
promise_then_user_callback *user_callback; // initialize user callback pointer.
for (size_t i = 0; i < promise->then_callbacks->length; i++)
{ // for each then callback, execute it.
user_callback = promise->then_callbacks->data[i]; // getting callback.
(user_callback->callback)(promise->result, user_callback->userdata); // executing callback with promised result.
}
}
void ksrpromise_resolve(ksrpromise *promise, void *result)
{
// save success and result.
promise->successful = true;
promise->result = result;
// running then callbacks.
ksrpromise_run_then_callbacks(promise);
}
/**
* Run reject callbacks for the given promise.
* @param promise - the promise for which to run reject callbacks.
*/
void ksrpromise_run_catch_callbacks(ksrpromise *promise)
{
promise_catch_user_callback *user_callback; // initialize user callback pointer.
for (size_t i = 0; i < promise->catch_callbacks->length; i++)
{ // for each catch callback, execute it.
user_callback = promise->catch_callbacks->data[i]; // getting callback.
(user_callback->callback)(promise->error_code, promise->error_message, user_callback->userdata); // executing callback with the promise error.
}
}
void ksrpromise_reject(ksrpromise *promise, int code, const char *message)
{
// save error details.
promise->error_code = code;
promise->error_message = message;
// running catch callbacks.
ksrpromise_run_catch_callbacks(promise);
}
void* ksrpromise_await(ksrpromise *promise)
{
// register signal reception callback.
signal(SIGINT, onsigint);
while (!interrupted)
{ // infinite loop, waiting for promise end or interruption.
if (!ksrpromise_is_running(promise))
// promise is not running anymore, returning result if there is one.
return promise->result;
usleep(KSRPROMISES_AWAIT_SLEEP_DELAY); // sleeping a bit...
}
return NULL; // the promise have been interrupted before the result was found.
}
void ksrpromise_then(ksrpromise *promise, ksrpromise_then_callback_f callback, void *userdata)
{
// allocate user callback.
promise_then_user_callback *user_callback = malloc(sizeof(promise_then_user_callback));
user_callback->callback = callback;
user_callback->userdata = userdata;
// we use a pointer of the given function pointer as a workaround for generic arrays (see https://stackoverflow.com/a/16682718/7496951).
ksrarray_push(promise->then_callbacks, user_callback);
}
void ksrpromise_catch(ksrpromise *promise, ksrpromise_catch_callback_f callback, void *userdata)
{
// allocate user callback.
promise_catch_user_callback *user_callback = malloc(sizeof(promise_catch_user_callback));
user_callback->callback = callback;
user_callback->userdata = userdata;
// we use a pointer of the given function pointer as a workaround for generic arrays (see https://stackoverflow.com/a/16682718/7496951).
ksrarray_push(promise->catch_callbacks, user_callback);
}