/* * DWN - Desktop Window Manager * retoor * Future/Promise Implementation - Async Result Handling */ #include "threading.h" #include "util.h" #include #include #include /* ============================================================================ * Future Implementation * ============================================================================ */ typedef struct FutureCallbackNode { FutureCallback callback; void *user_data; struct FutureCallbackNode *next; } FutureCallbackNode; struct Future { pthread_mutex_t mutex; pthread_cond_t ready; FutureResult result; int error_code; _Atomic int ready_flag; _Atomic int has_result; FutureCallbackNode *callbacks; _Atomic int callback_count; }; Future* future_create(void) { Future *f = dwn_malloc(sizeof(Future)); if (!f) return NULL; atomic_store_explicit(&f->ready_flag, 0, ATOMIC_RELEASE); atomic_store_explicit(&f->has_result, 0, ATOMIC_RELEASE); atomic_store_explicit(&f->callback_count, 0, ATOMIC_RELAXED); f->result = NULL; f->error_code = 0; f->callbacks = NULL; if (pthread_mutex_init(&f->mutex, NULL) != 0) { dwn_free(f); return NULL; } if (pthread_cond_init(&f->ready, NULL) != 0) { pthread_mutex_destroy(&f->mutex); dwn_free(f); return NULL; } return f; } void future_destroy(Future *f) { if (!f) return; /* Free callback chain */ FutureCallbackNode *node = f->callbacks; while (node) { FutureCallbackNode *next = node->next; dwn_free(node); node = next; } pthread_cond_destroy(&f->ready); pthread_mutex_destroy(&f->mutex); dwn_free(f); } void future_set_result(Future *f, FutureResult result) { if (!f) return; pthread_mutex_lock(&f->mutex); if (atomic_load_explicit(&f->has_result, ATOMIC_ACQUIRE)) { pthread_mutex_unlock(&f->mutex); return; /* Already set */ } f->result = result; f->error_code = 0; atomic_store_explicit(&f->has_result, 1, ATOMIC_RELEASE); atomic_store_explicit(&f->ready_flag, 1, ATOMIC_RELEASE); /* Execute callbacks */ FutureCallbackNode *node = f->callbacks; while (node) { node->callback(f, result, node->user_data); node = node->next; } pthread_cond_broadcast(&f->ready); pthread_mutex_unlock(&f->mutex); } void future_set_error(Future *f, int error_code) { if (!f) return; pthread_mutex_lock(&f->mutex); if (atomic_load_explicit(&f->has_result, ATOMIC_ACQUIRE)) { pthread_mutex_unlock(&f->mutex); return; } f->result = NULL; f->error_code = error_code; atomic_store_explicit(&f->has_result, 1, ATOMIC_RELEASE); atomic_store_explicit(&f->ready_flag, 1, ATOMIC_RELEASE); /* Execute callbacks with NULL result */ FutureCallbackNode *node = f->callbacks; while (node) { node->callback(f, NULL, node->user_data); node = node->next; } pthread_cond_broadcast(&f->ready); pthread_mutex_unlock(&f->mutex); } FutureResult future_get(Future *f, ThreadStatus *status) { return future_get_timeout(f, 0, status); } FutureResult future_get_timeout(Future *f, uint64_t timeout_ms, ThreadStatus *status) { if (!f) { if (status) *status = THREAD_ERROR_INVALID; return NULL; } pthread_mutex_lock(&f->mutex); if (!atomic_load_explicit(&f->ready_flag, ATOMIC_ACQUIRE)) { if (timeout_ms == 0) { pthread_cond_wait(&f->ready, &f->mutex); } else { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout_ms / 1000; ts.tv_nsec += (timeout_ms % 1000) * 1000000; if (ts.tv_nsec >= 1000000000) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } int ret = pthread_cond_timedwait(&f->ready, &f->mutex, &ts); if (ret == ETIMEDOUT) { pthread_mutex_unlock(&f->mutex); if (status) *status = THREAD_ERROR_TIMEOUT; return NULL; } } } FutureResult result = f->result; if (status) { *status = (f->error_code != 0) ? THREAD_ERROR : THREAD_OK; } pthread_mutex_unlock(&f->mutex); return result; } bool future_is_ready(Future *f) { if (!f) return false; return atomic_load_explicit(&f->ready_flag, ATOMIC_ACQUIRE) != 0; } void future_then(Future *f, FutureCallback callback, void *user_data) { if (!f || !callback) return; pthread_mutex_lock(&f->mutex); /* If already ready, execute immediately */ if (atomic_load_explicit(&f->ready_flag, ATOMIC_ACQUIRE)) { pthread_mutex_unlock(&f->mutex); callback(f, f->result, user_data); return; } /* Add to callback chain */ FutureCallbackNode *node = dwn_malloc(sizeof(FutureCallbackNode)); if (!node) { pthread_mutex_unlock(&f->mutex); return; } node->callback = callback; node->user_data = user_data; node->next = f->callbacks; f->callbacks = node; atomic_fetch_add_explicit(&f->callback_count, 1, ATOMIC_RELAXED); pthread_mutex_unlock(&f->mutex); } /* ============================================================================ * Composite Futures * ============================================================================ */ typedef struct { Future *composite; _Atomic int remaining; uint32_t total; pthread_mutex_t mutex; } CompositeFutureData; static void all_callback(Future *f, FutureResult result, void *user_data) { (void)f; (void)result; CompositeFutureData *data = user_data; int rem = atomic_fetch_sub_explicit(&data->remaining, 1, ATOMIC_ACQ_REL) - 1; if (rem == 0) { future_set_result(data->composite, (void*)(uintptr_t)data->total); } } Future* future_all(Future **futures, uint32_t count) { if (!futures || count == 0) return NULL; Future *composite = future_create(); if (!composite) return NULL; if (count == 1) { /* Optimize single future */ FutureResult r = future_get(futures[0], NULL); future_set_result(composite, r); return composite; } CompositeFutureData *data = dwn_malloc(sizeof(CompositeFutureData)); if (!data) { future_destroy(composite); return NULL; } data->composite = composite; atomic_store_explicit(&data->remaining, count, ATOMIC_RELEASE); data->total = count; pthread_mutex_init(&data->mutex, NULL); /* Attach callback to each future */ for (uint32_t i = 0; i < count; i++) { if (future_is_ready(futures[i])) { /* Already done */ if (atomic_fetch_sub_explicit(&data->remaining, 1, ATOMIC_ACQ_REL) - 1 == 0) { future_set_result(composite, (void*)(uintptr_t)count); pthread_mutex_destroy(&data->mutex); dwn_free(data); return composite; } } else { future_then(futures[i], all_callback, data); } } return composite; } static void any_callback(Future *f, FutureResult result, void *user_data) { (void)f; CompositeFutureData *data = user_data; pthread_mutex_lock(&data->mutex); if (!future_is_ready(data->composite)) { future_set_result(data->composite, result); } pthread_mutex_unlock(&data->mutex); } Future* future_any(Future **futures, uint32_t count) { if (!futures || count == 0) return NULL; Future *composite = future_create(); if (!composite) return NULL; if (count == 1) { FutureResult r = future_get(futures[0], NULL); future_set_result(composite, r); return composite; } CompositeFutureData *data = dwn_malloc(sizeof(CompositeFutureData)); if (!data) { future_destroy(composite); return NULL; } data->composite = composite; pthread_mutex_init(&data->mutex, NULL); /* Attach callback to each future */ for (uint32_t i = 0; i < count; i++) { if (future_is_ready(futures[i])) { future_set_result(composite, futures[i]->result); pthread_mutex_destroy(&data->mutex); dwn_free(data); return composite; } else { future_then(futures[i], any_callback, data); } } return composite; }