Initial commit of Pollcat library

This commit is contained in:
2022-06-28 15:25:57 -07:00
commit bbd4625d43
26 changed files with 3534 additions and 0 deletions

370
src/pollcat.c Normal file
View File

@@ -0,0 +1,370 @@
/** Wrapper structure for poll()
*
* This file is part of the Pollcat Library.
* Copyright (C) 2022 Expatria Technologies Inc.
* Contact: Morgan Hughes <morgan@expatria.ca>
*
* The Pollcat Library is free software: you can redistribute it and/or modify it under
* the terms of the the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received copies of the GNU General Public License and the GNU Lesser
* General Public License along with the Pollcat Library. If not, see
* https://www.gnu.org/licenses/
*
* vim:ts=4:noexpandtab
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <pollcat.h>
#include "private.h"
static struct pollfd *pollcat_list = NULL;
static size_t pollcat_size = 0;
static size_t pollcat_used = 0;
static int pollcat_last = -1;
static int pollcat_time_current = -1;
/** Optional function to call on fatal error; if unset an assert()-style message will be
* printed on stderr and abort() called.
*/
typedef void (* pollcat_assert_f) (const char *file, int line, const char *mesg);
pollcat_assert_f pollcat_assert_func = NULL;
/** Base timeout for wrapped poll() calls in milliseconds; pollcat_time_reduce() may be
* called to reduce this on a per-loop basis to improve latency of things like timers.
* The default is POLLCAT_TIME_BASE.
*/
int pollcat_time_base = POLLCAT_TIME_BASE;
/** Minimum timeout for wrapped poll() calls in milliseconds; pollcat_time_reduce() will
* not reduce the per-loop timeout below this value. The default is POLLCAT_TIME_MINIMUM.
*/
int pollcat_time_minimum = POLLCAT_TIME_MINIMUM;
/** Initial size of the pollfd array. The default is POLLCAT_SIZE_INITIAL.
*/
size_t pollcat_size_initial = POLLCAT_SIZE_INITIAL;
/** Incremental size of the pollfd array. The default is POLLCAT_SIZE_INCREMENT.
*/
size_t pollcat_size_increment = POLLCAT_SIZE_INCREMENT;
/** Cleanup function and atexit flag */
static int pollcat_exit = 0;
static void pollcat_done (void)
{
free(pollcat_list);
pollcat_list = NULL;
pollcat_size = 0;
pollcat_used = 0;
pollcat_last = -1;
}
/** Init function */
static void pollcat_init (void)
{
/* Register cleanup */
if ( !pollcat_exit )
{
atexit(pollcat_done);
pollcat_exit = 1;
}
if ( pollcat_list )
return;
if ( pollcat_size_initial < 1 )
pollcat_size_initial = 1;
if ( pollcat_size_increment < 1 )
pollcat_size_increment = 1;
pollcat_list = malloc(pollcat_size_initial * sizeof(struct pollfd));
memset(pollcat_list, 0xFF, pollcat_size_initial * sizeof(struct pollfd));
POLLCAT_ASSERT(pollcat_list != NULL);
pollcat_size = pollcat_size_initial;
pollcat_used = 0;
pollcat_last = -1;
pollcat_time_current = pollcat_time_base;
}
/** Binary search compare function */
static int pollcat_comp (const void *va, const void *vb)
{
const struct pollfd *pa = (const struct pollfd *)va;
const struct pollfd *pb = (const struct pollfd *)vb;
POLLCAT_ASSERT(va != NULL);
POLLCAT_ASSERT(vb != NULL);
return pa->fd - pb->fd;
}
/** Binary search function */
static struct pollfd *pollcat_search (int fd)
{
struct pollfd key = { 0 };
struct pollfd *ret;
if ( pollcat_used < 1 )
return NULL;
/* Check cached index first */
if ( pollcat_last > -1 && pollcat_last < pollcat_used
&& pollcat_list[pollcat_last].fd == fd )
return &pollcat_list[pollcat_last];
/* Do binary search and cache result */
key.fd = fd;
ret = bsearch(&key, pollcat_list, sizeof(struct pollfd), pollcat_used, pollcat_comp);
pollcat_last = ret ? ret - pollcat_list : -1;
return ret;
}
/** Add an FD to the poll array and set the events requested
*
* \param fd File descriptor
* \param events Bitmap of events from poll.h, such as POLLIN
*/
void pollcat_fd_add (int fd, short int events)
{
struct pollfd *pfd;
/* Auto init, no-return if failed */
pollcat_init();
/* Update existing if found */
if ( (pfd = pollcat_search(fd)) )
{
pfd->events = events;
return;
}
/* Grow list if full */
if ( pollcat_used == pollcat_size )
{
size_t new = pollcat_size + pollcat_size_increment;
pfd = malloc(new * sizeof(struct pollfd));
POLLCAT_ASSERT(pfd != NULL);
memcpy(pfd, pollcat_list, pollcat_size * sizeof(struct pollfd));
memset(pfd + pollcat_size, 0xFF, pollcat_size_increment * sizeof(struct pollfd));
free(pollcat_list);
pollcat_list = pfd;
pollcat_size += pollcat_size_increment;
}
/* Add new entry and increment used */
pollcat_list[pollcat_used].fd = fd;
pollcat_list[pollcat_used].events = events;
pollcat_used++;
/* Re-sort list */
qsort(pollcat_list, pollcat_used, sizeof(struct pollfd), pollcat_comp);
}
/** Get request bits of an FD in the poll array
*
* \param fd File descriptor
*
* \return bitmap on success, <0 if not found
*/
short int pollcat_events_get (int fd)
{
struct pollfd *pfd;
/* Auto init, no-return if failed */
pollcat_init();
if ( !(pfd = pollcat_search(fd)) )
return -1;
return pfd->events;
}
/** Set request bits on an FD in the poll array
*
* \param fd File descriptor
* \param events Bitmap of events from poll.h, such as POLLIN
*/
void pollcat_events_set (int fd, short int events)
{
struct pollfd *pfd;
/* Auto init, no-return if failed */
pollcat_init();
if ( !(pfd = pollcat_search(fd)) )
return;
pfd->events = events;
}
/** Get result bits of an FD in the poll array
*
* \param fd File descriptor
*
* \return bitmap on success, 0 on error
*/
short int pollcat_revents (int fd)
{
struct pollfd *pfd;
/* Auto init, no-return if failed */
pollcat_init();
if ( !(pfd = pollcat_search(fd)) )
return 0;
return pfd->revents;
}
/** Remove an FD from the poll array
*
* \param fd File descriptor
*/
void pollcat_fd_remove (int fd)
{
struct pollfd *pfd;
/* Auto init, no-return if failed */
pollcat_init();
if ( !(pfd = pollcat_search(fd)) )
return;
/* Last entry in list is cheap */
if ( 1 == pollcat_used )
{
pollcat_used--;
pollcat_last = -1;
return;
}
/* Decrement used and move last entry into vacant slot */
pollcat_used--;
pfd->fd = pollcat_list[pollcat_used].fd;
pfd->events = pollcat_list[pollcat_used].events;
memset(&pollcat_list[pollcat_used], 0xFF, sizeof(struct pollfd));
/* Re-sort list */
qsort(pollcat_list, pollcat_used, sizeof(struct pollfd), pollcat_comp);
}
/** Reduce the current timeout
*
* \param time New timeout in milliseconds
*/
void pollcat_time_reduce (int time)
{
if ( time < pollcat_time_current )
pollcat_time_current = time;
}
/** Wraps the system poll() command using the internal pollfd array and timeout
*
* \return as per poll(), <0 on error
*/
int pollcat_poll (void)
{
int ret;
/* Auto init, no-return if failed */
pollcat_init();
if ( pollcat_time_current < pollcat_time_minimum )
pollcat_time_current = pollcat_time_minimum;
ret = poll(pollcat_list, pollcat_used, pollcat_time_current);
pollcat_time_current = pollcat_time_base;
return ret;
}
#if POLLCAT_DEBUG
static const char *pollcat_bits (short int events)
{
static char buff[20] = { 0 };
char *ins = buff;
*ins++ = events & POLLIN ? 'I' : '_';
*ins++ = events & POLLPRI ? 'P' : '_';
*ins++ = events & POLLOUT ? 'O' : '_';
#ifdef POLLRDHUP
*ins++ = events & POLLRDHUP ? 'R' : '_';
#endif
*ins++ = events & POLLERR ? 'E' : '_';
*ins++ = events & POLLHUP ? 'H' : '_';
*ins++ = events & POLLNVAL ? 'N' : '_';
#ifdef POLLMSG
*ins++ = events & POLLMSG ? 'M' : '_';
#endif
#ifdef _XOPEN_SOURCE
*ins++ = ',';
*ins++ = 'R';
*ins++ = events & POLLRDNORM ? 'N' : '_';
*ins++ = events & POLLRDBAND ? 'B' : '_';
*ins++ = ',';
*ins++ = 'W';
*ins++ = events & POLLWRNORM ? 'N' : '_';
*ins++ = events & POLLWRBAND ? 'B' : '_';
#endif
return buff;
}
void pollcat_dump (FILE *fp)
{
int i;
if ( !fp && stderr )
fp = stderr;
if ( !fp && stdout )
fp = stdout;
fprintf(fp, "pollcat: list %p, used %zu / size %zu\n",
pollcat_list, pollcat_used, pollcat_size);
fprintf(fp, "fd events revents path\n");
for ( i = 0; i < pollcat_used; i++ )
{
char path[256];
char link[256];
snprintf(path, sizeof(path), "/proc/self/fd/%d", pollcat_list[i].fd);
memset(link, 0, sizeof(link));
if ( readlink(path, link, sizeof(link) - 1) < 0 )
snprintf(link, sizeof(link), "err:%s", strerror(errno));
fprintf(fp, "%2d %-8s ", pollcat_list[i].fd,
pollcat_bits(pollcat_list[i].events));
fprintf(fp, "%-8s %s\n", pollcat_bits(pollcat_list[i].revents), link);
}
}
#endif