220 lines
5.0 KiB
C
220 lines
5.0 KiB
C
|
|
#include "Internal.h"
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <assert.h>
|
||
|
|
|
||
|
|
#define INITIAL_CAPACITY 4
|
||
|
|
|
||
|
|
static int errnoToErrorCode(int value) {
|
||
|
|
switch (value) {
|
||
|
|
case 0:
|
||
|
|
return CGE_NET_EOK;
|
||
|
|
|
||
|
|
case EINVAL:
|
||
|
|
return CGE_NET_EINVAL;
|
||
|
|
|
||
|
|
case ENOMEM:
|
||
|
|
return CGE_NET_ENOMEM;
|
||
|
|
|
||
|
|
case EWOULDBLOCK:
|
||
|
|
return CGE_NET_EWOULDBLOCK;
|
||
|
|
|
||
|
|
default:
|
||
|
|
return CGE_NET_EUNKNOWN;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
CgeNetPoll *CgeNetPollCreate(int *result) {
|
||
|
|
CgeNetPoll *poll;
|
||
|
|
|
||
|
|
if (!(poll = malloc(sizeof(CgeNetPoll)))) {
|
||
|
|
if (result)
|
||
|
|
*result = CGE_NET_ENOMEM;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
poll->data = malloc(INITIAL_CAPACITY * sizeof(struct PollData));
|
||
|
|
poll->pfd = malloc(INITIAL_CAPACITY * sizeof(struct pollfd));
|
||
|
|
if (!poll->data || !poll->pfd) {
|
||
|
|
free(poll->data);
|
||
|
|
free(poll->pfd);
|
||
|
|
free(poll);
|
||
|
|
|
||
|
|
if (result)
|
||
|
|
*result = CGE_NET_ENOMEM;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
poll->size = 0;
|
||
|
|
poll->capacity = INITIAL_CAPACITY;
|
||
|
|
poll->lastError = CGE_NET_EOK;
|
||
|
|
|
||
|
|
if (result)
|
||
|
|
*result = CGE_NET_EOK;
|
||
|
|
return poll;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CgeNetPollFree(CgeNetPoll *poll) {
|
||
|
|
if (!poll)
|
||
|
|
return;
|
||
|
|
|
||
|
|
free(poll->pfd);
|
||
|
|
free(poll->data);
|
||
|
|
free(poll);
|
||
|
|
}
|
||
|
|
|
||
|
|
int CgeNetPollErrorCode(CgeNetPoll *poll) {
|
||
|
|
assert(poll != NULL);
|
||
|
|
return poll->lastError;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ensureCapacity(CgeNetPoll *poll) {
|
||
|
|
struct PollData *newData;
|
||
|
|
struct pollfd *newPfd;
|
||
|
|
size_t newCapacity;
|
||
|
|
|
||
|
|
assert(poll != NULL);
|
||
|
|
assert(poll->capacity > 0);
|
||
|
|
|
||
|
|
if (poll->size < poll->capacity)
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
newCapacity = poll->capacity * 2;
|
||
|
|
newData = malloc(newCapacity * sizeof(struct PollData));
|
||
|
|
newPfd = malloc(newCapacity * sizeof(struct pollfd));
|
||
|
|
|
||
|
|
if (!newData || !newPfd) {
|
||
|
|
free(newData);
|
||
|
|
free(newPfd);
|
||
|
|
poll->lastError = CGE_NET_ENOMEM;
|
||
|
|
return 0;
|
||
|
|
} else {
|
||
|
|
memcpy(newData, poll->data, poll->size * sizeof(struct PollData));
|
||
|
|
memcpy(newPfd, poll->pfd, poll->size * sizeof(struct pollfd));
|
||
|
|
free(poll->data);
|
||
|
|
free(poll->pfd);
|
||
|
|
}
|
||
|
|
|
||
|
|
poll->data = newData;
|
||
|
|
poll->pfd = newPfd;
|
||
|
|
poll->capacity = newCapacity;
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
int CgeNetPollAdd(CgeNetPoll *poll, CgeNetSocket *socket,
|
||
|
|
CgeNetCallback callback, int events) {
|
||
|
|
assert(poll != NULL);
|
||
|
|
assert(socket != NULL);
|
||
|
|
assert(socket->handle != -1);
|
||
|
|
assert(callback != NULL);
|
||
|
|
assert(events & CGE_NET_READABLE || events & CGE_NET_WRITABLE);
|
||
|
|
|
||
|
|
if (socket->pollObject) {
|
||
|
|
poll->lastError = CGE_NET_EINVAL;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!ensureCapacity(poll))
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
assert(poll->size + 1 <= poll->capacity);
|
||
|
|
|
||
|
|
socket->pollObject = poll;
|
||
|
|
socket->pollIndex = poll->size;
|
||
|
|
|
||
|
|
poll->data[poll->size].socket = socket;
|
||
|
|
poll->data[poll->size].callback = callback;
|
||
|
|
poll->data[poll->size].events = events;
|
||
|
|
poll->pfd[poll->size].fd = socket->handle;
|
||
|
|
poll->pfd[poll->size].events = 0;
|
||
|
|
poll->pfd[poll->size].revents = 0;
|
||
|
|
|
||
|
|
if (events & CGE_NET_READABLE)
|
||
|
|
poll->pfd[poll->size].events |= POLLIN;
|
||
|
|
if (events & CGE_NET_WRITABLE)
|
||
|
|
poll->pfd[poll->size].events |= POLLOUT;
|
||
|
|
|
||
|
|
poll->size++;
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void processEvent(CgeNetPoll *poll, struct PollData *data) {
|
||
|
|
size_t index;
|
||
|
|
int events, revents;
|
||
|
|
|
||
|
|
assert(poll != NULL);
|
||
|
|
assert(data != NULL);
|
||
|
|
assert(data >= poll->data && data < poll->data + poll->size);
|
||
|
|
|
||
|
|
index = data - poll->data;
|
||
|
|
revents = poll->pfd[index].revents;
|
||
|
|
events = poll->pfd[index].revents = 0;
|
||
|
|
|
||
|
|
poll->pfd[index].events = 0;
|
||
|
|
if (data->events & CGE_NET_READABLE)
|
||
|
|
poll->pfd[index].events |= POLLIN;
|
||
|
|
if (data->events & CGE_NET_WRITABLE)
|
||
|
|
poll->pfd[index].events |= POLLOUT;
|
||
|
|
|
||
|
|
if (revents & (POLLIN | POLLHUP | POLLERR))
|
||
|
|
events |= CGE_NET_READABLE;
|
||
|
|
if (revents & POLLOUT)
|
||
|
|
events |= CGE_NET_WRITABLE;
|
||
|
|
|
||
|
|
if (events)
|
||
|
|
data->callback(data->socket, events);
|
||
|
|
}
|
||
|
|
|
||
|
|
int CgeNetPollRemove(CgeNetPoll *poll, CgeNetSocket *socket) {
|
||
|
|
size_t index;
|
||
|
|
|
||
|
|
assert(poll != NULL);
|
||
|
|
assert(socket != NULL);
|
||
|
|
assert(socket->handle != -1);
|
||
|
|
|
||
|
|
if (socket->pollObject != poll) {
|
||
|
|
poll->lastError = CGE_NET_EINVAL;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
assert(poll->size > 0);
|
||
|
|
index = socket->pollIndex;
|
||
|
|
socket->pollObject = NULL;
|
||
|
|
socket->pollIndex = 0;
|
||
|
|
poll->size--;
|
||
|
|
|
||
|
|
if (index < poll->size) {
|
||
|
|
poll->data[poll->size].socket->pollIndex = index;
|
||
|
|
poll->data[index] = poll->data[poll->size];
|
||
|
|
poll->pfd[index] = poll->pfd[poll->size];
|
||
|
|
processEvent(poll, poll->data + index);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
int CgeNetPollWait(CgeNetPoll *p, int timeout) {
|
||
|
|
int ready;
|
||
|
|
size_t i;
|
||
|
|
|
||
|
|
assert(p != NULL);
|
||
|
|
|
||
|
|
if (p->size == 0)
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
do {
|
||
|
|
ready = poll(p->pfd, p->size, timeout);
|
||
|
|
} while (ready < 0 && errno == EINTR);
|
||
|
|
|
||
|
|
if (ready < 0) {
|
||
|
|
p->lastError = errnoToErrorCode(errno);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < p->size && ready > 0; i++) {
|
||
|
|
processEvent(p, p->data + i);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|