More minor bugfixes

- make pollcat_loop() work as expected with no structs linked, by
  - calling usleep() instead of poll()
  - refactoring delay calculation and reset into pollcat_time_value()
- simplify pollcat_timer_dispatch() in case of slow callbacks
  - deprecate pollcat_timer_dispatch_thresh
- revise tests/timer.c to support two new options:
  - -s to monitor stdin with pollcat_fd_add()
  - -w to use pollcat_loop() instead of handling main-loop separately
- make pollcat_dump() always present in header, since it's always compiled in
- add Makefile rules for installing tests, for easier running on cross-compiled targets
This commit is contained in:
2025-09-18 13:28:27 -07:00
parent c7729ca9b6
commit d0dd5f7a4a
8 changed files with 143 additions and 90 deletions

View File

@@ -1,7 +1,7 @@
/** Wrapper structure for poll() /** Wrapper structure for poll()
* *
* This file is part of the Pollcat Library. * This file is part of the Pollcat Library.
* Copyright (C) 2022 Expatria Technologies Inc. * Copyright (C) 2022,2025 Expatria Technologies Inc.
* Contact: Morgan Hughes <morgan@expatria.ca> * Contact: Morgan Hughes <morgan@expatria.ca>
* *
* The Pollcat Library is free software: you can redistribute it and/or modify it under * The Pollcat Library is free software: you can redistribute it and/or modify it under
@@ -40,9 +40,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <poll.h> #include <poll.h>
#if POLLCAT_DEBUG
#include <stdio.h> #include <stdio.h>
#endif
/** Optional function to call on fatal error; if unset an assert()-style message will be /** 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); int pollcat_poll (void);
#if POLLCAT_DEBUG /** Prints the pollfd array to a given file handle, if POLLCAT_DEBUG is nonzero
/** Prints the pollfd array to a given file handle
*/ */
void pollcat_dump (FILE *fp); void pollcat_dump (FILE *fp);
#endif
#endif /* _INCLUDE_POLLCAT_POLLCAT_H_ */ #endif /* _INCLUDE_POLLCAT_POLLCAT_H_ */

View File

@@ -28,8 +28,9 @@
typedef uint64_t pollcat_time_t; typedef uint64_t pollcat_time_t;
/** Grace period in milliseconds for late timers, set to 0 for exact match */ /** This was removed and replaced with a simpler implementation which allowed slow/late
extern pollcat_time_t pollcat_timer_dispatch_thresh; * 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 */ /** Maximum number of times to run the dispatch loop, set to -1 for no limit */

View File

@@ -1,7 +1,7 @@
/** Wrapper structure for poll() /** Wrapper structure for poll()
* *
* This file is part of the Pollcat Library. * This file is part of the Pollcat Library.
* Copyright (C) 2022 Expatria Technologies Inc. * Copyright (C) 2022,2025 Expatria Technologies Inc.
* Contact: Morgan Hughes <morgan@expatria.ca> * Contact: Morgan Hughes <morgan@expatria.ca>
* *
* The Pollcat Library is free software: you can redistribute it and/or modify it under * 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; 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 /** 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 */ /* Auto init, no-return if failed */
pollcat_init(); pollcat_init();
if ( pollcat_time_current < pollcat_time_minimum ) ret = poll(pollcat_list, pollcat_used, pollcat_time_value());
pollcat_time_current = pollcat_time_minimum;
ret = poll(pollcat_list, pollcat_used, pollcat_time_current);
pollcat_time_current = pollcat_time_base;
return ret; return ret;
} }

View File

@@ -1,7 +1,7 @@
/** Private includes /** Private includes
* *
* This file is part of the Pollcat Library. * This file is part of the Pollcat Library.
* Copyright (C) 2022 Expatria Technologies Inc. * Copyright (C) 2022,2025 Expatria Technologies Inc.
* Contact: Morgan Hughes <morgan@expatria.ca> * Contact: Morgan Hughes <morgan@expatria.ca>
* *
* The Pollcat Library is free software: you can redistribute it and/or modify it under * 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; 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_ */ #endif /* _SRC_PRIVATE_H_ */

View File

