/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Specialized Worker Threads - Implementation
*/
#include "thread_workers.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <curl/curl.h>
#include <errno.h>
/* ============================================================================
* 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;
}