From 5f1f5a002ce57c5622e551078f77296aa440c1ca Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 2 Aug 2023 05:57:30 +0930 Subject: [PATCH 01/16] base64: really fix. Playing games with chars is always a bad idea, but this time for sure! Signed-off-by: Rusty Russell --- ccan/base64/base64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ccan/base64/base64.c b/ccan/base64/base64.c index c28e0da2..89e0d38b 100644 --- a/ccan/base64/base64.c +++ b/ccan/base64/base64.c @@ -31,7 +31,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps, int8_t ret; ret = maps->decode_map[(unsigned char)b64letter]; - if (ret == '\xff') { + if (ret == (int8_t)'\xff') { errno = EDOM; return -1; } @@ -41,7 +41,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps, bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char) { - return (maps->decode_map[(const unsigned char)b64char] != '\xff'); + return (maps->decode_map[(const unsigned char)b64char] != (signed char)'\xff'); } void base64_init_maps(base64_maps_t *dest, const char src[64]) -- 2.39.5 From 4e192d2e06b8219d1c79c8ab63fac0bf1d44bee7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Mar 2024 09:03:11 +1030 Subject: [PATCH 02/16] rune: make error message better when integer parameter is missing. We say "is not an integer field" when a field is missing entirely, which is technically true but confusing. Say "not present" like every other condition does. Signed-off-by: Rusty Russell --- ccan/rune/rune.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ccan/rune/rune.c b/ccan/rune/rune.c index 84296c66..efbd8c49 100644 --- a/ccan/rune/rune.c +++ b/ccan/rune/rune.c @@ -345,6 +345,8 @@ static const char *rune_alt_single(const tal_t *ctx, memmem(fieldval_str, fieldval_strlen, alt->value, strlen(alt->value))); case RUNE_COND_INT_LESS: + if (!fieldval_str && !fieldval_int) + return tal_fmt(ctx, "%s not present", alt->fieldname); err = integer_compare_valid(ctx, fieldval_int, alt, &runeval_int); if (err) @@ -352,6 +354,8 @@ static const char *rune_alt_single(const tal_t *ctx, return cond_test(ctx, alt, "is greater or equal to", *fieldval_int < runeval_int); case RUNE_COND_INT_GREATER: + if (!fieldval_str && !fieldval_int) + return tal_fmt(ctx, "%s not present", alt->fieldname); err = integer_compare_valid(ctx, fieldval_int, alt, &runeval_int); if (err) -- 2.39.5 From 01bc8f9091e2bc2b285b568cf1f9d1347a5ec8de Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 22 Mar 2024 09:48:10 +1030 Subject: [PATCH 03/16] tal, tal/str: allow annotation with returns_nonnull if they opt in. This is true if you don't override the error callback, and helps with optimization (and warnings!). Signed-off-by: Rusty Russell --- ccan/tal/str/str.h | 18 +++++++++++------- ccan/tal/tal.h | 17 ++++++++++++++--- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/ccan/tal/str/str.h b/ccan/tal/str/str.h index f9394844..a2424cbe 100644 --- a/ccan/tal/str/str.h +++ b/ccan/tal/str/str.h @@ -17,7 +17,8 @@ * The returned string will have tal_count() == strlen() + 1. */ #define tal_strdup(ctx, p) tal_strdup_(ctx, p, TAL_LABEL(char, "[]")) -char *tal_strdup_(const tal_t *ctx, const char *p TAKES, const char *label); +char *tal_strdup_(const tal_t *ctx, const char *p TAKES, const char *label) + TAL_RETURN_PTR; /** * tal_strndup - duplicate a limited amount of a string. @@ -30,7 +31,8 @@ char *tal_strdup_(const tal_t *ctx, const char *p TAKES, const char *label); */ #define tal_strndup(ctx, p, n) tal_strndup_(ctx, p, n, TAL_LABEL(char, "[]")) char *tal_strndup_(const tal_t *ctx, const char *p TAKES, size_t n, - const char *label); + const char *label) + TAL_RETURN_PTR; /** * tal_fmt - allocate a formatted string @@ -42,7 +44,7 @@ char *tal_strndup_(const tal_t *ctx, const char *p TAKES, size_t n, #define tal_fmt(ctx, ...) \ tal_fmt_(ctx, TAL_LABEL(char, "[]"), __VA_ARGS__) char *tal_fmt_(const tal_t *ctx, const char *label, const char *fmt TAKES, - ...) PRINTF_FMT(3,4); + ...) PRINTF_FMT(3,4) TAL_RETURN_PTR; /** * tal_vfmt - allocate a formatted string (va_list version) @@ -56,7 +58,7 @@ char *tal_fmt_(const tal_t *ctx, const char *label, const char *fmt TAKES, tal_vfmt_(ctx, fmt, va, TAL_LABEL(char, "[]")) char *tal_vfmt_(const tal_t *ctx, const char *fmt TAKES, va_list ap, const char *label) - PRINTF_FMT(2,0); + PRINTF_FMT(2,0) TAL_RETURN_PTR; /** * tal_append_fmt - append a formatted string to a talloc string. @@ -89,7 +91,7 @@ bool tal_append_vfmt(char **baseptr, const char *fmt TAKES, va_list ap); */ #define tal_strcat(ctx, s1, s2) tal_strcat_(ctx, s1, s2, TAL_LABEL(char, "[]")) char *tal_strcat_(const tal_t *ctx, const char *s1 TAKES, const char *s2 TAKES, - const char *label); + const char *label) TAL_RETURN_PTR; enum strsplit { STR_EMPTY_OK, @@ -137,7 +139,8 @@ char **tal_strsplit_(const tal_t *ctx, const char *string TAKES, const char *delims TAKES, enum strsplit flag, - const char *label); + const char *label) + TAL_RETURN_PTR; enum strjoin { STR_TRAIL, @@ -175,7 +178,8 @@ char *tal_strjoin_(const void *ctx, char *strings[] TAKES, const char *delim TAKES, enum strjoin flags, - const char *label); + const char *label) + TAL_RETURN_PTR; /** * tal_strreg - match/extract from a string via (extended) regular expressions. diff --git a/ccan/tal/tal.h b/ccan/tal/tal.h index c486f9e8..347a5e8c 100644 --- a/ccan/tal/tal.h +++ b/ccan/tal/tal.h @@ -11,6 +11,14 @@ #include #include +/* Define this for better optimization if you never override errfn + * to something tat returns */ +#ifdef CCAN_TAL_NEVER_RETURN_NULL +#define TAL_RETURN_PTR RETURNS_NONNULL +#else +#define TAL_RETURN_PTR +#endif /* CCAN_TAL_NEVER_RETURN_NULL */ + /** * tal_t - convenient alias for void to mark tal pointers. * @@ -417,7 +425,8 @@ tal_t *tal_parent(const tal_t *ctx); * @error_fn: called on errors or NULL (default is abort) * * The defaults are set up so tal functions never return NULL, but you - * can override erorr_fn to change that. error_fn can return, and is + * can override error_fn to change that. error_fn can return (only if + * you haven't defined CCAN_TAL_NEVER_RETURN_NULL!), and is * called if alloc_fn or resize_fn fail. * * If any parameter is NULL, that function is unchanged. @@ -521,9 +530,11 @@ bool tal_set_name_(tal_t *ctx, const char *name, bool literal); #define tal_typechk_(ptr, ptype) (ptr) #endif -void *tal_alloc_(const tal_t *ctx, size_t bytes, bool clear, const char *label); +void *tal_alloc_(const tal_t *ctx, size_t bytes, bool clear, const char *label) + TAL_RETURN_PTR; void *tal_alloc_arr_(const tal_t *ctx, size_t bytes, size_t count, bool clear, - const char *label); + const char *label) + TAL_RETURN_PTR; void *tal_dup_(const tal_t *ctx, const void *p TAKES, size_t size, size_t n, size_t extra, bool nullok, const char *label); -- 2.39.5 From 9524c7eb8e54697da5a366c8ad3405f75c3f4195 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 4 Apr 2024 14:04:09 +1030 Subject: [PATCH 04/16] tal: fix clang complaining we return NULL if we promised not to. Since really, the user is promising their alloc fn won't return NULL. Signed-off-by: Rusty Russell --- ccan/tal/str/str.c | 45 ++++++++-------------------------------- ccan/tal/str/str.h | 51 ++++++++++++++++++++++++---------------------- ccan/tal/tal.c | 17 +++++++++++++--- 3 files changed, 49 insertions(+), 64 deletions(-) diff --git a/ccan/tal/str/str.c b/ccan/tal/str/str.c index cec31c75..617b942c 100644 --- a/ccan/tal/str/str.c +++ b/ccan/tal/str/str.c @@ -14,21 +14,14 @@ char *tal_strdup_(const tal_t *ctx, const char *p, const char *label) { - /* We have to let through NULL for take(). */ - return tal_dup_arr_label(ctx, char, p, p ? strlen(p) + 1: 1, 0, label); + return tal_dup_arr_label(ctx, char, p, strlen(p) + 1, 0, label); } char *tal_strndup_(const tal_t *ctx, const char *p, size_t n, const char *label) { - size_t len; + size_t len = strnlen(p, n); char *ret; - /* We have to let through NULL for take(). */ - if (likely(p)) - len = strnlen(p, n); - else - len = n; - ret = tal_dup_arr_label(ctx, char, p, len, 1, label); if (ret) ret[len] = '\0'; @@ -84,9 +77,6 @@ char *tal_vfmt_(const tal_t *ctx, const char *fmt, va_list ap, const char *label { char *buf; - if (!fmt && taken(fmt)) - return NULL; - /* A decent guess to start. */ buf = tal_arr_label(ctx, char, strlen(fmt) * 2, label); if (!do_vfmt(&buf, 0, fmt, ap)) @@ -96,9 +86,6 @@ char *tal_vfmt_(const tal_t *ctx, const char *fmt, va_list ap, const char *label bool tal_append_vfmt(char **baseptr, const char *fmt, va_list ap) { - if (!fmt && taken(fmt)) - return false; - return do_vfmt(baseptr, strlen(*baseptr), fmt, ap); } @@ -120,13 +107,7 @@ char *tal_strcat_(const tal_t *ctx, const char *s1, const char *s2, size_t len1, len2; char *ret; - if (unlikely(!s2) && taken(s2)) { - if (taken(s1)) - tal_free(s1); - return NULL; - } - /* We have to let through NULL for take(). */ - len1 = s1 ? strlen(s1) : 0; + len1 = strlen(s1); len2 = strlen(s2); ret = tal_dup_arr_label(ctx, char, s1, len1, len2 + 1, label); @@ -151,13 +132,11 @@ char **tal_strsplit_(const tal_t *ctx, tal_free(string); if (taken(delims)) tal_free(delims); - return NULL; + return parts; } str = tal_strdup(parts, string); if (unlikely(!str)) goto fail; - if (unlikely(!delims) && is_taken(delims)) - goto fail; if (flags == STR_NO_EMPTY) str += strspn(str, delims); @@ -185,10 +164,14 @@ char **tal_strsplit_(const tal_t *ctx, return parts; fail: +#ifdef CCAN_TAL_NEVER_RETURN_NULL + abort(); +#else tal_free(parts); if (taken(delims)) tal_free(delims); return NULL; +#endif } char *tal_strjoin_(const tal_t *ctx, @@ -199,12 +182,6 @@ char *tal_strjoin_(const tal_t *ctx, char *ret = NULL; size_t totlen = 0, dlen; - if (unlikely(!strings) && is_taken(strings)) - goto fail; - - if (unlikely(!delim) && is_taken(delim)) - goto fail; - dlen = strlen(delim); ret = tal_arr_label(ctx, char, dlen*2+1, label); if (!ret) @@ -269,15 +246,9 @@ bool tal_strreg_(const tal_t *ctx, const char *string, const char *label, unsigned int i; va_list ap; - if (unlikely(!regex) && is_taken(regex)) - goto fail_no_re; - if (regcomp(&r, regex, REG_EXTENDED) != 0) goto fail_no_re; - if (unlikely(!string) && is_taken(string)) - goto fail; - if (regexec(&r, string, nmatch, matches, 0) != 0) goto fail; diff --git a/ccan/tal/str/str.h b/ccan/tal/str/str.h index a2424cbe..12b16478 100644 --- a/ccan/tal/str/str.h +++ b/ccan/tal/str/str.h @@ -12,18 +12,18 @@ /** * tal_strdup - duplicate a string * @ctx: NULL, or tal allocated object to be parent. - * @p: the string to copy (can be take()). + * @p: the string to copy (can be take(), must not be NULL). * * The returned string will have tal_count() == strlen() + 1. */ #define tal_strdup(ctx, p) tal_strdup_(ctx, p, TAL_LABEL(char, "[]")) char *tal_strdup_(const tal_t *ctx, const char *p TAKES, const char *label) - TAL_RETURN_PTR; + TAL_RETURN_PTR NON_NULL_ARGS(2); /** * tal_strndup - duplicate a limited amount of a string. * @ctx: NULL, or tal allocated object to be parent. - * @p: the string to copy (can be take()). + * @p: the string to copy (can be take(), must not be NULL). * @n: the maximum length to copy. * * Always gives a nul-terminated string, with strlen() <= @n. @@ -32,24 +32,24 @@ char *tal_strdup_(const tal_t *ctx, const char *p TAKES, const char *label) #define tal_strndup(ctx, p, n) tal_strndup_(ctx, p, n, TAL_LABEL(char, "[]")) char *tal_strndup_(const tal_t *ctx, const char *p TAKES, size_t n, const char *label) - TAL_RETURN_PTR; + TAL_RETURN_PTR NON_NULL_ARGS(2); /** * tal_fmt - allocate a formatted string * @ctx: NULL, or tal allocated object to be parent. - * @fmt: the printf-style format (can be take()). + * @fmt: the printf-style format (can be take(), must not be NULL). * * The returned string will have tal_count() == strlen() + 1. */ #define tal_fmt(ctx, ...) \ tal_fmt_(ctx, TAL_LABEL(char, "[]"), __VA_ARGS__) char *tal_fmt_(const tal_t *ctx, const char *label, const char *fmt TAKES, - ...) PRINTF_FMT(3,4) TAL_RETURN_PTR; + ...) PRINTF_FMT(3,4) TAL_RETURN_PTR NON_NULL_ARGS(3); /** * tal_vfmt - allocate a formatted string (va_list version) * @ctx: NULL, or tal allocated object to be parent. - * @fmt: the printf-style format (can be take()). + * @fmt: the printf-style format (can be take(), must not be NULL). * @va: the va_list containing the format args. * * The returned string will have tal_count() == strlen() + 1. @@ -58,40 +58,42 @@ char *tal_fmt_(const tal_t *ctx, const char *label, const char *fmt TAKES, tal_vfmt_(ctx, fmt, va, TAL_LABEL(char, "[]")) char *tal_vfmt_(const tal_t *ctx, const char *fmt TAKES, va_list ap, const char *label) - PRINTF_FMT(2,0) TAL_RETURN_PTR; + PRINTF_FMT(2,0) TAL_RETURN_PTR NON_NULL_ARGS(2); /** * tal_append_fmt - append a formatted string to a talloc string. * @baseptr: a pointer to the tal string to be appended to. - * @fmt: the printf-style format (can be take()). + * @fmt: the printf-style format (can be take(), must not be NULL). * * Returns false on allocation failure. * Otherwise tal_count(*@baseptr) == strlen(*@baseptr) + 1. */ -bool tal_append_fmt(char **baseptr, const char *fmt TAKES, ...) PRINTF_FMT(2,3); +bool tal_append_fmt(char **baseptr, const char *fmt TAKES, ...) + PRINTF_FMT(2,3) NON_NULL_ARGS(2); /** * tal_append_vfmt - append a formatted string to a talloc string (va_list) * @baseptr: a pointer to the tal string to be appended to. - * @fmt: the printf-style format (can be take()). + * @fmt: the printf-style format (can be take(), must not be NULL). * @va: the va_list containing the format args. * * Returns false on allocation failure. * Otherwise tal_count(*@baseptr) == strlen(*@baseptr) + 1. */ -bool tal_append_vfmt(char **baseptr, const char *fmt TAKES, va_list ap); +bool tal_append_vfmt(char **baseptr, const char *fmt TAKES, va_list ap) + NON_NULL_ARGS(2); /** * tal_strcat - join two strings together * @ctx: NULL, or tal allocated object to be parent. - * @s1: the first string (can be take()). - * @s2: the second string (can be take()). + * @s1: the first string (can be take(), must not be NULL). + * @s2: the second string (can be take(), must not be NULL). * * The returned string will have tal_count() == strlen() + 1. */ #define tal_strcat(ctx, s1, s2) tal_strcat_(ctx, s1, s2, TAL_LABEL(char, "[]")) char *tal_strcat_(const tal_t *ctx, const char *s1 TAKES, const char *s2 TAKES, - const char *label) TAL_RETURN_PTR; + const char *label) TAL_RETURN_PTR NON_NULL_ARGS(2,3); enum strsplit { STR_EMPTY_OK, @@ -101,8 +103,8 @@ enum strsplit { /** * tal_strsplit - Split string into an array of substrings * @ctx: the context to tal from (often NULL). - * @string: the string to split (can be take()). - * @delims: delimiters where lines should be split (can be take()). + * @string: the string to split (can be take(), must not be NULL). + * @delims: delimiters where lines should be split (can be take(), must not be NULL). * @flags: whether to include empty substrings. * * This function splits a single string into multiple strings. @@ -140,7 +142,7 @@ char **tal_strsplit_(const tal_t *ctx, const char *delims TAKES, enum strsplit flag, const char *label) - TAL_RETURN_PTR; + TAL_RETURN_PTR NON_NULL_ARGS(2,3); enum strjoin { STR_TRAIL, @@ -150,8 +152,8 @@ enum strjoin { /** * tal_strjoin - Join an array of substrings into one long string * @ctx: the context to tal from (often NULL). - * @strings: the NULL-terminated array of strings to join (can be take()) - * @delim: the delimiter to insert between the strings (can be take()) + * @strings: the NULL-terminated array of strings to join (can be take(), must not be NULL) + * @delim: the delimiter to insert between the strings (can be take(), must not be NULL) * @flags: whether to add a delimieter to the end * * This function joins an array of strings into a single string. The @@ -179,13 +181,13 @@ char *tal_strjoin_(const void *ctx, const char *delim TAKES, enum strjoin flags, const char *label) - TAL_RETURN_PTR; + TAL_RETURN_PTR NON_NULL_ARGS(2,3); /** * tal_strreg - match/extract from a string via (extended) regular expressions. * @ctx: the context to tal from (often NULL) - * @string: the string to try to match (can be take()) - * @regex: the regular expression to match (can be take()) + * @string: the string to try to match (can be take(), must not be NULL) + * @regex: the regular expression to match (can be take(), must not be NULL) * ...: pointers to strings to allocate for subexpressions. * * Returns true if we matched, in which case any parenthesized @@ -225,5 +227,6 @@ char *tal_strjoin_(const void *ctx, #define tal_strreg(ctx, string, ...) \ tal_strreg_(ctx, string, TAL_LABEL(char, "[]"), __VA_ARGS__) bool tal_strreg_(const void *ctx, const char *string TAKES, - const char *label, const char *regex, ...); + const char *label, const char *regex TAKES, ...) + NON_NULL_ARGS(2,4); #endif /* CCAN_STR_TAL_H */ diff --git a/ccan/tal/tal.c b/ccan/tal/tal.c index 1230d8ca..e799059f 100644 --- a/ccan/tal/tal.c +++ b/ccan/tal/tal.c @@ -456,13 +456,24 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno) freefn(t); } +/* Don't have compiler complain we're returning NULL if we promised not to! */ +static void *null_alloc_failed(void) +{ +#ifdef CCAN_TAL_NEVER_RETURN_NULL + abort(); +#else + return NULL; +#endif /* CCAN_TAL_NEVER_RETURN_NULL */ +} + void *tal_alloc_(const tal_t *ctx, size_t size, bool clear, const char *label) { struct tal_hdr *child, *parent = debug_tal(to_tal_hdr_or_null(ctx)); child = allocate(sizeof(struct tal_hdr) + size); if (!child) - return NULL; + return null_alloc_failed(); + if (clear) memset(from_tal_hdr(child), 0, size); child->prop = (void *)label; @@ -470,7 +481,7 @@ void *tal_alloc_(const tal_t *ctx, size_t size, bool clear, const char *label) if (!add_child(parent, child)) { freefn(child); - return NULL; + return null_alloc_failed(); } debug_tal(parent); if (notifiers) @@ -501,7 +512,7 @@ void *tal_alloc_arr_(const tal_t *ctx, size_t size, size_t count, bool clear, const char *label) { if (!adjust_size(&size, count)) - return NULL; + return null_alloc_failed(); return tal_alloc_(ctx, size, clear, label); } -- 2.39.5 From a657724f442899ec1ce16c8cfe9e63e8e9d861ff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Apr 2024 13:25:44 +0930 Subject: [PATCH 05/16] ccan/io: extended errors API so caller can be told if we fail accept() Signed-off-by: Rusty Russell --- ccan/io/io.c | 13 ++++++++++++- ccan/io/io.h | 11 +++++++++++ ccan/io/poll.c | 8 +++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ccan/io/io.c b/ccan/io/io.c index 12df06d8..3d00de04 100644 --- a/ccan/io/io.c +++ b/ccan/io/io.c @@ -15,6 +15,7 @@ void *io_loop_return; struct io_plan io_conn_freed; +static bool io_extended_errors; struct io_listener *io_new_listener_(const tal_t *ctx, int fd, struct io_plan *(*init)(struct io_conn *, @@ -540,7 +541,7 @@ struct io_plan *io_sock_shutdown(struct io_conn *conn) /* And leave unset .*/ return &conn->plan[IO_IN]; } - + bool io_flush_sync(struct io_conn *conn) { struct io_plan *plan = &conn->plan[IO_OUT]; @@ -576,3 +577,13 @@ again: io_fd_block(io_conn_fd(conn), false); return ok; } + +void io_set_extended_errors(bool state) +{ + io_extended_errors = state; +} + +bool io_get_extended_errors(void) +{ + return io_extended_errors; +} diff --git a/ccan/io/io.h b/ccan/io/io.h index eeb5e36e..cc92be2c 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -113,6 +113,8 @@ void io_set_finish_(struct io_conn *conn, * (tal'ocated off @ctx) and pass that to init(). Note that if there is * an error on this file descriptor, it will be freed. * + * Note: if the accept fails (usually due to EMFILE), init() will be called + * wth * Returns NULL on error (and sets errno). * * Example: @@ -823,4 +825,13 @@ int (*io_poll_override(int (*poll)(struct pollfd *fds, nfds_t nfds, int timeout) */ const void *io_have_fd(int fd, bool *listener); +/** + * io_set_extended_errors - enable callbacks for errors. + * @state: true or false. + * + * Defaults false for compatibility. See io_new_conn for what this changes. + */ +void io_set_extended_errors(bool state); +bool io_get_extended_errors(void); + #endif /* CCAN_IO_H */ diff --git a/ccan/io/poll.c b/ccan/io/poll.c index 634f83d2..7fe9e2c5 100644 --- a/ccan/io/poll.c +++ b/ccan/io/poll.c @@ -270,10 +270,12 @@ static void accept_conn(struct io_listener *l) { int fd = accept(l->fd.fd, NULL, NULL); - /* FIXME: What to do here? */ - if (fd < 0) + if (fd < 0) { + /* If they've enabled it, this is how we tell them */ + if (io_get_extended_errors()) + l->init(NULL, l->arg); return; - + } io_new_conn(l->ctx, fd, l->init, l->arg); } -- 2.39.5 From a36430c8db83efa088b229a7b48e4248e78e187f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Apr 2024 13:26:27 +0930 Subject: [PATCH 06/16] ccan/io/fdpass: tell caller that fd recv failed if they enabled extended errors Signed-off-by: Rusty Russell --- ccan/io/fdpass/fdpass.c | 4 +++- ccan/io/fdpass/fdpass.h | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ccan/io/fdpass/fdpass.c b/ccan/io/fdpass/fdpass.c index 63c46223..e002ddc1 100644 --- a/ccan/io/fdpass/fdpass.c +++ b/ccan/io/fdpass/fdpass.c @@ -39,7 +39,9 @@ static int do_fd_recv(int fd, struct io_plan_arg *arg) /* In case ccan/io ever gets smart with non-blocking. */ if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; - return -1; + /* If they can't handle the error, this will close conn! */ + if (!io_get_extended_errors()) + return -1; } *(int *)arg->u1.vp = fdin; return 1; diff --git a/ccan/io/fdpass/fdpass.h b/ccan/io/fdpass/fdpass.h index 533bc840..9cdfa2c0 100644 --- a/ccan/io/fdpass/fdpass.h +++ b/ccan/io/fdpass/fdpass.h @@ -45,8 +45,9 @@ struct io_plan *io_send_fd_(struct io_conn *conn, * @arg: @next argument * * This creates a plan to receive a file descriptor, as sent by - * io_send_fd. Once it's all read, the @next function will be called: - * on an error, the finish function is called instead. + * io_send_fd. Once it's all read, the @next function will be called. + * On an error, if io_get_extended_errors() is true, then @next is called + * and @fd will be -1, otherwise the finish function is called. * * Note that the I/O may actually be done immediately. * -- 2.39.5 From c5c87890f3441856fff4b50861c52e85ee2f41f4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 May 2024 15:01:03 +0930 Subject: [PATCH 07/16] io: clean up compiler warning. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit ``` /home/rusty/devel/cvs/ccan/ccan/io/test/run-08-read-after-hangup.c: In function ‘main’: /home/rusty/devel/cvs/ccan/ccan/io/io.h:62:58: warning: declaration of ‘conn’ shadows a previous local [-Wshadow=compatible-local] 62 | struct io_conn *conn), \ | ~~~~~~~~~~~~~~~~^~~~ /home/rusty/devel/cvs/ccan/ccan/typesafe_cb/typesafe_cb.h:35:46: note: in definition of macro ‘typesafe_cb_cast’ 35 | oktype), \ | ^~~~~~ /home/rusty/devel/cvs/ccan/ccan/io/io.h:60:22: note: in expansion of macro ‘typesafe_cb_preargs’ 60 | typesafe_cb_preargs(struct io_plan *, void *, \ | ^~~~~~~~~~~~~~~~~~~ /home/rusty/devel/cvs/ccan/ccan/io/test/run-08-read-after-hangup.c:42:9: note: in expansion of macro ‘io_new_conn’ 42 | io_new_conn(conn, fds[1], init_writer, conn); | ^~~~~~~~~~~ /home/rusty/devel/cvs/ccan/ccan/io/test/run-08-read-after-hangup.c:36:25: note: shadowed declaration is here 36 | struct io_conn *conn; | ^~~~ ``` --- ccan/io/io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccan/io/io.h b/ccan/io/io.h index cc92be2c..5d084828 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -59,7 +59,7 @@ struct io_conn; io_new_conn_((ctx), (fd), \ typesafe_cb_preargs(struct io_plan *, void *, \ (init), (arg), \ - struct io_conn *conn), \ + struct io_conn *), \ (void *)(arg)) struct io_conn *io_new_conn_(const tal_t *ctx, int fd, -- 2.39.5 From 208e988650a6b1ddaefb0ea7781ffed831698ebc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 24 Jun 2024 12:15:02 +0930 Subject: [PATCH 08/16] io: add benchmark for large poll. This simulates a performance issue reported in `connectd` for lightningd with 900 fds: it's consuming 70% of a CPU, and strace shows it getting a single fd (or two) from poll, doing a write (gossip store streaming), then going back into poll. I tested using 1000 x netcat from a machine on the same LAN: ``` remote$ for i in `seq 1000`; do sleep 0.1; nc 10.88.9.17 8888 /dev/null & done ``` ``` local$ ./run-stream-many 8888 980 20 100 rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwStarting! Finished: 122852940usec ``` Perf record shows 93.5% of time spent in poll. strace (separate run) shows us servicing all 20 fds at once, because LAN is too fast. Signed-off-by: Rusty Russell --- ccan/io/benchmarks/Makefile | 11 ++- ccan/io/benchmarks/run-stream-many.c | 132 +++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 ccan/io/benchmarks/run-stream-many.c diff --git a/ccan/io/benchmarks/Makefile b/ccan/io/benchmarks/Makefile index 7dcf9beb..0bc0ab46 100644 --- a/ccan/io/benchmarks/Makefile +++ b/ccan/io/benchmarks/Makefile @@ -1,16 +1,17 @@ -ALL:=run-loop run-different-speed run-length-prefix +ALL:=run-loop run-different-speed run-length-prefix run-stream-many CCANDIR:=../../.. CFLAGS:=-Wall -I$(CCANDIR) -O3 -flto LDFLAGS:=-O3 -flto LDLIBS:=-lrt -OBJS:=time.o poll.o io.o err.o timer.o list.o +OBJS:=time.o poll.o io.o err.o timer.o list.o ccan-tal.o ccan-take.o ccan-ilog.o default: $(ALL) run-loop: run-loop.o $(OBJS) run-different-speed: run-different-speed.o $(OBJS) run-length-prefix: run-length-prefix.o $(OBJS) +run-stream-many: run-stream-many.o $(OBJS) time.o: $(CCANDIR)/ccan/time/time.c $(CC) $(CFLAGS) -c -o $@ $< @@ -24,6 +25,12 @@ io.o: $(CCANDIR)/ccan/io/io.c $(CC) $(CFLAGS) -c -o $@ $< err.o: $(CCANDIR)/ccan/err/err.c $(CC) $(CFLAGS) -c -o $@ $< +ccan-ilog.o: $(CCANDIR)/ccan/ilog/ilog.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-take.o: $(CCANDIR)/ccan/take/take.c + $(CC) $(CFLAGS) -c -o $@ $< clean: rm -f *.o $(ALL) diff --git a/ccan/io/benchmarks/run-stream-many.c b/ccan/io/benchmarks/run-stream-many.c new file mode 100644 index 00000000..d5dc4166 --- /dev/null +++ b/ccan/io/benchmarks/run-stream-many.c @@ -0,0 +1,132 @@ +/* Wait for many fds to connect, then try to stream the file to some of them in small chunks. + * + * This approximates the connectd behaviour in CLN, where we send gossip to peers. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We expect num_expected connections, and how many will be writers */ +static size_t max_readers, max_writers; + +/* How many raeaders and writers still going */ +static size_t num_readers, num_writers; + +/* How many times to do the write */ +static size_t write_iterations; + +/* The buffer to write */ +static char writebuf[256]; + +/* We need this for readers, though we don't actually care! */ +static size_t len_ignored; + +struct timemono start_time; + +static void finished(void) +{ + struct timerel elapsed = timemono_since(start_time); + printf("Finished: %"PRIu64"usec\n", time_to_usec(elapsed)); + exit(0); +} + +static struct io_plan *write_loop(struct io_conn *conn, ptrint_t *iter) +{ + ptrdiff_t n = ptr2int(iter); + + if (n > write_iterations) { + --num_writers; + if (num_writers == 0) + finished(); + return io_wait(conn, conn, io_never, NULL); + } + return io_write(conn, writebuf, sizeof(writebuf), write_loop, int2ptr(n + 1)); +} + +static struct io_plan *read_loop(struct io_conn *conn, void *unused) +{ + return io_read_partial(conn, writebuf, sizeof(writebuf), &len_ignored, read_loop, unused); +} + +static void reader_failed(struct io_conn *conn, intptr_t *num) +{ + err(1, "Reader %zu/%zu", (size_t)ptr2int(num), max_readers); +} + +static void writer_failed(struct io_conn *conn, intptr_t *num) +{ + err(1, "Writer %zu/%zu", (size_t)ptr2int(num), max_writers); +} + +static struct io_plan *connection_in(struct io_conn *conn, void *sleep_on) +{ + if (num_readers < max_readers) { + printf("r"); + fflush(stdout); + num_readers++; + io_set_finish(conn, reader_failed, int2ptr(num_readers)); + return read_loop(conn, NULL); + } + + /* We assign writers last: not sure it matters, but it's more reflective + * of lightning where more recent connections tend to ask for gossip */ + num_writers++; + printf("w"); + fflush(stdout); + + io_set_finish(conn, writer_failed, int2ptr(num_writers)); + io_set_finish(conn, writer_failed, NULL); + if (num_writers < max_writers) + return io_wait(conn, sleep_on, write_loop, int2ptr(0)); + + /* Everyone is connected. Wake them and start final one */ + io_wake(sleep_on); + printf("Starting!\n"); + start_time = time_mono(); + return write_loop(conn, int2ptr(0)); +} + +int main(int argc, char *argv[]) +{ + int fd; + struct sockaddr_in s4; + int on = 1; + + if (argc != 5) + errx(1, "Usage: "); + + memset(&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_port = htons(atol(argv[1])); + s4.sin_addr.s_addr = INADDR_ANY; + + max_readers = atol(argv[2]); + max_writers = atol(argv[3]); + write_iterations = atol(argv[4]) * (1024 * 1024 / sizeof(writebuf)); + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + err(1, "Creating socket"); + + /* Re-use, please.. */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + err(1, "Setting reuseaddr"); + + if (bind(fd, &s4, sizeof(s4)) != 0) + err(1, "Binding"); + + if (listen(fd, 1) != 0) + err(1, "Listening"); + + io_new_listener(NULL, fd, connection_in, &s4); + io_loop(NULL, NULL); + errx(1, "Sockets exited?"); +} -- 2.39.5 From d493282092b509fac8e03208744d9d196992e29a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 24 Jun 2024 12:15:03 +0930 Subject: [PATCH 09/16] ccan: call io routines repeatedly until EAGAIN. Benchmark time halved. ``` Finished: 68697129usec ``` perf shows 75% of time in libc_write. Signed-off-by: Rusty Russell --- ccan/io/io.c | 77 +++++++++++++++++++----- ccan/io/test/run-43-io_plan_in_started.c | 10 ++- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/ccan/io/io.c b/ccan/io/io.c index 3d00de04..58ae19bd 100644 --- a/ccan/io/io.c +++ b/ccan/io/io.c @@ -384,9 +384,19 @@ void io_wake(const void *wait) backend_wake(wait); } -/* Returns false if this should not be touched (eg. freed). */ -static bool do_plan(struct io_conn *conn, struct io_plan *plan, - bool idle_on_epipe) +enum plan_result { + /* Destroyed, do not touch */ + FREED, + /* Worked, call again. */ + KEEP_GOING, + /* Failed with EAGAIN or did partial. */ + EXHAUSTED, + /* No longer interested in read (or write) */ + UNINTERESTED +}; + +static enum plan_result do_plan(struct io_conn *conn, struct io_plan *plan, + bool idle_on_epipe) { /* We shouldn't have polled for this event if this wasn't true! */ assert(plan->status == IO_POLLING_NOTSTARTED @@ -394,18 +404,26 @@ static bool do_plan(struct io_conn *conn, struct io_plan *plan, switch (plan->io(conn->fd.fd, &plan->arg)) { case -1: + /* This is expected, as we call optimistically! */ + if (errno == EAGAIN) + return EXHAUSTED; if (errno == EPIPE && idle_on_epipe) { plan->status = IO_UNSET; backend_new_plan(conn); - return false; + return UNINTERESTED; } io_close(conn); - return false; + return FREED; case 0: plan->status = IO_POLLING_STARTED; - return true; + /* If it started but didn't finish, don't call again. */ + return EXHAUSTED; case 1: - return next_plan(conn, plan); + if (!next_plan(conn, plan)) + return FREED; + if (plan->status == IO_POLLING_NOTSTARTED) + return KEEP_GOING; + return UNINTERESTED; default: /* IO should only return -1, 0 or 1 */ abort(); @@ -414,16 +432,43 @@ static bool do_plan(struct io_conn *conn, struct io_plan *plan, void io_ready(struct io_conn *conn, int pollflags) { - if (pollflags & POLLIN) - if (!do_plan(conn, &conn->plan[IO_IN], false)) - return; + enum plan_result res; + + if (pollflags & POLLIN) { + for (;;) { + res = do_plan(conn, &conn->plan[IO_IN], false); + switch (res) { + case FREED: + return; + case EXHAUSTED: + case UNINTERESTED: + goto try_write; + case KEEP_GOING: + continue; + } + abort(); + } + } - if (pollflags & POLLOUT) - /* If we're writing to a closed pipe, we need to wait for - * read to fail if we're duplex: we want to drain it! */ - do_plan(conn, &conn->plan[IO_OUT], - conn->plan[IO_IN].status == IO_POLLING_NOTSTARTED - || conn->plan[IO_IN].status == IO_POLLING_STARTED); +try_write: + if (pollflags & POLLOUT) { + for (;;) { + /* If we're writing to a closed pipe, we need to wait for + * read to fail if we're duplex: we want to drain it! */ + res = do_plan(conn, &conn->plan[IO_OUT], + conn->plan[IO_IN].status == IO_POLLING_NOTSTARTED + || conn->plan[IO_IN].status == IO_POLLING_STARTED); + switch (res) { + case FREED: + case EXHAUSTED: + case UNINTERESTED: + return; + case KEEP_GOING: + continue; + } + abort(); + } + } } void io_do_always(struct io_plan *plan) diff --git a/ccan/io/test/run-43-io_plan_in_started.c b/ccan/io/test/run-43-io_plan_in_started.c index f63f8779..74fcf55c 100644 --- a/ccan/io/test/run-43-io_plan_in_started.c +++ b/ccan/io/test/run-43-io_plan_in_started.c @@ -18,9 +18,17 @@ static struct io_plan *init_in_conn(struct io_conn *conn, char *buf) return io_read(conn, buf, 2, in_conn_done, NULL); } +/* Every second time we say we're exhausted */ static int do_nothing(int fd, struct io_plan_arg *arg) { - return 1; + static bool read_once; + + read_once = !read_once; + if (read_once) + return 1; + + errno = EAGAIN; + return -1; } static struct io_plan *dummy_write(struct io_conn *conn, -- 2.39.5 From f927e4beeaa63ff5596967012b1a84e0d1cc4c49 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Jul 2024 07:43:38 +0930 Subject: [PATCH 10/16] io/fdpass: don't leak fd if conn closes before we write it. Signed-off-by: Rusty Russell --- ccan/io/fdpass/fdpass.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ccan/io/fdpass/fdpass.c b/ccan/io/fdpass/fdpass.c index e002ddc1..d4936ed1 100644 --- a/ccan/io/fdpass/fdpass.c +++ b/ccan/io/fdpass/fdpass.c @@ -4,6 +4,12 @@ #include #include +static void destroy_conn_close_send_fd(struct io_conn *conn, + struct io_plan_arg *arg) +{ + close(arg->u1.s); +} + static int do_fd_send(int fd, struct io_plan_arg *arg) { if (!fdpass_send(fd, arg->u1.s)) { @@ -12,8 +18,11 @@ static int do_fd_send(int fd, struct io_plan_arg *arg) return 0; return -1; } - if (arg->u2.s) + if (arg->u2.vp) { + struct io_conn *conn = arg->u2.vp; close(arg->u1.s); + tal_del_destructor2(conn, destroy_conn_close_send_fd, arg); + } return 1; } @@ -26,7 +35,11 @@ struct io_plan *io_send_fd_(struct io_conn *conn, struct io_plan_arg *arg = io_plan_arg(conn, IO_OUT); arg->u1.s = fd; - arg->u2.s = fdclose; + /* We need conn ptr for destructor */ + arg->u2.vp = fdclose ? conn : NULL; + /* If conn closes before sending, we still need to close fd */ + if (fdclose) + tal_add_destructor2(conn, destroy_conn_close_send_fd, arg); return io_set_plan(conn, IO_OUT, do_fd_send, next, next_arg); } -- 2.39.5 From 624dbca5fe08119b2c339929afaf9351f70bac26 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 27 Aug 2024 13:42:02 +0930 Subject: [PATCH 11/16] tcon: fix warning from gcc when tcon_check_ptr arg is constant. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit e.g. `&msg`: ``` src/nostrdb.c: In function ‘ndb_ingester_queue_event’: ccan/ccan/tcon/tcon.h:150:24: error: the address of ‘msg’ will always evaluate as ‘true’ [-Werror=address] 150 | (sizeof((expr) ? (expr) : &(x)->_tcon[0].canary) ? (x) : (x)) | ^ ccan/ccan/tcon/tcon.h:116:30: note: in definition of macro ‘tcon_unwrap’ 116 | #define tcon_unwrap(ptr) (&((ptr)->_base)) | ^~~ src/threadpool.h:91:42: note: in expansion of macro ‘tcon_check_ptr’ 91 | threadpool_dispatch_(tcon_unwrap(tcon_check_ptr((tp), tp_canary, (msg))), (msg)) | ^~~~~~~~~~~~~~ src/nostrdb.c:4114:16: note: in expansion of macro ‘threadpool_dispatch’ 4114 | return threadpool_dispatch(&ingester->tp, &msg); ``` Signed-off-by: Rusty Russell --- ccan/tcon/tcon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccan/tcon/tcon.h b/ccan/tcon/tcon.h index df3aac88..35d83e19 100644 --- a/ccan/tcon/tcon.h +++ b/ccan/tcon/tcon.h @@ -147,7 +147,7 @@ * It evaluates to @x so you can chain it. */ #define tcon_check_ptr(x, canary, expr) \ - (sizeof((expr) ? (expr) : &(x)->_tcon[0].canary) ? (x) : (x)) + (sizeof(0 ? (expr) : &(x)->_tcon[0].canary) ? (x) : (x)) /** * tcon_type - the type within a container (or void *) -- 2.39.5 From 161fe383533c12b95d9117a7ab75b5d3513287b0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 6 Sep 2024 15:02:18 +0930 Subject: [PATCH 12/16] opt: add more user-available bits. Signed-off-by: Rusty Russell --- ccan/opt/opt.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index e0331be2..d6f5634e 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -530,7 +530,7 @@ struct opt_table *opt_find_short(char arg); * You can set bits in type e.g. (1< Date: Mon, 12 Aug 2024 15:04:00 +0930 Subject: [PATCH 13/16] htable: separate htable_type constructors to clarify duplicate keys. Most hash tables do not want duplicates, so specify this at declaration time. This way you crash should such a thing be inserted, not have some subtle behavior you didn't expect! Also disable the functions depending on the type. Signed-off-by: Rusty Russell --- ccan/failtest/failtest.c | 4 +- ccan/htable/htable_type.h | 132 +++++++++++++++---------- ccan/htable/test/run-type-int.c | 36 ++++--- ccan/htable/test/run-type.c | 40 +++++--- ccan/htable/tools/density.c | 2 +- ccan/htable/tools/speed.c | 2 +- ccan/htable/tools/stringspeed.c | 2 +- ccan/intmap/benchmark/speed.c | 2 +- ccan/likely/likely.c | 4 +- ccan/objset/objset.h | 2 +- tools/ccanlint/tests/reduce_features.c | 3 +- tools/manifest.c | 4 +- 12 files changed, 142 insertions(+), 91 deletions(-) diff --git a/ccan/failtest/failtest.c b/ccan/failtest/failtest.c index 205ded25..5494e172 100644 --- a/ccan/failtest/failtest.c +++ b/ccan/failtest/failtest.c @@ -79,8 +79,8 @@ static bool call_eq(const struct failtest_call *call1, } /* Defines struct failtable. */ -HTABLE_DEFINE_TYPE(struct failtest_call, (struct failtest_call *), hash_call, - call_eq, failtable); +HTABLE_DEFINE_NODUPS_TYPE(struct failtest_call, (struct failtest_call *), hash_call, + call_eq, failtable); bool (*failtest_exit_check)(struct tlist_calls *history); diff --git a/ccan/htable/htable_type.h b/ccan/htable/htable_type.h index 0aacb7f3..103d2ff1 100644 --- a/ccan/htable/htable_type.h +++ b/ccan/htable/htable_type.h @@ -1,19 +1,23 @@ /* Licensed under LGPLv2+ - see LICENSE file for details */ #ifndef CCAN_HTABLE_TYPE_H #define CCAN_HTABLE_TYPE_H +#include "config.h" +#include #include #include -#include "config.h" /** - * HTABLE_DEFINE_TYPE - create a set of htable ops for a type + * HTABLE_DEFINE_NODUPS_TYPE/HTABLE_DEFINE_DUPS_TYPE - create a set of htable ops for a type * @type: a type whose pointers will be values in the hash. * @keyof: a function/macro to extract a key: @keyof(const type *elem) * @hashfn: a hash function for a @key: size_t @hashfn(const *) * @eqfn: an equality function keys: bool @eqfn(const type *, const *) * @prefix: a prefix for all the functions to define (of form _*) * - * NULL values may not be placed into the hash table. + * There are two variants, one of which allows duplicate keys, and one which + * does not. The defined functions differ in some cases, as shown below. + * + * NULL values may not be placed into the hash table (nor (void *)1). * * This defines the type hashtable type and an iterator type: * struct ; @@ -33,15 +37,18 @@ * * Delete and delete-by key return true if it was in the set: * bool _del(struct *ht, const *e); - * bool _delkey(struct *ht, const *k); + * bool _delkey(struct *ht, const *k) (NODUPS only); * * Delete by iterator: * bool _delval(struct *ht, struct _iter *i); * - * Find and return the (first) matching element, or NULL: - * type *_get(const struct @name *ht, const *k); + * Find and return the matching element, or NULL: + * type *_get(const struct @name *ht, const *k) (NODUPS only); * - * Find and return all matching elements, or NULL: + * Test for an element: + * bool _exists(const struct @name *ht, const *k); + * + * Find and return all matching elements, or NULL (DUPS only): * type *_getfirst(const struct @name *ht, const *k, * struct _iter *i); * type *_getnext(const struct @name *ht, const *k, @@ -59,7 +66,7 @@ * You can use HTABLE_INITIALIZER like so: * struct ht = { HTABLE_INITIALIZER(ht.raw, _hash, NULL) }; */ -#define HTABLE_DEFINE_TYPE(type, keyof, hashfn, eqfn, name) \ +#define HTABLE_DEFINE_TYPE_CORE(type, keyof, hashfn, eqfn, name) \ struct name { struct htable raw; }; \ struct name##_iter { struct htable_iter i; }; \ static inline size_t name##_hash(const void *elem, void *priv) \ @@ -89,66 +96,33 @@ { \ return htable_copy(&dst->raw, &src->raw); \ } \ - static inline bool name##_add(struct name *ht, const type *elem) \ - { \ - return htable_add(&ht->raw, hashfn(keyof(elem)), elem); \ - } \ static inline UNNEEDED bool name##_del(struct name *ht, \ const type *elem) \ { \ return htable_del(&ht->raw, hashfn(keyof(elem)), elem); \ } \ - static inline UNNEEDED type *name##_get(const struct name *ht, \ - const HTABLE_KTYPE(keyof, type) k) \ - { \ - struct htable_iter i; \ - size_t h = hashfn(k); \ - void *c; \ - \ - for (c = htable_firstval(&ht->raw,&i,h); \ - c; \ - c = htable_nextval(&ht->raw,&i,h)) { \ - if (eqfn(c, k)) \ - return c; \ - } \ - return NULL; \ - } \ static inline UNNEEDED type *name##_getmatch_(const struct name *ht, \ const HTABLE_KTYPE(keyof, type) k, \ size_t h, \ type *v, \ - struct name##_iter *iter) \ + struct htable_iter *iter) \ { \ while (v) { \ if (eqfn(v, k)) \ break; \ - v = htable_nextval(&ht->raw, &iter->i, h); \ + v = htable_nextval(&ht->raw, iter, h); \ } \ return v; \ } \ - static inline UNNEEDED type *name##_getfirst(const struct name *ht, \ - const HTABLE_KTYPE(keyof, type) k, \ - struct name##_iter *iter) \ - { \ - size_t h = hashfn(k); \ - type *v = htable_firstval(&ht->raw, &iter->i, h); \ - return name##_getmatch_(ht, k, h, v, iter); \ - } \ - static inline UNNEEDED type *name##_getnext(const struct name *ht, \ - const HTABLE_KTYPE(keyof, type) k, \ - struct name##_iter *iter) \ + static inline UNNEEDED bool name##_exists(const struct name *ht, \ + const HTABLE_KTYPE(keyof, type) k) \ { \ + struct htable_iter i; \ size_t h = hashfn(k); \ - type *v = htable_nextval(&ht->raw, &iter->i, h); \ - return name##_getmatch_(ht, k, h, v, iter); \ - } \ - static inline UNNEEDED bool name##_delkey(struct name *ht, \ - const HTABLE_KTYPE(keyof, type) k) \ - { \ - type *elem = name##_get(ht, k); \ - if (elem) \ - return name##_del(ht, elem); \ - return false; \ + void *v; \ + \ + v = htable_firstval(&ht->raw, &i, h); \ + return name##_getmatch_(ht, k, h, v, &i) != NULL; \ } \ static inline UNNEEDED void name##_delval(struct name *ht, \ struct name##_iter *iter) \ @@ -177,6 +151,64 @@ return htable_prev(&ht->raw, &iter->i); \ } +#define HTABLE_DEFINE_NODUPS_TYPE(type, keyof, hashfn, eqfn, name) \ + HTABLE_DEFINE_TYPE_CORE(type, keyof, hashfn, eqfn, name) \ + static inline UNNEEDED type *name##_get(const struct name *ht, \ + const HTABLE_KTYPE(keyof, type) k) \ + { \ + struct htable_iter i; \ + size_t h = hashfn(k); \ + void *v; \ + \ + v = htable_firstval(&ht->raw, &i, h); \ + return name##_getmatch_(ht, k, h, v, &i); \ + } \ + static inline bool name##_add(struct name *ht, const type *elem) \ + { \ + /* Open-coded for slightly more efficiency */ \ + const HTABLE_KTYPE(keyof, type) k = keyof(elem); \ + struct htable_iter i; \ + size_t h = hashfn(k); \ + void *v; \ + \ + v = htable_firstval(&ht->raw, &i, h); \ + assert(!name##_getmatch_(ht, k, h, v, &i)); \ + return htable_add(&ht->raw, h, elem); \ + } \ + static inline UNNEEDED bool name##_delkey(struct name *ht, \ + const HTABLE_KTYPE(keyof, type) k) \ + { \ + type *elem = name##_get(ht, k); \ + if (elem) \ + return name##_del(ht, elem); \ + return false; \ + } + +#define HTABLE_DEFINE_DUPS_TYPE(type, keyof, hashfn, eqfn, name) \ + HTABLE_DEFINE_TYPE_CORE(type, keyof, hashfn, eqfn, name) \ + static inline bool name##_add(struct name *ht, const type *elem) \ + { \ + const HTABLE_KTYPE(keyof, type) k = keyof(elem); \ + return htable_add(&ht->raw, hashfn(k), elem); \ + } \ + static inline UNNEEDED type *name##_getfirst(const struct name *ht, \ + const HTABLE_KTYPE(keyof, type) k, \ + struct name##_iter *iter) \ + { \ + size_t h = hashfn(k); \ + type *v = htable_firstval(&ht->raw, &iter->i, h); \ + return name##_getmatch_(ht, k, h, v, &iter->i); \ + } \ + static inline UNNEEDED type *name##_getnext(const struct name *ht, \ + const HTABLE_KTYPE(keyof, type) k, \ + struct name##_iter *iter) \ + { \ + size_t h = hashfn(k); \ + type *v = htable_nextval(&ht->raw, &iter->i, h); \ + return name##_getmatch_(ht, k, h, v, &iter->i); \ + } + + #if HAVE_TYPEOF #define HTABLE_KTYPE(keyof, type) typeof(keyof((const type *)NULL)) #else diff --git a/ccan/htable/test/run-type-int.c b/ccan/htable/test/run-type-int.c index 16c4f56e..69142c7a 100644 --- a/ccan/htable/test/run-type-int.c +++ b/ccan/htable/test/run-type-int.c @@ -38,7 +38,7 @@ static bool cmp(const struct obj *obj, const unsigned int key) return obj->key == key; } -HTABLE_DEFINE_TYPE(struct obj, objkey, objhash, cmp, htable_obj); +HTABLE_DEFINE_NODUPS_TYPE(struct obj, objkey, objhash, cmp, htable_obj); static void add_vals(struct htable_obj *ht, struct obj val[], unsigned int num) @@ -112,14 +112,19 @@ static bool check_mask(struct htable *ht, const struct obj val[], unsigned num) return true; } +/* This variant allows duplicates! */ +HTABLE_DEFINE_DUPS_TYPE(struct obj, objkey, objhash, cmp, htable_obj_dups); + int main(void) { unsigned int i; struct htable_obj ht, ht2; + struct htable_obj_dups ht_dups; struct obj val[NUM_VALS], *result; unsigned int dne; void *p; struct htable_obj_iter iter; + struct htable_obj_dups_iter dups_iter; plan_tests(29); for (i = 0; i < NUM_VALS; i++) @@ -183,32 +188,35 @@ int main(void) del_vals_bykey(&ht, val, NUM_VALS); del_vals_bykey(&ht2, val, NUM_VALS); + /* Duplicates-allowed tests */ + htable_obj_dups_init(&ht_dups); /* Write two of the same value. */ val[1] = val[0]; - htable_obj_add(&ht, &val[0]); - htable_obj_add(&ht, &val[1]); + htable_obj_dups_add(&ht_dups, &val[0]); + htable_obj_dups_add(&ht_dups, &val[1]); i = 0; - result = htable_obj_getfirst(&ht, i, &iter); + result = htable_obj_dups_getfirst(&ht_dups, i, &dups_iter); ok1(result == &val[0] || result == &val[1]); if (result == &val[0]) { - ok1(htable_obj_getnext(&ht, i, &iter) == &val[1]); - ok1(htable_obj_getnext(&ht, i, &iter) == NULL); + ok1(htable_obj_dups_getnext(&ht_dups, i, &dups_iter) == &val[1]); + ok1(htable_obj_dups_getnext(&ht_dups, i, &dups_iter) == NULL); /* Deleting first should make us iterate over the other. */ - ok1(htable_obj_del(&ht, &val[0])); - ok1(htable_obj_getfirst(&ht, i, &iter) == &val[1]); - ok1(htable_obj_getnext(&ht, i, &iter) == NULL); + ok1(htable_obj_dups_del(&ht_dups, &val[0])); + ok1(htable_obj_dups_getfirst(&ht_dups, i, &dups_iter) == &val[1]); + ok1(htable_obj_dups_getnext(&ht_dups, i, &dups_iter) == NULL); } else { - ok1(htable_obj_getnext(&ht, i, &iter) == &val[0]); - ok1(htable_obj_getnext(&ht, i, &iter) == NULL); + ok1(htable_obj_dups_getnext(&ht_dups, i, &dups_iter) == &val[0]); + ok1(htable_obj_dups_getnext(&ht_dups, i, &dups_iter) == NULL); /* Deleting first should make us iterate over the other. */ - ok1(htable_obj_del(&ht, &val[1])); - ok1(htable_obj_getfirst(&ht, i, &iter) == &val[0]); - ok1(htable_obj_getnext(&ht, i, &iter) == NULL); + ok1(htable_obj_dups_del(&ht_dups, &val[1])); + ok1(htable_obj_dups_getfirst(&ht_dups, i, &dups_iter) == &val[0]); + ok1(htable_obj_dups_getnext(&ht_dups, i, &dups_iter) == NULL); } + htable_obj_dups_clear(&ht_dups); htable_obj_clear(&ht); htable_obj_clear(&ht2); return exit_status(); diff --git a/ccan/htable/test/run-type.c b/ccan/htable/test/run-type.c index c4201ed0..857bbbfb 100644 --- a/ccan/htable/test/run-type.c +++ b/ccan/htable/test/run-type.c @@ -33,7 +33,10 @@ static bool cmp(const struct obj *obj, const unsigned int *key) return obj->key == *key; } -HTABLE_DEFINE_TYPE(struct obj, objkey, objhash, cmp, htable_obj); +HTABLE_DEFINE_NODUPS_TYPE(struct obj, objkey, objhash, cmp, + htable_obj); +HTABLE_DEFINE_DUPS_TYPE(struct obj, objkey, objhash, cmp, + htable_obj_dups); static void add_vals(struct htable_obj *ht, struct obj val[], unsigned int num) @@ -111,12 +114,14 @@ int main(void) { unsigned int i; struct htable_obj ht, ht2; + struct htable_obj_dups ht_dups; struct obj val[NUM_VALS], *result; unsigned int dne; void *p; struct htable_obj_iter iter; + struct htable_obj_dups_iter dups_iter; - plan_tests(35); + plan_tests(36); for (i = 0; i < NUM_VALS; i++) val[i].key = i; dne = i; @@ -182,32 +187,37 @@ int main(void) del_vals_bykey(&ht, val, NUM_VALS); del_vals_bykey(&ht2, val, NUM_VALS); + /* Duplicates-allowed tests */ + htable_obj_dups_init(&ht_dups); + /* Write two of the same value. */ val[1] = val[0]; - htable_obj_add(&ht, &val[0]); - htable_obj_add(&ht, &val[1]); + htable_obj_dups_add(&ht_dups, &val[0]); + htable_obj_dups_add(&ht_dups, &val[1]); i = 0; - result = htable_obj_getfirst(&ht, &i, &iter); + result = htable_obj_dups_getfirst(&ht_dups, &i, &dups_iter); ok1(result == &val[0] || result == &val[1]); if (result == &val[0]) { - ok1(htable_obj_getnext(&ht, &i, &iter) == &val[1]); - ok1(htable_obj_getnext(&ht, &i, &iter) == NULL); + ok1(htable_obj_dups_getnext(&ht_dups, &i, &dups_iter) == &val[1]); + ok1(htable_obj_dups_getnext(&ht_dups, &i, &dups_iter) == NULL); /* Deleting first should make us iterate over the other. */ - ok1(htable_obj_del(&ht, &val[0])); - ok1(htable_obj_getfirst(&ht, &i, &iter) == &val[1]); - ok1(htable_obj_getnext(&ht, &i, &iter) == NULL); + ok1(htable_obj_dups_del(&ht_dups, &val[0])); + ok1(htable_obj_dups_getfirst(&ht_dups, &i, &dups_iter) == &val[1]); + ok1(htable_obj_dups_getnext(&ht_dups, &i, &dups_iter) == NULL); } else { - ok1(htable_obj_getnext(&ht, &i, &iter) == &val[0]); - ok1(htable_obj_getnext(&ht, &i, &iter) == NULL); + ok1(htable_obj_dups_getnext(&ht_dups, &i, &dups_iter) == &val[0]); + ok1(htable_obj_dups_getnext(&ht_dups, &i, &dups_iter) == NULL); /* Deleting first should make us iterate over the other. */ - ok1(htable_obj_del(&ht, &val[1])); - ok1(htable_obj_getfirst(&ht, &i, &iter) == &val[0]); - ok1(htable_obj_getnext(&ht, &i, &iter) == NULL); + ok1(htable_obj_dups_del(&ht_dups, &val[1])); + ok1(htable_obj_dups_getfirst(&ht_dups, &i, &dups_iter) == &val[0]); + ok1(htable_obj_dups_getnext(&ht_dups, &i, &dups_iter) == NULL); } + htable_obj_dups_clear(&ht_dups); + ok1(htable_obj_dups_count(&ht_dups) == 0); htable_obj_clear(&ht); ok1(htable_obj_count(&ht) == 0); htable_obj_clear(&ht2); diff --git a/ccan/htable/tools/density.c b/ccan/htable/tools/density.c index 5f7400b9..df5afc51 100644 --- a/ccan/htable/tools/density.c +++ b/ccan/htable/tools/density.c @@ -26,7 +26,7 @@ static bool cmp(const ptrint_t *p, uintptr_t k) return key(p) == k; } -HTABLE_DEFINE_TYPE(ptrint_t, key, hash_uintptr, cmp, htable_ptrint); +HTABLE_DEFINE_NODUPS_TYPE(ptrint_t, key, hash_uintptr, cmp, htable_ptrint); /* Nanoseconds per operation */ static size_t normalize(const struct timeabs *start, diff --git a/ccan/htable/tools/speed.c b/ccan/htable/tools/speed.c index e185b6f6..a6198528 100644 --- a/ccan/htable/tools/speed.c +++ b/ccan/htable/tools/speed.c @@ -33,7 +33,7 @@ static bool cmp(const struct object *object, const unsigned int *key) return object->key == *key; } -HTABLE_DEFINE_TYPE(struct object, objkey, hash_obj, cmp, htable_obj); +HTABLE_DEFINE_NODUPS_TYPE(struct object, objkey, hash_obj, cmp, htable_obj); static unsigned int popcount(unsigned long val) { diff --git a/ccan/htable/tools/stringspeed.c b/ccan/htable/tools/stringspeed.c index c6ca10f5..5f30359a 100644 --- a/ccan/htable/tools/stringspeed.c +++ b/ccan/htable/tools/stringspeed.c @@ -31,7 +31,7 @@ static bool cmp(const char *obj, const char *key) return strcmp(obj, key) == 0; } -HTABLE_DEFINE_TYPE(char, strkey, hash_str, cmp, htable_str); +HTABLE_DEFINE_NODUPS_TYPE(char, strkey, hash_str, cmp, htable_str); /* Nanoseconds per operation */ static size_t normalize(const struct timeabs *start, diff --git a/ccan/intmap/benchmark/speed.c b/ccan/intmap/benchmark/speed.c index 16eb40f3..cf2dae7e 100644 --- a/ccan/intmap/benchmark/speed.c +++ b/ccan/intmap/benchmark/speed.c @@ -59,7 +59,7 @@ static bool eqfn(const struct htable_elem *elem, const uint64_t index) { return elem->index == index; } -HTABLE_DEFINE_TYPE(struct htable_elem, keyof, hashfn, eqfn, hash); +HTABLE_DEFINE_NODUPS_TYPE(struct htable_elem, keyof, hashfn, eqfn, hash); static bool check_val(intmap_index_t i, uint64_t *v, uint64_t *expected) { diff --git a/ccan/likely/likely.c b/ccan/likely/likely.c index 83e8d6fb..6b6e4aea 100644 --- a/ccan/likely/likely.c +++ b/ccan/likely/likely.c @@ -29,8 +29,8 @@ static bool trace_eq(const struct trace *t1, const struct trace *t2) } /* struct thash */ -HTABLE_DEFINE_TYPE(struct trace, (const struct trace *), hash_trace, trace_eq, - thash); +HTABLE_DEFINE_NODUPS_TYPE(struct trace, (const struct trace *), hash_trace, trace_eq, + thash); static struct thash htable = { HTABLE_INITIALIZER(htable.raw, thash_hash, NULL) }; diff --git a/ccan/objset/objset.h b/ccan/objset/objset.h index 03ec00f3..6464d913 100644 --- a/ccan/objset/objset.h +++ b/ccan/objset/objset.h @@ -20,7 +20,7 @@ static inline bool objset_eqfn_(const void *e1, const void *e2) { return e1 == e2; } -HTABLE_DEFINE_TYPE(void, objset_key_, objset_hashfn_, objset_eqfn_, objset_h); +HTABLE_DEFINE_NODUPS_TYPE(void, objset_key_, objset_hashfn_, objset_eqfn_, objset_h); /** * OBJSET_MEMBERS - declare members for a type-specific unordered objset. diff --git a/tools/ccanlint/tests/reduce_features.c b/tools/ccanlint/tests/reduce_features.c index 34a89706..89e61595 100644 --- a/tools/ccanlint/tests/reduce_features.c +++ b/tools/ccanlint/tests/reduce_features.c @@ -37,7 +37,8 @@ static bool option_cmp(const char *name1, const char *name2) return streq(name1, name2); } -HTABLE_DEFINE_TYPE(char, option_name, option_hash, option_cmp, htable_option); +HTABLE_DEFINE_NODUPS_TYPE(char, option_name, option_hash, option_cmp, + htable_option); static struct htable_option *htable_option_new(void) { diff --git a/tools/manifest.c b/tools/manifest.c index 82668acf..fcc2f15b 100644 --- a/tools/manifest.c +++ b/tools/manifest.c @@ -37,8 +37,8 @@ static bool dir_cmp(const struct manifest *m, const char *dir) return strcmp(m->dir, dir) == 0; } -HTABLE_DEFINE_TYPE(struct manifest, manifest_name, dir_hash, dir_cmp, - htable_manifest); +HTABLE_DEFINE_NODUPS_TYPE(struct manifest, manifest_name, dir_hash, dir_cmp, + htable_manifest); static struct htable_manifest *manifests; const char *get_ccan_file_contents(struct ccan_file *f) -- 2.39.5 From bc6c7377c3e5537c16f11fcdde815298b1ea5c99 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Apr 2025 10:14:44 +0930 Subject: [PATCH 14/16] time: flesh out more timemono functions. Signed-off-by: Rusty Russell --- ccan/time/test/run-monotonic.c | 7 ++++-- ccan/time/time.h | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/ccan/time/test/run-monotonic.c b/ccan/time/test/run-monotonic.c index 2da492d4..878d6249 100644 --- a/ccan/time/test/run-monotonic.c +++ b/ccan/time/test/run-monotonic.c @@ -7,13 +7,13 @@ int main(void) struct timemono t1, t2; struct timerel t3; - plan_tests(5); + plan_tests(7); /* Test time_mono */ t1 = time_mono(); t2 = time_mono(); - ok1(!time_less_(t2.ts, t1.ts)); + ok1(!timemono_before(t2, t1)); t3.ts.tv_sec = 1; t3.ts.tv_nsec = 0; @@ -24,5 +24,8 @@ int main(void) ok1(timemono_add(t1, t3).ts.tv_sec == t1.ts.tv_sec + 1); ok1(timemono_add(t2, t3).ts.tv_nsec == t2.ts.tv_nsec); + ok1(timemono_sub(timemono_add(t1, t3), t3).ts.tv_sec == t1.ts.tv_sec); + ok1(timemono_sub(timemono_add(t1, t3), t3).ts.tv_nsec == t1.ts.tv_nsec); + return exit_status(); } diff --git a/ccan/time/time.h b/ccan/time/time.h index 2fc8161e..48b8d1d2 100644 --- a/ccan/time/time.h +++ b/ccan/time/time.h @@ -220,6 +220,23 @@ static inline bool time_before(struct timeabs a, struct timeabs b) return time_less_(a.ts, b.ts); } +/** + * timemono_before - is a before b? + * @a: one monotonic time. + * @b: another monotonic time. + * + * Example: + * static bool still_valid(const struct timemono *start) + * { + * #define TIMEOUT time_from_msec(1000) + * return timemono_before(time_mono(), timemono_add(*start, TIMEOUT)); + * } + */ +static inline bool timemono_before(struct timemono a, struct timemono b) +{ + return time_less_(a.ts, b.ts); +} + /** * time_less - is a before b? * @a: one relative time. @@ -404,6 +421,29 @@ static inline struct timeabs timeabs_sub(struct timeabs abs, struct timerel rel) return t; } +/** + * timemono_sub - subtract a relative time from a monotonic time + * @mono: the monotonic time. + * @rel: the relative time. + * + * This returns a well formed struct timemono of @mono - @rel. + * + * Example: + * // We do one every second. + * static struct timemono previous_time(void) + * { + * return timemono_sub(time_mono(), time_from_msec(1000)); + * } + */ +static inline struct timemono timemono_sub(struct timemono mono, struct timerel rel) +{ + struct timemono t; + + t.ts = time_sub_(mono.ts, rel.ts); + return t; +} + + static inline struct timespec time_add_(struct timespec a, struct timespec b) { struct timespec sum; -- 2.39.5 From 734087370994b06caf4b91eb4027e039e15dd77a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Apr 2025 11:25:57 +0930 Subject: [PATCH 15/16] time: add timemono_after, fix example compilation. Signed-off-by: Rusty Russell --- ccan/time/test/run-monotonic.c | 7 +++++-- ccan/time/time.h | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ccan/time/test/run-monotonic.c b/ccan/time/test/run-monotonic.c index 878d6249..5eded26c 100644 --- a/ccan/time/test/run-monotonic.c +++ b/ccan/time/test/run-monotonic.c @@ -7,7 +7,7 @@ int main(void) struct timemono t1, t2; struct timerel t3; - plan_tests(7); + plan_tests(10); /* Test time_mono */ t1 = time_mono(); @@ -26,6 +26,9 @@ int main(void) ok1(timemono_sub(timemono_add(t1, t3), t3).ts.tv_sec == t1.ts.tv_sec); ok1(timemono_sub(timemono_add(t1, t3), t3).ts.tv_nsec == t1.ts.tv_nsec); - + + ok1(timemono_after(timemono_add(t1, t3), t1)); + ok1(!timemono_after(t1, timemono_add(t1, t3))); + ok1(!timemono_after(t1, t1)); return exit_status(); } diff --git a/ccan/time/time.h b/ccan/time/time.h index 48b8d1d2..cbfeefa0 100644 --- a/ccan/time/time.h +++ b/ccan/time/time.h @@ -193,6 +193,23 @@ static inline bool time_greater(struct timerel a, struct timerel b) return time_greater_(a.ts, b.ts); } +/** + * timemono_after - is a after b? + * @a: one monotonic time. + * @b: another monotonic time. + * + * Example: + * static bool timed_out(const struct timemono *start) + * { + * #define TIMEOUT time_from_msec(1000) + * return timemono_after(time_mono(), timemono_add(*start, TIMEOUT)); + * } + */ +static inline bool timemono_after(struct timemono a, struct timemono b) +{ + return time_greater_(a.ts, b.ts); +} + static inline bool time_less_(struct timespec a, struct timespec b) { if (TIME_CHECK(a).tv_sec < TIME_CHECK(b).tv_sec) @@ -528,6 +545,8 @@ static inline struct timerel timerel_add(struct timerel a, struct timerel b) * @div: number to divide it by. * * Example: + * #include + * * // How long does it take to do a fork? * static struct timerel forking_time(void) * { -- 2.39.5 From ca0940392e3cf17a0537d66ee6db3a4931cd3821 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Jun 2025 11:43:20 +0930 Subject: [PATCH 16/16] fdpass: fix complilation on FreeBSD. ``` cc ccan/ccan/fdpass/fdpass.c ccan/ccan/fdpass/fdpass.c:16:12: error: use of undeclared identifier 'u_long'; did you mean 'long'? char buf[CMSG_SPACE(sizeof(fd))]; ^ /usr/include/sys/socket.h:577:25: note: expanded from macro 'CMSG_SPACE' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:16:12: error: use of undeclared identifier 'u_long' /usr/include/sys/socket.h:577:25: note: expanded from macro 'CMSG_SPACE' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:16:12: error: use of undeclared identifier 'u_long'; did you mean 'long'? /usr/include/sys/socket.h:577:58: note: expanded from macro 'CMSG_SPACE' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:16:12: error: use of undeclared identifier 'u_long' /usr/include/sys/socket.h:577:58: note: expanded from macro 'CMSG_SPACE' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:26:19: error: use of undeclared identifier 'u_long'; did you mean 'long'? cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); ^ /usr/include/sys/socket.h:578:23: note: expanded from macro 'CMSG_LEN' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:26:19: error: use of undeclared identifier 'u_long' cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); ^ /usr/include/sys/socket.h:578:23: note: expanded from macro 'CMSG_LEN' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:26:19: error: use of undeclared identifier 'u_long' /usr/include/sys/socket.h:578:23: note: expanded from macro 'CMSG_LEN' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:27:9: error: use of undeclared identifier 'u_long'; did you mean 'long'? memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); ^ /usr/include/sys/socket.h:554:6: note: expanded from macro 'CMSG_DATA' _ALIGN(sizeof(struct cmsghdr))) ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:27:9: error: use of undeclared identifier 'u_long' /usr/include/sys/socket.h:554:6: note: expanded from macro 'CMSG_DATA' _ALIGN(sizeof(struct cmsghdr))) ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:53:12: error: use of undeclared identifier 'u_long'; did you mean 'long'? char buf[CMSG_SPACE(sizeof(fd))]; ^ /usr/include/sys/socket.h:577:25: note: expanded from macro 'CMSG_SPACE' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:53:12: error: use of undeclared identifier 'u_long' /usr/include/sys/socket.h:577:25: note: expanded from macro 'CMSG_SPACE' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:53:12: error: use of undeclared identifier 'u_long'; did you mean 'long'? /usr/include/sys/socket.h:577:58: note: expanded from macro 'CMSG_SPACE' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:74:27: error: use of undeclared identifier 'u_long'; did you mean 'long'? || cmsg->cmsg_len != CMSG_LEN(sizeof(fd)) ^ /usr/include/sys/socket.h:578:23: note: expanded from macro 'CMSG_LEN' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:74:27: error: use of undeclared identifier 'u_long' /usr/include/sys/socket.h:578:23: note: expanded from macro 'CMSG_LEN' ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:81:14: error: use of undeclared identifier 'u_long'; did you mean 'long'? memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); ^ /usr/include/sys/socket.h:554:6: note: expanded from macro 'CMSG_DATA' _ALIGN(sizeof(struct cmsghdr))) ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ ccan/ccan/fdpass/fdpass.c:81:14: error: use of undeclared identifier 'u_long' /usr/include/sys/socket.h:554:6: note: expanded from macro 'CMSG_DATA' _ALIGN(sizeof(struct cmsghdr))) ^ /usr/include/machine/_align.h:45:22: note: expanded from macro '_ALIGN' ^ 16 errors generated. gmake: *** [Makefile:994: ccan-fdpass.o] Error 1 ``` Reported-by: https://github.com/21M4TW Signed-off-by: Rusty Russell --- ccan/fdpass/fdpass.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ccan/fdpass/fdpass.c b/ccan/fdpass/fdpass.c index 7331468f..af1a7fdb 100644 --- a/ccan/fdpass/fdpass.c +++ b/ccan/fdpass/fdpass.c @@ -3,6 +3,7 @@ #include #include #include +#include bool fdpass_send(int sockout, int fd) { -- 2.39.5