/*****************************************************************************\
* Copyright (c) 2004 Pelle Johansson. *
* All rights reserved. *
* *
* This file is part of the moftpd package. Use and distribution of *
* this software is governed by the terms in the file LICENCE, which *
* should have come with this package. *
\*****************************************************************************/
/* $moftpd: events.c 1251 2005-03-06 22:24:29Z morth $ */
#include "system.h"
#include "events.h"
#include "utf8fs/memory.h"
/*
* This is a red-black tree implementation for handling the file descriptors.
*/
typedef struct fdHandlerNode
{
int fd;
fdHandlerFun_t handler;
void *user;
struct fdHandlerNode *p, *l, *r;
unsigned int red:1;
} fdHandlerNode_t;
static fdHandlerNode_t *readTree, *writeTree;
int numFds = 0;
extern int debug;
void events_init (void)
{
pfree (readTree, NULL);
readTree = NULL;
pfree (writeTree, NULL);
writeTree = NULL;
numFds = 0;
events_einit ();
}
int event_channels (void)
{
return numFds;
}
static void print_tree (fdHandlerNode_t *x, int indent)
{
if (x)
{
fprintf (stderr, "%*s%04d: %s\n", indent, "", x->fd, x->red? "red" : "black");
print_tree (x->l, indent + 2);
print_tree (x->r, indent + 2);
}
else
fprintf (stderr, "%*snull: black\n", indent, "");
}
static fdHandlerNode_t *find_fd_or_parent (int fd, fdHandlerNode_t *x)
{
if (!x)
return NULL;
while (x->fd != fd)
{
if (x->fd > fd)
{
if (!x->l)
return x;
x = x->l;
}
else
{
if (!x->r)
return x;
x = x->r;
}
}
return x;
}
static void left_rotate (fdHandlerNode_t *node)
{
fdHandlerNode_t *x;
x = node->r;
pfree (node->r, node);
node->r = pattach (x->l, node);
if (x->l)
x->l->p = node;
x->p = node->p;
pattach (node, x);
if (!node->p)
{
if (node == readTree)
readTree = prootify (x);
else
writeTree = prootify (x);
}
else
{
pfree (node, node->p);
if (node == node->p->l)
node->p->l = pattach (x, node->p);
else
node->p->r = pattach (x, node->p);
}
x->l = node;
node->p = x;
}
static void right_rotate (fdHandlerNode_t *node)
{
fdHandlerNode_t *x;
x = node->l;
pfree (node->l, node);
node->l = pattach (x->r, node);
if (x->r)
x->r->p = node;
x->p = node->p;
pattach (node, x);
if (!node->p)
{
if (node == readTree)
readTree = prootify (x);
else
writeTree = prootify (x);
}
else
{
pfree (node, node->p);
if (node == node->p->l)
node->p->l = pattach (x, node->p);
else
node->p->r = pattach (x, node->p);
}
x->r = node;
node->p = x;
}
static void insert_node (fdHandlerNode_t *node, fdHandlerNode_t *p)
{
node->p = p;
if (node->fd < p->fd)
p->l = node;
else
p->r = node;
node->red = 1;
while (node->p->red)
{
if (node->p == node->p->p->l)
{
p = node->p->p->r;
if (p && p->red)
{
p->red = node->p->red = 0;
node = p->p;
if (!node->p)
break;
else
node->red = 1;
}
else
{
if (node == node->p->r)
{
node = node->p;
left_rotate (node);
}
node->p->red = 0;
node->p->p->red = 1;
right_rotate (node->p->p);
}
}
else
{
p = node->p->p->l;
if (p && p->red)
{
p->red = node->p->red = 0;
node = p->p;
if (!node->p)
break;
else
node->red = 1;
}
else
{
if (node == node->p->l)
{
node = node->p;
right_rotate (node);
}
node->p->red = 0;
node->p->p->red = 1;
left_rotate (node->p->p);
}
}
}
}
static fdHandlerNode_t *insert_read_node (int fd, fdHandlerFun_t handler, void *user, fdHandlerNode_t *p)
{
fdHandlerNode_t *node = palloc (sizeof (fdHandlerNode_t), p, NULL);
node->fd = fd;
node->handler = handler;
node->user = user;
if (p)
insert_node (node, p);
else
readTree = node;
numFds++;
if (debug >= 2)
print_tree (readTree, 0);
return node;
}
static fdHandlerNode_t *insert_write_node (int fd, fdHandlerFun_t handler, void *user, fdHandlerNode_t *p)
{
fdHandlerNode_t *node = palloc (sizeof (fdHandlerNode_t), p, NULL);
node->fd = fd;
node->handler = handler;
node->user = user;
if (p)
insert_node (node, p);
else
writeTree = node;
numFds++;
if (debug >= 2)
print_tree (writeTree, 0);
return node;
}
static void delete_fd_node (fdHandlerNode_t *node)
{
fdHandlerNode_t *x, *y, *p;
int r;
if (!node->l || !node->r)
{
x = node;
if (x->l)
y = x->l;
else
y = x->r;
}
else
{
x = node->r;
while (x->l)
x = x->l;
/*
* Since we need to delete the node sent in let node and x
* switch places.
*/
if (node->p)
{
if (node == node->p->l)
node->p->l = pattach (x, node->p);
else
node->p->r = pattach (x, node->p);
pfree (node, node->p);
}
else if (node == readTree)
readTree = prootify (x);
else
writeTree = prootify (x);
y = node->p;
if (x->p == node)
node->p = x;
else
node->p = x->p;
x->p = y;
y = x->r;
x->l = pattach (node->l, x);
x->l->p = x;
if (node->r == x)
x->r = pattach (node, x);
else
{
x->r = pattach (node->r, x);
x->r->p = x;
node->p->l = node;
}
r = x->red;
x->red = node->red;
node->red = r;
x = node;
}
p = x->p;
if (y)
y->p = p;
pfree (x, p);
if (!p)
{
if (x == writeTree)
writeTree = prootify (y);
else
readTree = prootify (y);
if (y)
y->red = 0;
}
else
{
if (x == p->l)
p->l = pattach (y, p);
else
p->r = pattach (y, p);
if (!x->red)
{
while (!y || (y->p && !y->red))
{
if (y == p->l)
{
x = p->r;
if (x->red)
{
x->red = 0;
p->red = 1;
left_rotate (p);
x = p->r;
}
if (!x->r || !x->r->red)
{
if (!x->l || !x->l->red)
{
x->red = 1;
y = p;
p = p->p;
continue;
}
x->l->red = 0;
x->red = 1;
right_rotate (x);
x = p->r;
}
x->red = p->red;
p->red = x->r->red = 0;
left_rotate (p);
y = NULL;
break;
}
else
{
x = p->l;
if (x->red)
{
x->red = 0;
p->red = 1;
right_rotate (p);
x = p->l;
}
if (!x->l || !x->l->red)
{
if (!x->r || !x->r->red)
{
x->red = 1;
y = p;
p = p->p;
continue;
}
x->r->red = 0;
x->red = 1;
left_rotate (x);
x = p->l;
}
x->red = p->red;
p->red = x->l->red = 0;
right_rotate (p);
y = NULL;
break;
}
}
if (y)
y->red = 0;
}
}
numFds--;
}
int add_read_fd (int fd, fdHandlerFun_t handler, void *user)
{
fdHandlerNode_t *node;
if (!handler)
{
errno = EINVAL;
return -1;
}
node = find_fd_or_parent (fd, readTree);
if (node && node->fd == fd)
{
node->handler = handler;
node->user = user;
return 0;
}
node = insert_read_node (fd, handler, user, node);
if (!node)
return -1;
if (events_earf (fd, node))
{
delete_fd_node (node);
if (debug >= 2)
print_tree (readTree, 0);
return -1;
}
return 0;
}
int add_write_fd (int fd, fdHandlerFun_t handler, void *user)
{
fdHandlerNode_t *node;
if (!handler)
{
errno = EINVAL;
return -1;
}
node = find_fd_or_parent (fd, writeTree);
if (node && node->fd == fd)
{
node->handler = handler;
node->user = user;
return 0;
}
node = insert_write_node (fd, handler, user, node);
if (!node)
return -1;
if (events_eawf (fd, node))
{
delete_fd_node (node);
if (debug >= 2)
print_tree (writeTree, 0);
return -1;
}
return 0;
}
void remove_read_fd (int fd)
{
fdHandlerNode_t *node;
node = find_fd_or_parent (fd, readTree);
if (node && node->fd == fd)
{
delete_fd_node (node);
if (debug >= 2)
print_tree (readTree, 0);
events_errf (fd);
}
}
void remove_write_fd (int fd)
{
fdHandlerNode_t *node;
node = find_fd_or_parent (fd, writeTree);
if (node && node->fd == fd)
{
delete_fd_node (node);
if (debug >= 2)
print_tree (writeTree, 0);
events_erwf (fd);
}
}
int events_run_handler (int fd, int isWrite, int urgent)
{
fdHandlerNode_t *node;
node = find_fd_or_parent (fd, isWrite? writeTree : readTree);
if (node && node->fd == fd)
return node->handler (fd, node->user, urgent);
return 0;
}
int events_run_data (void *data, int urgent)
{
fdHandlerNode_t *node = data;
return node->handler (node->fd, node->user, urgent);
}
syntax highlighted by Code2HTML, v. 0.9.1