/* * DWN - Desktop Window Manager * retoor * Specialized Worker Threads - Implementation */ #include "thread_workers.h" #include "util.h" #include #include #include #include #include #include #include #include #include /* ============================================================================ * I/O Worker Implementation * ============================================================================ */ typedef struct { char path[512]; IoCallback callback; void *user_data; void *data; size_t size; int error; } IoOperation; static AsyncContext *g_io_context = NULL; static size_t dwn_curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp) { size_t total = size * nmemb; IoOperation *op = userp; void *new_data = realloc(op->data, op->size + total + 1); if (!new_data) return 0; op->data = new_data; memcpy((char*)op->data + op->size, contents, total); op->size += total; ((char*)op->data)[op->size] = '\0'; return total; } static void io_read_file_worker(void *user_data, atomic_int *cancelled) { (void)cancelled; IoOperation *op = user_data; int fd = open(op->path, O_RDONLY); if (fd < 0) { op->error = errno; return; } struct stat st; if (fstat(fd, &st) < 0) { op->error = errno; close(fd); return; } op->size = st.st_size; op->data = malloc(op->size + 1); if (!op->data) { op->error = ENOMEM; close(fd); return; } ssize_t n = read(fd, op->data, op->size); close(fd); if (n < 0) { op->error = errno; free(op->data); op->data = NULL; return; } ((char*)op->data)[n] = '\0'; } static void io_write_file_worker(void *user_data, atomic_int *cancelled) { (void)cancelled; IoOperation *op = user_data; int fd = open(op->path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { op->error = errno; return; } ssize_t n = write(fd, op->data, op->size); close(fd); if (n < 0 || (size_t)n != op->size) { op->error = errno; return; } op->error = 0; } static void io_read_dir_worker(void *user_data, atomic_int *cancelled) { (void)cancelled; IoOperation *op = user_data; DIR *dir = opendir(op->path); if (!dir) { op->error = errno; return; } size_t total_size = 0; struct dirent *entry; while ((entry = readdir(dir)) != NULL) { total_size += strlen(entry->d_name) + 1; } rewinddir(dir); op->data = malloc(total_size + 1); if (!op->data) { op->error = ENOMEM; closedir(dir); return; } char *p = op->data; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } size_t name_len = strlen(entry->d_name); size_t remaining = total_size - (size_t)(p - (char*)op->data); if (name_len + 1 > remaining) break; memcpy(p, entry->d_name, name_len); p[name_len] = '\0'; p += name_len + 1; } *p = '\0'; op->size = p - (char*)op->data; closedir(dir); } static void io_http_get_worker(void *user_data, atomic_int *cancelled) { (void)cancelled; IoOperation *op = user_data; CURL *curl = curl_easy_init(); if (!curl) { op->error = -1; return; } curl_easy_setopt(curl, CURLOPT_URL, op->path); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dwn_curl_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, op); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); /* Check for cancellation periodically */ /* Note: curl doesn't easily support cancellation mid-transfer */ CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { op->error = res; if (op->data) { free(op->data); op->data = NULL; op->size = 0; } } curl_easy_cleanup(curl); } static void io_operation_cleanup(Task *task, void *user_data, ThreadStatus status) { (void)task; (void)status; IoOperation *op = user_data; if (op->callback) { op->callback(op->data, op->size, op->error, op->user_data); } if (op->data && op->error != 0) { free(op->data); } free(op); } bool io_worker_init(void) { if (g_io_context) return true; g_io_context = async_context_create("io"); return g_io_context != NULL; } void io_worker_shutdown(void) { if (g_io_context) { async_context_destroy(g_io_context); g_io_context = NULL; } } TaskHandle io_read_file_async(const char *path, IoCallback callback, void *user_data) { if (!g_io_context || !path) return NULL; IoOperation *op = calloc(1, sizeof(IoOperation)); if (!op) return NULL; strncpy(op->path, path, sizeof(op->path) - 1); op->callback = callback; op->user_data = user_data; TaskHandle task = task_create_with_callback( io_read_file_worker, op, TASK_PRIORITY_NORMAL, io_operation_cleanup, op ); if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) { free(op); return NULL; } return task; } TaskHandle io_write_file_async(const char *path, const void *data, size_t size, IoCallback callback, void *user_data) { if (!g_io_context || !path || !data) return NULL; IoOperation *op = calloc(1, sizeof(IoOperation)); if (!op) return NULL; strncpy(op->path, path, sizeof(op->path) - 1); op->data = malloc(size); if (!op->data) { free(op); return NULL; } memcpy(op->data, data, size); op->size = size; op->callback = callback; op->user_data = user_data; TaskHandle task = task_create_with_callback( io_write_file_worker, op, TASK_PRIORITY_NORMAL, io_operation_cleanup, op ); if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) { free(op->data); free(op); return NULL; } return task; } TaskHandle io_read_dir_async(const char *path, IoCallback callback, void *user_data) { if (!g_io_context || !path) return NULL; IoOperation *op = calloc(1, sizeof(IoOperation)); if (!op) return NULL; strncpy(op->path, path, sizeof(op->path) - 1); op->callback = callback; op->user_data = user_data; TaskHandle task = task_create_with_callback( io_read_dir_worker, op, TASK_PRIORITY_NORMAL, io_operation_cleanup, op ); if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) { free(op); return NULL; } return task; } TaskHandle io_http_get_async(const char *url, IoCallback callback, void *user_data) { if (!g_io_context || !url) return NULL; IoOperation *op = calloc(1, sizeof(IoOperation)); if (!op) return NULL; strncpy(op->path, url, sizeof(op->path) - 1); op->callback = callback; op->user_data = user_data; TaskHandle task = task_create_with_callback( io_http_get_worker, op, TASK_PRIORITY_NORMAL, io_operation_cleanup, op ); if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) { free(op); return NULL; } return task; } /* ============================================================================ * System Worker Implementation * ============================================================================ */ static AsyncContext *g_system_context = NULL; static SystemStats g_cached_stats; static pthread_mutex_t g_stats_mutex = PTHREAD_MUTEX_INITIALIZER; static uint32_t g_stats_timer_id = 0; static void parse_meminfo(uint64_t *total, uint64_t *available) { *total = 0; *available = 0; FILE *f = fopen("/proc/meminfo", "r"); if (!f) return; char line[256]; while (fgets(line, sizeof(line), f)) { if (strncmp(line, "MemTotal:", 9) == 0) { sscanf(line, "MemTotal: %lu", total); *total *= 1024; } else if (strncmp(line, "MemAvailable:", 13) == 0) { sscanf(line, "MemAvailable: %lu", available); *available *= 1024; break; } else if (strncmp(line, "MemFree:", 8) == 0 && *available == 0) { /* Fallback if MemAvailable not present */ sscanf(line, "MemFree: %lu", available); *available *= 1024; } } fclose(f); } static void parse_loadavg(float load[3]) { FILE *f = fopen("/proc/loadavg", "r"); if (!f) return; if (fscanf(f, "%f %f %f", &load[0], &load[1], &load[2]) != 3) { load[0] = load[1] = load[2] = 0.0f; } fclose(f); } static void parse_stat(float *cpu_percent) { static uint64_t prev_idle = 0, prev_total = 0; FILE *f = fopen("/proc/stat", "r"); if (!f) return; char line[256]; if (fgets(line, sizeof(line), f)) { unsigned long user, nice, system, idle, iowait, irq, softirq, steal; sscanf(line, "cpu %lu %lu %lu %lu %lu %lu %lu %lu", &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal); uint64_t idle_time = idle + iowait; uint64_t total_time = user + nice + system + idle + iowait + irq + softirq + steal; uint64_t diff_idle = idle_time - prev_idle; uint64_t diff_total = total_time - prev_total; if (diff_total > 0) { *cpu_percent = 100.0f * (1.0f - ((float)diff_idle / diff_total)); } prev_idle = idle_time; prev_total = total_time; } fclose(f); } static void system_stats_worker(void *user_data, atomic_int *cancelled) { (void)user_data; (void)cancelled; SystemStats stats; memset(&stats, 0, sizeof(stats)); parse_stat(&stats.cpu_percent); uint64_t mem_available = 0; parse_meminfo(&stats.memory_total, &mem_available); stats.memory_used = stats.memory_total - mem_available; if (stats.memory_total > 0) { stats.memory_percent = 100.0f * (float)stats.memory_used / stats.memory_total; } parse_loadavg(stats.load_avg); FILE *f = fopen("/proc/uptime", "r"); if (f) { double uptime = 0.0; if (fscanf(f, "%lf", &uptime) == 1) { stats.uptime_seconds = (uint64_t)uptime; } fclose(f); } pthread_mutex_lock(&g_stats_mutex); g_cached_stats = stats; pthread_mutex_unlock(&g_stats_mutex); } static void stats_timer_callback(TimerId id, void *user_data) { (void)id; (void)user_data; ASYNC(system_stats_worker, NULL); } bool system_worker_init(void) { if (g_system_context) return true; g_system_context = async_context_create("system"); if (!g_system_context) return false; /* Initial stats read */ system_stats_worker(NULL, NULL); /* Start periodic updates */ g_stats_timer_id = timer_schedule_repeating(1000, stats_timer_callback, NULL); return true; } void system_worker_shutdown(void) { if (g_stats_timer_id) { timer_cancel(g_stats_timer_id); g_stats_timer_id = 0; } if (g_system_context) { async_context_destroy(g_system_context); g_system_context = NULL; } } bool system_worker_get_cached_stats(SystemStats *stats) { if (!stats) return false; pthread_mutex_lock(&g_stats_mutex); *stats = g_cached_stats; pthread_mutex_unlock(&g_stats_mutex); return true; } /* ============================================================================ * Deferred Worker Implementation * ============================================================================ */ static bool g_deferred_initialized = false; bool deferred_worker_init(void) { g_deferred_initialized = true; return true; } void deferred_worker_shutdown(void) { g_deferred_initialized = false; } DeferredId deferred_after(uint64_t delay_ms, DeferredCallback callback, void *user_data) { if (!g_deferred_initialized) return 0; return timer_schedule(delay_ms, (TimerCallback)callback, user_data); } DeferredId deferred_every(uint64_t interval_ms, DeferredCallback callback, void *user_data) { if (!g_deferred_initialized) return 0; return timer_schedule_repeating(interval_ms, (TimerCallback)callback, user_data); } bool deferred_cancel(DeferredId id) { return timer_cancel(id); } uint32_t deferred_process(void) { return timer_process(); } int deferred_get_poll_fd(void) { return timer_get_poll_fd(); } /* ============================================================================ * Integration * ============================================================================ */ bool thread_workers_init(void) { if (!io_worker_init()) return false; if (!system_worker_init()) { io_worker_shutdown(); return false; } if (!deferred_worker_init()) { system_worker_shutdown(); io_worker_shutdown(); return false; } return true; } void thread_workers_shutdown(void) { deferred_worker_shutdown(); system_worker_shutdown(); io_worker_shutdown(); } bool thread_workers_process_all(void) { bool work_done = false; /* Process timers */ if (deferred_process() > 0) work_done = true; /* Process event bus */ if (event_bus_process(event_bus_default()) > 0) work_done = true; return work_done; } int thread_workers_get_poll_fds(int *fds) { int count = 0; int fd = deferred_get_poll_fd(); if (fd >= 0) fds[count++] = fd; /* Note: event bus fd would be added here if exposed */ return count; }