mirror of https://github.com/CPunch/Laika.git
292 lines
8.9 KiB
C
292 lines
8.9 KiB
C
#include "net/lpolllist.h"
|
|
|
|
#include "core/lerror.h"
|
|
#include "core/lmem.h"
|
|
|
|
/* ===================================[[ Helper Functions ]]==================================== */
|
|
|
|
typedef struct sLaika_hashMapElem
|
|
{
|
|
SOCKET fd;
|
|
struct sLaika_socket *sock;
|
|
} tLaika_hashMapElem;
|
|
|
|
int elem_compare(const void *a, const void *b, void *udata)
|
|
{
|
|
const tLaika_hashMapElem *ua = a;
|
|
const tLaika_hashMapElem *ub = b;
|
|
|
|
return ua->fd != ub->fd;
|
|
}
|
|
|
|
uint64_t elem_hash(const void *item, uint64_t seed0, uint64_t seed1)
|
|
{
|
|
const tLaika_hashMapElem *u = item;
|
|
return (uint64_t)(u->fd);
|
|
}
|
|
|
|
/* =====================================[[ PollList API ]]====================================== */
|
|
|
|
void laikaP_initPList(struct sLaika_pollList *pList)
|
|
{
|
|
laikaS_init();
|
|
|
|
/* setup hashmap */
|
|
pList->sockets = hashmap_new(sizeof(tLaika_hashMapElem), POLLSTARTCAP, 0, 0, elem_hash,
|
|
elem_compare, NULL, NULL);
|
|
|
|
/* laikaP_pollList() will allocate these buffer */
|
|
laikaM_initVector(pList->revents, POLLSTARTCAP / GROW_FACTOR);
|
|
laikaM_initVector(pList->outQueue, POLLSTARTCAP / GROW_FACTOR);
|
|
|
|
#ifdef LAIKA_USE_EPOLL
|
|
/* setup our epoll */
|
|
memset(&pList->ev, 0, sizeof(struct epoll_event));
|
|
if ((pList->epollfd = epoll_create(POLLSTARTCAP)) == -1)
|
|
LAIKA_ERROR("epoll_create() failed!\n");
|
|
|
|
#else
|
|
/* laikaP_addSock will allocate this buffer */
|
|
laikaM_initVector(pList->fds, POLLSTARTCAP / GROW_FACTOR);
|
|
#endif
|
|
}
|
|
|
|
void laikaP_cleanPList(struct sLaika_pollList *pList)
|
|
{
|
|
laikaM_free(pList->revents);
|
|
laikaM_free(pList->outQueue);
|
|
hashmap_free(pList->sockets);
|
|
|
|
#ifdef LAIKA_USE_EPOLL
|
|
close(pList->epollfd);
|
|
#else
|
|
laikaM_free(pList->fds);
|
|
#endif
|
|
|
|
laikaS_cleanUp();
|
|
}
|
|
|
|
void laikaP_addSock(struct sLaika_pollList *pList, struct sLaika_socket *sock)
|
|
{
|
|
/* add socket to hashmap */
|
|
hashmap_set(pList->sockets, &(tLaika_hashMapElem){.fd = sock->sock, .sock = sock});
|
|
|
|
#ifdef LAIKA_USE_EPOLL
|
|
pList->ev.events = EPOLLIN;
|
|
pList->ev.data.ptr = (void *)sock;
|
|
|
|
if (epoll_ctl(pList->epollfd, EPOLL_CTL_ADD, sock->sock, &pList->ev) == -1)
|
|
LAIKA_ERROR("epoll_ctl [ADD] failed\n");
|
|
|
|
#else
|
|
/* allocate space in array & add PollFD */
|
|
laikaM_growVector(PollFD, pList->fds, 1);
|
|
pList->fds[laikaM_countVector(pList->fds)++] = (PollFD){sock->sock, POLLIN};
|
|
#endif
|
|
}
|
|
|
|
void laikaP_rmvSock(struct sLaika_pollList *pList, struct sLaika_socket *sock)
|
|
{
|
|
int i;
|
|
|
|
/* remove socket from hashmap */
|
|
hashmap_delete(pList->sockets, &(tLaika_hashMapElem){.fd = sock->sock, .sock = sock});
|
|
|
|
/* make sure peer isn't in outQueue */
|
|
for (i = 0; i < laikaM_countVector(pList->outQueue); i++) {
|
|
if ((void *)pList->outQueue[i] == (void *)sock) {
|
|
laikaM_rmvVector(pList->outQueue, i, 1);
|
|
}
|
|
}
|
|
|
|
#ifdef LAIKA_USE_EPOLL
|
|
/* epoll_event* isn't needed with EPOLL_CTL_DEL, however we still need to pass a NON-NULL
|
|
* pointer. [see: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html#BUGS] */
|
|
if (epoll_ctl(pList->epollfd, EPOLL_CTL_DEL, sock->sock, &pList->ev) == -1) {
|
|
/* non-fatal error, socket probably just didn't exist, so ignore it. */
|
|
LAIKA_WARN("epoll_ctl [DEL] failed\n");
|
|
}
|
|
#else
|
|
|
|
/* search fds for socket, remove it and shrink array */
|
|
for (i = 0; i < laikaM_countVector(pList->fds); i++) {
|
|
if (pList->fds[i].fd == sock->sock) {
|
|
/* remove from array */
|
|
laikaM_rmvVector(pList->fds, i, 1);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void laikaP_addPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock)
|
|
{
|
|
if (sock->setPollOut)
|
|
return;
|
|
|
|
#ifdef LAIKA_USE_EPOLL
|
|
pList->ev.events = EPOLLIN | EPOLLOUT;
|
|
pList->ev.data.ptr = (void *)sock;
|
|
if (epoll_ctl(pList->epollfd, EPOLL_CTL_MOD, sock->sock, &pList->ev) == -1) {
|
|
/* non-fatal error, socket probably just didn't exist, so ignore it. */
|
|
LAIKA_WARN("epoll_ctl [MOD] failed\n");
|
|
}
|
|
#else
|
|
int i;
|
|
|
|
/* search fds for socket, add POLLOUT flag */
|
|
for (i = 0; i < laikaM_countVector(pList->fds); i++) {
|
|
if (pList->fds[i].fd == sock->sock) {
|
|
pList->fds[i].events = POLLIN | POLLOUT;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
sock->setPollOut = true;
|
|
}
|
|
|
|
void laikaP_rmvPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock)
|
|
{
|
|
if (!sock->setPollOut)
|
|
return;
|
|
|
|
#ifdef LAIKA_USE_EPOLL
|
|
pList->ev.events = EPOLLIN;
|
|
pList->ev.data.ptr = (void *)sock;
|
|
if (epoll_ctl(pList->epollfd, EPOLL_CTL_MOD, sock->sock, &pList->ev) == -1) {
|
|
/* non-fatal error, socket probably just didn't exist, so ignore it. */
|
|
LAIKA_WARN("epoll_ctl [MOD] failed\n");
|
|
}
|
|
#else
|
|
int i;
|
|
|
|
/* search fds for socket, remove POLLOUT flag */
|
|
for (i = 0; i < laikaM_countVector(pList->fds); i++) {
|
|
if (pList->fds[i].fd == sock->sock) {
|
|
pList->fds[i].events = POLLIN;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
sock->setPollOut = false;
|
|
}
|
|
|
|
void laikaP_pushOutQueue(struct sLaika_pollList *pList, struct sLaika_socket *sock)
|
|
{
|
|
int i;
|
|
|
|
/* first, check that we don't have this peer in the queue already */
|
|
for (i = 0; i < laikaM_countVector(pList->outQueue); i++) {
|
|
if (pList->outQueue[i] == sock)
|
|
return; /* found it :) */
|
|
}
|
|
|
|
laikaM_growVector(struct sLaika_socket *, pList->outQueue, 1);
|
|
pList->outQueue[laikaM_countVector(pList->outQueue)++] = sock;
|
|
}
|
|
|
|
void laikaP_resetOutQueue(struct sLaika_pollList *pList)
|
|
{
|
|
laikaM_countVector(pList->outQueue) = 0; /* ez lol */
|
|
}
|
|
|
|
void laikaP_flushOutQueue(struct sLaika_pollList *pList)
|
|
{
|
|
struct sLaika_socket *sock;
|
|
int i;
|
|
|
|
/* flush pList's outQueue */
|
|
for (i = 0; i < laikaM_countVector(pList->outQueue); i++) {
|
|
sock = pList->outQueue[i];
|
|
LAIKA_DEBUG("sending OUT to %p\n", sock);
|
|
if (sock->onPollOut && !sock->onPollOut(sock) && sock->onPollFail)
|
|
sock->onPollFail(sock, sock->uData);
|
|
}
|
|
laikaP_resetOutQueue(pList);
|
|
}
|
|
|
|
struct sLaika_pollEvent *laikaP_poll(struct sLaika_pollList *pList, int timeout, int *_nevents)
|
|
{
|
|
int nEvents, i;
|
|
|
|
laikaM_countVector(pList->revents) = 0; /* reset revent array */
|
|
#ifdef LAIKA_USE_EPOLL
|
|
/* fastpath: we store the sLaika_socket* pointer directly in the epoll_data_t, saving us a
|
|
lookup into our socket hashmap not to mention the various improvements epoll() has over
|
|
poll() :D
|
|
*/
|
|
nEvents = epoll_wait(pList->epollfd, pList->ep_events, MAX_EPOLL_EVENTS, timeout);
|
|
|
|
if (SOCKETERROR(nEvents))
|
|
LAIKA_ERROR("epoll_wait() failed!\n");
|
|
|
|
for (i = 0; i < nEvents; i++) {
|
|
/* add event to revent array */
|
|
laikaM_growVector(struct sLaika_pollEvent, pList->revents, 1);
|
|
pList->revents[laikaM_countVector(pList->revents)++] =
|
|
(struct sLaika_pollEvent){.sock = pList->ep_events[i].data.ptr,
|
|
.pollIn = pList->ep_events[i].events & EPOLLIN,
|
|
.pollOut = pList->ep_events[i].events & EPOLLOUT};
|
|
}
|
|
#else
|
|
/* poll returns -1 for error, or the number of events */
|
|
nEvents = poll(pList->fds, laikaM_countVector(pList->fds), timeout);
|
|
|
|
if (SOCKETERROR(nEvents))
|
|
LAIKA_ERROR("poll() failed!\n");
|
|
|
|
/* walk through the returned poll fds, if they have an event, add it to our revents array */
|
|
for (i = 0; i < laikaM_countVector(pList->fds) && nEvents > 0; i++) {
|
|
PollFD pfd = pList->fds[i];
|
|
if (pList->fds[i].revents != 0) {
|
|
/* grab socket from hashmap */
|
|
tLaika_hashMapElem *elem = (tLaika_hashMapElem *)hashmap_get(
|
|
pList->sockets, &(tLaika_hashMapElem){.fd = (SOCKET)pfd.fd});
|
|
|
|
/* insert event into revents array */
|
|
laikaM_growVector(struct sLaika_pollEvent, pList->revents, 1);
|
|
pList->revents[laikaM_countVector(pList->revents)++] =
|
|
(struct sLaika_pollEvent){.sock = elem->sock,
|
|
.pollIn = pfd.revents & POLLIN,
|
|
.pollOut = pfd.revents & POLLOUT};
|
|
|
|
nEvents--;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
*_nevents = laikaM_countVector(pList->revents);
|
|
|
|
/* return revents array */
|
|
return pList->revents;
|
|
}
|
|
|
|
bool laikaP_handleEvent(struct sLaika_pollEvent *evnt)
|
|
{
|
|
bool result = true;
|
|
|
|
/* sanity check */
|
|
if (evnt->sock->onPollIn == NULL || evnt->sock->onPollOut == NULL)
|
|
return true;
|
|
|
|
LAIKA_TRY
|
|
if (evnt->pollIn && !evnt->sock->onPollIn(evnt->sock))
|
|
goto _PHNDLEEVNTFAIL;
|
|
|
|
if (evnt->pollOut && !evnt->sock->onPollIn(evnt->sock))
|
|
goto _PHNDLEEVNTFAIL;
|
|
|
|
if (!evnt->pollIn && !evnt->pollOut)
|
|
goto _PHNDLEEVNTFAIL;
|
|
LAIKA_CATCH
|
|
_PHNDLEEVNTFAIL:
|
|
/* call onFail event */
|
|
if (evnt->sock->onPollFail)
|
|
evnt->sock->onPollFail(evnt->sock, evnt->sock->uData);
|
|
result = false;
|
|
LAIKA_TRYEND
|
|
|
|
return result;
|
|
} |