| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <malloc.h> |
| #include <string.h> |
| #include <limits.h> |
| #include <sys/time.h> |
| #include <time.h> |
| |
| #include "glib-ectomy.h" |
| #include "list.h" |
| |
| GIOError g_io_channel_read(GIOChannel *channel, gchar *buf, gsize count, gsize *bytes_read) |
| { |
| int fd = channel->fd; |
| gssize result; |
| |
| /* At least according to the Debian manpage for read */ |
| if (count > SSIZE_MAX) |
| count = SSIZE_MAX; |
| |
| retry: |
| result = read (fd, buf, count); |
| |
| if (result < 0) { |
| *bytes_read = 0; |
| |
| switch (errno) { |
| #ifdef EINTR |
| case EINTR: |
| goto retry; |
| #endif |
| #ifdef EAGAIN |
| case EAGAIN: |
| return G_IO_STATUS_AGAIN; |
| #endif |
| default: |
| return G_IO_STATUS_ERROR; |
| } |
| } |
| |
| *bytes_read = result; |
| |
| return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF; |
| } |
| |
| void g_io_channel_close(GIOChannel *channel) |
| { |
| if (!channel) |
| return; |
| |
| close(channel->fd); |
| |
| memset(channel, 0, sizeof(channel)); |
| free(channel); |
| } |
| |
| GIOChannel *g_io_channel_unix_new(int fd) |
| { |
| GIOChannel *channel; |
| |
| channel = malloc(sizeof(GIOChannel)); |
| if (!channel) |
| return NULL; |
| |
| channel->fd = fd; |
| |
| return channel; |
| } |
| |
| gint g_io_channel_unix_get_fd(GIOChannel *channel) |
| { |
| return channel->fd; |
| } |
| |
| struct watch { |
| guint id; |
| GIOChannel *channel; |
| gint priority; |
| GIOCondition condition; |
| short *revents; |
| GIOFunc func; |
| gpointer user_data; |
| GDestroyNotify destroy; |
| |
| struct watch *prev; |
| struct watch *next; |
| }; |
| |
| static struct watch watch_head = { .id = 0, .prev = 0, .next = 0, .revents = 0 }; |
| |
| static GMainContext *default_context = NULL; |
| |
| static void watch_remove(struct watch *w) |
| { |
| struct watch *p, *n; |
| |
| if (!w) |
| return; |
| |
| p = w->prev; |
| n = w->next; |
| |
| if (p) |
| p->next = n; |
| |
| if (n) |
| n->prev = p; |
| |
| free(w); |
| } |
| |
| void g_io_remove_watch(guint id) |
| { |
| struct watch *w, *n; |
| |
| for (w = watch_head.next; w; w = n) { |
| n = w->next; |
| if (w->id != id) |
| continue; |
| |
| watch_remove(w); |
| return; |
| } |
| } |
| |
| guint g_io_add_watch_full(GIOChannel *channel, gint priority, |
| GIOCondition condition, GIOFunc func, |
| gpointer user_data, GDestroyNotify notify) |
| { |
| struct watch *watch = malloc(sizeof(struct watch)); |
| |
| watch->id = ++watch_head.id; |
| watch->channel = channel; |
| watch->priority = priority; |
| watch->condition = condition; |
| watch->func = func; |
| watch->user_data = user_data; |
| watch->destroy = notify; |
| |
| watch->prev = &watch_head; |
| watch->next = watch_head.next; |
| if (watch_head.next) |
| watch_head.next->prev = watch; |
| |
| watch_head.next = watch; |
| |
| return watch->id; |
| } |
| |
| guint g_io_add_watch(GIOChannel *channel, GIOCondition condition, |
| GIOFunc func, gpointer user_data) |
| { |
| return g_io_add_watch_full(channel, 0, condition, |
| func, user_data, NULL); |
| } |
| |
| static void timeout_free(void *data, void *user_data) |
| { |
| struct timeout *t = data; |
| |
| if (t) |
| free (t); |
| } |
| |
| static GMainContext *g_main_context_default() |
| { |
| if (default_context) |
| return default_context; |
| |
| default_context = malloc(sizeof(GMainContext)); |
| if (!default_context) |
| return NULL; |
| |
| memset(default_context, 0, sizeof(GMainContext)); |
| |
| default_context->timeout = -1; |
| |
| return default_context; |
| } |
| |
| GMainLoop *g_main_loop_new(GMainContext *context, gboolean is_running) |
| { |
| GMainLoop *ml; |
| |
| ml = malloc(sizeof(GMainLoop)); |
| if (!ml) |
| return NULL; |
| |
| memset(ml, 0, sizeof(GMainLoop)); |
| |
| if (!context) |
| ml->context = g_main_context_default(); |
| else |
| ml->context = context; |
| |
| ml->bail = 0; |
| |
| return ml; |
| } |
| |
| static void timeout_handlers_prepare(GMainContext *context) |
| { |
| struct slist *l = context->ltimeout; |
| struct timeout *t; |
| struct timeval tv; |
| glong msec, timeout = LONG_MAX; |
| |
| gettimeofday(&tv, NULL); |
| |
| while (l) { |
| t = l->data; |
| l = l->next; |
| |
| /* calculate the remainning time */ |
| msec = (t->expiration.tv_sec - tv.tv_sec) * 1000 + |
| (t->expiration.tv_usec - tv.tv_usec) / 1000; |
| if (msec < 0) |
| msec = 0; |
| |
| timeout = MIN_TIMEOUT(timeout, msec); |
| } |
| |
| /* set to min value found or NO timeout */ |
| context->timeout = (timeout != LONG_MAX ? timeout: -1); |
| } |
| |
| static void timeout_handlers_check(GMainContext *context) |
| { |
| struct slist *l = context->ltimeout; |
| struct timeout *t; |
| struct timeval tv; |
| |
| gettimeofday(&tv, NULL); |
| |
| while (l) { |
| t = l->data; |
| l = l->next; |
| |
| if ((tv.tv_sec < t->expiration.tv_sec) || |
| (tv.tv_sec == t->expiration.tv_sec && |
| tv.tv_usec < t->expiration.tv_usec)) |
| continue; |
| |
| if (t->function(t->data)) { |
| /* if false/expired: remove it from the list */ |
| context->ltimeout = slist_remove(context->ltimeout, t); |
| free(t); |
| } else { |
| glong secs, msecs; |
| /* update the next expiration time */ |
| secs = t->interval / 1000; |
| msecs = t->interval - secs * 1000; |
| |
| t->expiration.tv_sec = tv.tv_sec + secs; |
| t->expiration.tv_usec = tv.tv_usec + msecs * 1000; |
| if (t->expiration.tv_usec >= 1000000) { |
| t->expiration.tv_usec -= 1000000; |
| t->expiration.tv_sec++; |
| } |
| } |
| } |
| } |
| |
| void g_main_loop_run(GMainLoop *loop) |
| { |
| int open_max = sysconf(_SC_OPEN_MAX); |
| struct pollfd *ufds; |
| |
| ufds = malloc(open_max * sizeof(struct pollfd)); |
| if (!ufds) |
| return; |
| |
| while (!loop->bail) { |
| int nfds, rc; |
| struct watch *n, *w; |
| |
| nfds = 0; |
| for (w = watch_head.next; w != NULL; w = w->next) { |
| ufds[nfds].fd = w->channel->fd; |
| ufds[nfds].events = w->condition; |
| ufds[nfds].revents = 0; |
| w->revents = &ufds[nfds].revents; |
| nfds++; |
| } |
| |
| /* calculate the next timeout */ |
| timeout_handlers_prepare(loop->context); |
| |
| rc = poll(ufds, nfds, loop->context->timeout); |
| if (rc < 0) |
| continue; |
| |
| w = watch_head.next; |
| while (w) { |
| if (!*w->revents || w->func(w->channel, *w->revents, w->user_data)) { |
| w = w->next; |
| continue; |
| } |
| |
| n = w->next; |
| |
| if (w->destroy) |
| w->destroy(w->user_data); |
| watch_remove(w); |
| |
| w = n; |
| } |
| |
| /* check expired timers */ |
| timeout_handlers_check(loop->context); |
| } |
| |
| free(ufds); |
| } |
| |
| void g_main_loop_quit(GMainLoop *loop) |
| { |
| struct watch *w; |
| |
| loop->bail = 1; |
| |
| for (w = watch_head.next; w; w = w->next) { |
| if (w->destroy) |
| w->destroy(w->user_data); |
| watch_head.next = w->next; |
| free(w); |
| } |
| } |
| |
| void g_main_loop_unref(GMainLoop *loop) |
| { |
| if (!loop->context) |
| return; |
| |
| slist_foreach(loop->context->ltimeout, timeout_free, NULL); |
| slist_free(loop->context->ltimeout); |
| free(loop->context); |
| } |
| |
| guint g_timeout_add(guint interval, GSourceFunc function, gpointer data) |
| { |
| struct timeval tv; |
| guint secs; |
| guint msecs; |
| struct timeout *t; |
| |
| if (!default_context || !function) |
| return 0; |
| |
| t = malloc(sizeof(*t)); |
| |
| if (!t) |
| return 0; |
| |
| t->interval = interval; |
| t->function = function; |
| t->data = data; |
| |
| gettimeofday(&tv, NULL); |
| |
| secs = interval /1000; |
| msecs = interval - secs * 1000; |
| |
| t->expiration.tv_sec = tv.tv_sec + secs; |
| t->expiration.tv_usec = tv.tv_usec + msecs * 1000; |
| |
| if (t->expiration.tv_usec >= 1000000) { |
| t->expiration.tv_usec -= 1000000; |
| t->expiration.tv_sec++; |
| } |
| |
| /* attach the timeout the default context */ |
| t->id = ++default_context->next_id; |
| default_context->ltimeout = slist_append(default_context->ltimeout, t); |
| |
| return t->id; |
| } |
| |
| gint g_timeout_remove(const guint id) |
| { |
| struct slist *l; |
| struct timeout *t; |
| |
| if (!default_context) |
| return -1; |
| |
| l = default_context->ltimeout; |
| |
| while (l) { |
| t = l->data; |
| l = l->next; |
| |
| if (t->id != id) |
| continue; |
| |
| default_context->ltimeout = slist_remove(default_context->ltimeout, t); |
| free(t); |
| |
| return 0; |
| } |
| |
| return -1; |
| } |