blob: dff48ef69a621cfa4c1d8fd584532f2cf976f652 [file] [log] [blame]
#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;
}