Files
CgeNet/win32/Poll.c

217 lines
5.0 KiB
C
Raw Normal View History

2026-08-08 16:00:39 +03:00
#include "Internal.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define INITIAL_CAPACITY 4
static int wsaToErrorCode(int value) {
switch (value) {
case 0:
return CGE_NET_EOK;
case WSAEINVAL:
return CGE_NET_EINVAL;
case WSAENOBUFS:
case WSA_NOT_ENOUGH_MEMORY:
return CGE_NET_ENOMEM;
case WSAEWOULDBLOCK:
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(WSAPOLLFD));
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;
WSAPOLLFD *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(WSAPOLLFD));
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(WSAPOLLFD));
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 != INVALID_SOCKET);
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 | POLLERR | POLLHUP))
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 != INVALID_SOCKET);
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;
if ((ready = WSAPoll(p->pfd, (ULONG)p->size, timeout)) == SOCKET_ERROR) {
p->lastError = wsaToErrorCode(WSAGetLastError());
return 0;
}
for (i = 0; i < p->size && ready > 0; i++) {
processEvent(p, p->data + i);
}
return 1;
}