@@ -1,7 +1,7 @@
/** Registration struct, iterators, and main-loop convenience functions /** Registration struct, iterators, and main-loop convenience functions
* *
* This file is part of the Pollcat Library. * This file is part of the Pollcat Library.
* Copyright (C) 2022 Expatria Technologies Inc. * Copyright (C) 2022,2025 Expatria Technologies Inc.
* Contact: Morgan Hughes <morgan@expatria.ca> * Contact: Morgan Hughes <morgan@expatria.ca>
* *
* The Pollcat Library is free software: you can redistribute it and/or modify it under * The Pollcat Library is free software: you can redistribute it and/or modify it under
@@ -21,6 +21,7 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <pollcat.h> #include <pollcat.h>
#include "private.h" #include "private.h"
@@ -145,8 +146,18 @@ int pollcat_loop (int *poll_ret)
if ( walk->ops && walk->ops->prepare ) if ( walk->ops && walk->ops->prepare )
walk->ops->prepare(walk); walk->ops->prepare(walk);
/* call pollcat_poll() and optionally save the return value in poll-ret */ /* call pollcat_poll() if the iterator list is non-empty, otherwise sleep */
ret = pollcat_poll(); 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 ) if ( poll_ret )
*poll_ret = ret; *poll_ret = ret;

View File

