diff --git a/include/pollcat/pollcat.h b/include/pollcat/pollcat.h index 5ab111e..692293c 100644 --- a/include/pollcat/pollcat.h +++ b/include/pollcat/pollcat.h @@ -1,7 +1,7 @@ /** Wrapper structure for poll() * * This file is part of the Pollcat Library. - * Copyright (C) 2022 Expatria Technologies Inc. + * Copyright (C) 2022,2025 Expatria Technologies Inc. * Contact: Morgan Hughes * * The Pollcat Library is free software: you can redistribute it and/or modify it under @@ -40,9 +40,7 @@ #include #include -#if POLLCAT_DEBUG #include -#endif /** Optional function to call on fatal error; if unset an assert()-style message will be @@ -123,11 +121,9 @@ void pollcat_time_reduce (int time); int pollcat_poll (void); -#if POLLCAT_DEBUG -/** Prints the pollfd array to a given file handle +/** Prints the pollfd array to a given file handle, if POLLCAT_DEBUG is nonzero */ void pollcat_dump (FILE *fp); -#endif #endif /* _INCLUDE_POLLCAT_POLLCAT_H_ */ diff --git a/include/pollcat/timer.h b/include/pollcat/timer.h index fcf3589..2204f54 100644 --- a/include/pollcat/timer.h +++ b/include/pollcat/timer.h @@ -28,8 +28,9 @@ typedef uint64_t pollcat_time_t; -/** Grace period in milliseconds for late timers, set to 0 for exact match */ -extern pollcat_time_t pollcat_timer_dispatch_thresh; +/** This was removed and replaced with a simpler implementation which allowed slow/late + * timers to run reliably. Setting it now has no effect. */ +extern pollcat_time_t pollcat_timer_dispatch_thresh __attribute__((deprecated)); /** Maximum number of times to run the dispatch loop, set to -1 for no limit */ diff --git a/src/pollcat.c b/src/pollcat.c index 40a87e3..a329676 100644 --- a/src/pollcat.c +++ b/src/pollcat.c @@ -1,7 +1,7 @@ /** Wrapper structure for poll() * * This file is part of the Pollcat Library. - * Copyright (C) 2022 Expatria Technologies Inc. + * Copyright (C) 2022,2025 Expatria Technologies Inc. * Contact: Morgan Hughes * * The Pollcat Library is free software: you can redistribute it and/or modify it under @@ -284,6 +284,23 @@ void pollcat_time_reduce (int time) pollcat_time_current = time; } +/** Return the current timeout and reset the current timeout to the base value + * + * \return Timeout in milliseconds + */ +int pollcat_time_value (void) +{ + /* latch current value, which may have been reduced by pollcat_time_reduce() */ + int value = pollcat_time_current; + + /* enforce minimum */ + if ( value < pollcat_time_minimum ) + value = pollcat_time_minimum; + + /* reset to base before return, for next call */ + pollcat_time_current = pollcat_time_base; + return value; +} /** Wraps the system poll() command using the internal pollfd array and timeout * @@ -296,11 +313,7 @@ int pollcat_poll (void) /* 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; + ret = poll(pollcat_list, pollcat_used, pollcat_time_value()); return ret; } diff --git a/src/private.h b/src/private.h index 9315052..73928a5 100644 --- a/src/private.h +++ b/src/private.h @@ -1,7 +1,7 @@ /** Private includes * * This file is part of the Pollcat Library. - * Copyright (C) 2022 Expatria Technologies Inc. + * Copyright (C) 2022,2025 Expatria Technologies Inc. * Contact: Morgan Hughes * * The Pollcat Library is free software: you can redistribute it and/or modify it under @@ -40,4 +40,8 @@ extern pollcat_assert_f pollcat_assert_func; +/** Return the current timeout and reset the current timeout to the base value */ +int pollcat_time_value (void); + + #endif /* _SRC_PRIVATE_H_ */ diff --git a/src/struct.c b/src/struct.c index 1f6d737..97cba3b 100644 --- a/src/struct.c +++ b/src/struct.c @@ -1,7 +1,7 @@ /** Registration struct, iterators, and main-loop convenience functions * * This file is part of the Pollcat Library. - * Copyright (C) 2022 Expatria Technologies Inc. + * Copyright (C) 2022,2025 Expatria Technologies Inc. * Contact: Morgan Hughes * * The Pollcat Library is free software: you can redistribute it and/or modify it under @@ -21,6 +21,7 @@ */ #include #include +#include #include #include "private.h" @@ -145,8 +146,18 @@ int pollcat_loop (int *poll_ret) if ( walk->ops && walk->ops->prepare ) walk->ops->prepare(walk); - /* call pollcat_poll() and optionally save the return value in poll-ret */ - ret = pollcat_poll(); + /* call pollcat_poll() if the iterator list is non-empty, otherwise sleep */ + if ( pollcat_iter_head ) + ret = pollcat_poll(); + else + { + /* pollcat_time_value() returns milliseconds, usleep takes microseconds */ + ret = pollcat_time_value() * 1000; + ret = usleep(ret); + ret = 0; + } + + /* optionally save the return value in poll-ret */ if ( poll_ret ) *poll_ret = ret; diff --git a/src/timer.c b/src/timer.c index 9a38419..ac1629d 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,7 +1,7 @@ /** Millisecond-resolution event timers * * This file is part of the Pollcat Library. - * Copyright (C) 2022 Expatria Technologies Inc. + * Copyright (C) 2022,2025 Expatria Technologies Inc. * Contact: Morgan Hughes * * The Pollcat Library is free software: you can redistribute it and/or modify it under @@ -219,11 +219,8 @@ void pollcat_timer_dispatch (void) while ( timer_head && max-- ) { - pollcat_time_t due = pollcat_now(); - - due -= timer_head->due; - if ( due > pollcat_timer_dispatch_thresh ) - return; + if ( pollcat_now() < timer_head->due ) + return; // detach the head struct pollcat_timer *t = timer_head; diff --git a/tests/Makefile b/tests/Makefile index b4011e5..a84e3f0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,7 @@ # Test stubs Makefile # # This file is part of the Pollcat Library. -# Copyright (C) 2022 Expatria Technologies Inc. +# Copyright (C) 2022,2025 Expatria Technologies Inc. # Contact: Morgan Hughes # # The Pollcat Library is free software: you can redistribute it and/or modify it under the @@ -18,6 +18,10 @@ # # vim:ts=4:noexpandtab +# Integrators should override these +PREFIX ?= /usr/local +export PREFIX DESTDIR + # output targets BINS := \ poll \ @@ -57,7 +61,8 @@ timer: $(TIMER_OBJS) install: - false + mkdir -p $(DESTDIR)$(PREFIX)/bin + install -m755 $(BINS) $(DESTDIR)$(PREFIX)/bin clean: rm -f $(POLL_OBJS) diff --git a/tests/timer.c b/tests/timer.c index 39ff0a9..9e6e3ec 100644 --- a/tests/timer.c +++ b/tests/timer.c @@ -1,7 +1,7 @@ /** Pollcat test stub - timers * * This file is part of the Pollcat Library. - * Copyright (C) 2022 Expatria Technologies Inc. + * Copyright (C) 2022,2025 Expatria Technologies Inc. * Contact: Morgan Hughes * * The Pollcat Library is free software: you can redistribute it and/or modify it under @@ -34,12 +34,17 @@ #include +int opt_wrapper = 0; +int opt_stdin = 0; + + /** control for main loop for orderly shutdown */ static int main_loop = 1; static void signal_fatal (int signum) { fprintf(stderr, "Fatal signal %d, exit loop\n", signum); main_loop = 0; + pollcat_loop_exit(); } static void stop (const char *fmt, ...); @@ -55,8 +60,9 @@ static void callback (void *arg) extern char *__progname; static void usage (void) { - printf("Usage: %s -v [-t timeout]\n" - "-v Verbose (set all modules to debug, enable trace)\n" + printf("Usage: %s [-ws] [-t timeout]\n" + "-w Use pollcat_poll() wrapper\n" + "-s Add stdin file descriptor to pollcat\n" "-t timeout Timeout for poll() calls (ms)\n" "", __progname); @@ -65,16 +71,17 @@ static void usage (void) int main (int argc, char **argv) { - int ret = 0; - short revents; + struct termios tio_old; + int flags; + int ret = 0; // basic arguments parsing int opt; - while ( (opt = getopt(argc, argv, "vt:")) != -1 ) + while ( (opt = getopt(argc, argv, "wst:")) != -1 ) switch ( opt ) { - case 'v': - break; + case 'w': opt_wrapper = 1; break; + case 's': opt_stdin = 1; break; case 't': pollcat_time_base = atoi(optarg); @@ -84,73 +91,92 @@ int main (int argc, char **argv) usage(); } - pollcat_fd_add(fileno(stdin), POLLIN); + if ( opt_stdin ) + pollcat_fd_add(fileno(stdin), POLLIN); signal(SIGTERM, signal_fatal); signal(SIGINT, signal_fatal); - // reopen stdout/stderr before making stdin non-blocking - char tty[256]; - memset(tty, 0, sizeof(tty)); - if ( readlink("/proc/self/fd/1", tty, sizeof(tty)) > 0 ) + if ( opt_stdin ) { - if ( !freopen(tty, "w", stdout) || !freopen(tty, "w", stderr) ) - perror(tty); - setbuf(stderr, NULL); - } - - // set stdin nonblocking - int flags = fcntl(fileno(stdin), F_GETFL); - if ( flags < 0 ) - stop("fcntl(0, F_GETFL)"); - if ( fcntl(fileno(stdin), F_SETFL, flags | O_NONBLOCK) < 0 ) - stop("fcntl(0, F_SETFL)"); - - // save old terminal modes and make raw - struct termios tio_old; - if ( tcgetattr(fileno(stdin), &tio_old) ) - stop("tcgetattr(0, &tio_old)"); - - struct termios tio_new = tio_old; - tio_new.c_lflag &= ~(ECHO|ICANON); - if ( tcsetattr(fileno(stdin), TCSANOW, &tio_new) ) - stop("tcsetattr(0, TCSANOW, &tio_new)"); - - while ( main_loop ) - { - pollcat_timer_reduce_timeout(); - - if ( (ret = pollcat_poll()) < 0 ) + // reopen stdout/stderr before making stdin non-blocking + char tty[256]; + memset(tty, 0, sizeof(tty)); + if ( readlink("/proc/self/fd/1", tty, sizeof(tty)) > 0 ) { - perror("poll()"); - break; + if ( !freopen(tty, "w", stdout) || !freopen(tty, "w", stderr) ) + perror(tty); + setbuf(stderr, NULL); + } + + // set stdin nonblocking + if ( (flags = fcntl(fileno(stdin), F_GETFL)) < 0 ) + stop("fcntl(0, F_GETFL)"); + if ( fcntl(fileno(stdin), F_SETFL, flags | O_NONBLOCK) < 0 ) + stop("fcntl(0, F_SETFL)"); + + // save old terminal modes and make raw + if ( tcgetattr(fileno(stdin), &tio_old) ) + stop("tcgetattr(0, &tio_old)"); + + struct termios tio_new = tio_old; + tio_new.c_lflag &= ~(ECHO|ICANON); + if ( tcsetattr(fileno(stdin), TCSANOW, &tio_new) ) + stop("tcsetattr(0, TCSANOW, &tio_new)"); + } + else + pollcat_timer_attach(&timer, 1000, callback, NULL); + + if ( opt_wrapper ) + { + pollcat_dump(stderr); + while ( pollcat_loop(&ret) ) + pollcat_dump(stderr); + } + else + while ( main_loop ) + { + pollcat_timer_reduce_timeout(); + + pollcat_dump(stderr); + if ( (ret = pollcat_poll()) < 0 ) + { + perror("poll()"); + break; + } + pollcat_dump(stderr); + + pollcat_timer_dispatch(); + + if ( !ret ) + continue; + + if ( opt_stdin ) + { + short revents = pollcat_revents(fileno(stdin)); + assert(revents > -1); + if ( revents & POLLIN ) + { + char buff[16]; + + memset(buff, 0, sizeof(buff)); + ret = read(fileno(stdin), buff, sizeof(buff) - 1); + fprintf(stderr, "recv(%d): '%s'\n", ret, buff); + + pollcat_timer_attach(&timer, 333, callback, NULL); + } + } } - pollcat_timer_dispatch(); + if ( opt_stdin ) + { + if ( fcntl(0, F_SETFL, flags) < 0 ) + stop("fcntl(0, F_SETFL)"); - if ( !ret ) - continue; - - revents = pollcat_revents(fileno(stdin)); - assert(revents > -1); - if ( revents & POLLIN ) - { - char buff[16]; - - memset(buff, 0, sizeof(buff)); - ret = read(fileno(stdin), buff, sizeof(buff) - 1); - fprintf(stderr, "recv(%d): '%s'\n", ret, buff); - - pollcat_timer_attach(&timer, 333, callback, NULL); - } + if ( tcsetattr(0, TCSANOW, &tio_old) ) + stop("tcsetattr(0, TCSANOW, &tio_old)"); } - if ( fcntl(0, F_SETFL, flags) < 0 ) - stop("fcntl(0, F_SETFL)"); - - if ( tcsetattr(0, TCSANOW, &tio_old) ) - stop("tcsetattr(0, TCSANOW, &tio_old)"); - return 0; }