@@ -1,7 +1,7 @@
/** Millisecond-resolution event timers /** Millisecond-resolution event timers
* *
* This file is part of the Pollcat Library. * This file is part of the Pollcat Library.
* Copyright (C) 2022 Expatria Technologies Inc. * Copyright (C) 2022,2025 Expatria Technologies Inc.
* Contact: Morgan Hughes <morgan@expatria.ca> * Contact: Morgan Hughes <morgan@expatria.ca>
* *
* The Pollcat Library is free software: you can redistribute it and/or modify it under * 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-- ) while ( timer_head && max-- )
{ {
pollcat_time_t due = pollcat_now(); if ( pollcat_now() < timer_head->due )
return;
due -= timer_head->due;
if ( due > pollcat_timer_dispatch_thresh )
return;
// detach the head // detach the head
struct pollcat_timer *t = timer_head; struct pollcat_timer *t = timer_head;

View File

@@ -1,7 +1,7 @@
# Test stubs Makefile # Test stubs Makefile
# #
# This file is part of the Pollcat Library. # This file is part of the Pollcat Library.
# Copyright (C) 2022 Expatria Technologies Inc. # Copyright (C) 2022,2025 Expatria Technologies Inc.
# Contact: Morgan Hughes <morgan@expatria.ca> # Contact: Morgan Hughes <morgan@expatria.ca>
# #
# The Pollcat Library is free software: you can redistribute it and/or modify it under the # The Pollcat Library is free software: you can redistribute it and/or modify it under the
@@ -18,6 +18,10 @@
# #
# vim:ts=4:noexpandtab # vim:ts=4:noexpandtab
# Integrators should override these
PREFIX ?= /usr/local
export PREFIX DESTDIR
# output targets # output targets
BINS := \ BINS := \
poll \ poll \
@@ -57,7 +61,8 @@ timer: $(TIMER_OBJS)
install: install:
false mkdir -p $(DESTDIR)$(PREFIX)/bin
install -m755 $(BINS) $(DESTDIR)$(PREFIX)/bin
clean: clean:
rm -f $(POLL_OBJS) rm -f $(POLL_OBJS)

View File

@@ -1,7 +1,7 @@
/** Pollcat test stub - timers /** Pollcat test stub - timers
* *
* This file is part of the Pollcat Library. * This file is part of the Pollcat Library.
* Copyright (C) 2022 Expatria Technologies Inc. * Copyright (C) 2022,2025 Expatria Technologies Inc.
* Contact: Morgan Hughes <morgan@expatria.ca> * Contact: Morgan Hughes <morgan@expatria.ca>
* *
* The Pollcat Library is free software: you can redistribute it and/or modify it under * The Pollcat Library is free software: you can redistribute it and/or modify it under
@@ -34,12 +34,17 @@
#include <pollcat.h> #include <pollcat.h>
int opt_wrapper = 0;
int opt_stdin = 0;
/** control for main loop for orderly shutdown */ /** control for main loop for orderly shutdown */
static int main_loop = 1; static int main_loop = 1;
static void signal_fatal (int signum) static void signal_fatal (int signum)
{ {
fprintf(stderr, "Fatal signal %d, exit loop\n", signum); fprintf(stderr, "Fatal signal %d, exit loop\n", signum);
main_loop = 0; main_loop = 0;
pollcat_loop_exit();
} }
static void stop (const char *fmt, ...); static void stop (const char *fmt, ...);
@@ -55,8 +60,9 @@ static void callback (void *arg)
extern char *__progname; extern char *__progname;
static void usage (void) static void usage (void)
{ {
printf("Usage: %s -v [-t timeout]\n" printf("Usage: %s [-ws] [-t timeout]\n"
"-v Verbose (set all modules to debug, enable trace)\n" "-w Use pollcat_poll() wrapper\n"
"-s Add stdin file descriptor to pollcat\n"
"-t timeout Timeout for poll() calls (ms)\n" "-t timeout Timeout for poll() calls (ms)\n"
"", "",
__progname); __progname);
@@ -65,16 +71,17 @@ static void usage (void)
int main (int argc, char **argv) int main (int argc, char **argv)
{ {
int ret = 0; struct termios tio_old;
short revents; int flags;
int ret = 0;
// basic arguments parsing // basic arguments parsing
int opt; int opt;
while ( (opt = getopt(argc, argv, "vt:")) != -1 ) while ( (opt = getopt(argc, argv, "wst:")) != -1 )
switch ( opt ) switch ( opt )
{ {
case 'v': case 'w': opt_wrapper = 1; break;
break; case 's': opt_stdin = 1; break;
case 't': case 't':
pollcat_time_base = atoi(optarg); pollcat_time_base = atoi(optarg);
@@ -84,72 +91,91 @@ int main (int argc, char **argv)
usage(); usage();
} }
pollcat_fd_add(fileno(stdin), POLLIN); if ( opt_stdin )
pollcat_fd_add(fileno(stdin), POLLIN);
signal(SIGTERM, signal_fatal); signal(SIGTERM, signal_fatal);
signal(SIGINT, signal_fatal); signal(SIGINT, signal_fatal);
// reopen stdout/stderr before making stdin non-blocking if ( opt_stdin )
char tty[256];
memset(tty, 0, sizeof(tty));
if ( readlink("/proc/self/fd/1", tty, sizeof(tty)) > 0 )
{ {
if ( !freopen(tty, "w", stdout) || !freopen(tty, "w", stderr) ) // reopen stdout/stderr before making stdin non-blocking
perror(tty); char tty[256];
setbuf(stderr, NULL); memset(tty, 0, sizeof(tty));
} if ( readlink("/proc/self/fd/1", tty, sizeof(tty)) > 0 )
// 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 )
{ {
perror("poll()"); if ( !freopen(tty, "w", stdout) || !freopen(tty, "w", stderr) )
break; perror(tty);
setbuf(stderr, NULL);
} }
pollcat_timer_dispatch(); // 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)");
if ( !ret ) // save old terminal modes and make raw
continue; if ( tcgetattr(fileno(stdin), &tio_old) )
stop("tcgetattr(0, &tio_old)");
revents = pollcat_revents(fileno(stdin)); struct termios tio_new = tio_old;
assert(revents > -1); tio_new.c_lflag &= ~(ECHO|ICANON);
if ( revents & POLLIN ) if ( tcsetattr(fileno(stdin), TCSANOW, &tio_new) )
{ stop("tcsetattr(0, TCSANOW, &tio_new)");
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);
}
} }
else
pollcat_timer_attach(&timer, 1000, callback, NULL);
if ( fcntl(0, F_SETFL, flags) < 0 ) if ( opt_wrapper )
stop("fcntl(0, F_SETFL)"); {
pollcat_dump(stderr);
while ( pollcat_loop(&ret) )
pollcat_dump(stderr);
}
else
while ( main_loop )
{
pollcat_timer_reduce_timeout();
if ( tcsetattr(0, TCSANOW, &tio_old) ) pollcat_dump(stderr);
stop("tcsetattr(0, TCSANOW, &tio_old)"); 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);
}
}
}
if ( opt_stdin )
{
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; return 0;
} }