From 524ca348c0afabf186b8fd03a1660f63fe9df29f Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 14 Apr 2025 13:47:36 -0600 Subject: [PATCH 01/66] New `duration` type This is intended to replace `mcd_duration` as a more capable duration type. --- src/common/src/mlib/duration.h | 273 +++++++++++++++++++++++++++++++++ src/common/tests/test-mlib.c | 86 +++++++++++ 2 files changed, 359 insertions(+) create mode 100644 src/common/src/mlib/duration.h diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h new file mode 100644 index 0000000000..8895cf7d65 --- /dev/null +++ b/src/common/src/mlib/duration.h @@ -0,0 +1,273 @@ +#ifndef MLIB_DURATION_H_INCLUDED +#define MLIB_DURATION_H_INCLUDED + +#include +#include +#include +#include + +#include +#include + +mlib_extern_c_begin (); + +/** + * @brief The integral type used to represent a count of units of time. + */ +typedef int64_t mlib_duration_rep_t; + +/** + * @brief Represents a duration of time, either positive, negative, or zero. + * + * @note A zero-initialized (static initialized) duration represents the zero + * duration (no elapsed time) + * + * @note The time representation is intended to be abstract, and should be + * converted to concrete units of time by calling the `_count` functions. + */ +typedef struct mlib_duration { + /** + * @brief The integral representation of the duration. + * + * Do not read or modify this field except to zero-initialize it. + */ + mlib_duration_rep_t _rep; +} mlib_duration; + +/** + * @brief A macro that expands to an `mlib_duration` representing no elapsed + * time + */ +#define mlib_duration_zero() mlib_init (mlib_duration){0} +/** + * @brief A macro that expands to the maximum positive duration + */ +#define mlib_duration_max() \ + mlib_init (mlib_duration) \ + { \ + mlib_maxof (mlib_duration_rep_t) \ + } +/** + * @brief A macro that expands to the minimum duration (a negative duration) + */ +#define mlib_duration_min() \ + mlib_init (mlib_duration) \ + { \ + mlib_minof (mlib_duration_rep_t) \ + } + +/** + * @brief Obtain the count of microseconds represented by the duration (round + * toward zero) + */ +static inline mlib_duration_rep_t +mlib_microseconds_count (const mlib_duration dur) mlib_noexcept +{ + return dur._rep; +} + +/** + * @brief Obtain the count of milliseconds represented by the duration (round + * toward zero) + */ +static inline mlib_duration_rep_t +mlib_milliseconds_count (const mlib_duration dur) mlib_noexcept +{ + return mlib_microseconds_count (dur) / 1000; +} + +/** + * @brief Obtain the count of seconds represented by the duration (rounded + * toward zero) + */ +static inline mlib_duration_rep_t +mlib_seconds_count (const mlib_duration dur) mlib_noexcept +{ + return mlib_milliseconds_count (dur) / 1000; +} + +/** + * @brief Create a duration object that represents the given number of + * nanoseconds + */ +static inline mlib_duration +mlib_nanoseconds (const mlib_duration_rep_t n) mlib_noexcept +{ + // We encode as a count of microseconds, so we lose precision here. + mlib_duration ret; + ret._rep = n / 1000; + return ret; +} + +/** + * @brief Create a duration object that represents the given number of + * microseconds + */ +static inline mlib_duration +mlib_microseconds (const mlib_duration_rep_t n) mlib_noexcept +{ + mlib_duration ret; + ret._rep = n; + return ret; +} + +/** + * @brief Create a duration object that represents the given number of + * milliseconds + */ +static inline mlib_duration +mlib_milliseconds (const mlib_duration_rep_t n) mlib_noexcept +{ + mlib_duration_rep_t clamp = 0; + if (mlib_mul (&clamp, n, 1000)) { + clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); + } + return mlib_microseconds (clamp); +} + +/** + * @brief Create a duration object that represents the given number of seconds + */ +static inline mlib_duration +mlib_seconds (const mlib_duration_rep_t n) mlib_noexcept +{ + mlib_duration_rep_t clamp = 0; + if (mlib_mul (&clamp, n, 1000 * 1000)) { + clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); + } + return mlib_microseconds (clamp); +} + +/** + * @brief Create a new duration that represents the sum of two other durations + */ +static inline mlib_duration +mlib_duration_add (const mlib_duration a, const mlib_duration b) mlib_noexcept +{ + mlib_duration ret = {0}; + if (mlib_add (&ret._rep, a._rep, b._rep)) { + if (a._rep > 0) { + ret = mlib_duration_max (); + } else { + ret = mlib_duration_min (); + } + } + return ret; +} + +/** + * @brief Create a duration from subtracting the right-hand duration from + * the left-hand duration (computes their difference) + */ +static inline mlib_duration +mlib_duration_sub (const mlib_duration a, const mlib_duration b) mlib_noexcept +{ + mlib_duration ret = {0}; + if (mlib_sub (&ret._rep, a._rep, b._rep)) { + if (a._rep < 0) { + ret = mlib_duration_min (); + } else { + ret = mlib_duration_max (); + } + } + return ret; +} + +/** + * @brief Multiply a duration by some factor + */ +static inline mlib_duration +mlib_duration_mul (const mlib_duration dur, int fac) mlib_noexcept +{ + mlib_duration ret = {0}; + if (mlib_mul (&ret._rep, dur._rep, fac)) { + if ((dur._rep < 0) != (fac < 0)) { + // Different signs: Neg × Pos = Neg + ret = mlib_duration_min (); + } else { + // Same signs: Pos × Pos = Pos + // Neg × Neg = Pos + ret = mlib_duration_max (); + } + } + return ret; +} + +/** + * @brief Divide a duration by some divisor + */ +static inline mlib_duration +mlib_duration_div (mlib_duration a, int div) mlib_noexcept +{ + mlib_check (div, neq, 0); + if (div == -1 && a._rep == mlib_minof (mlib_duration_rep_t)) { + // MIN / -1 is UB, but the saturating result is the max + a = mlib_duration_max (); + } else { + a._rep /= div; + } + return a; +} + +/** + * @brief Compare two durations + * + * @retval <0 If `a` is less-than `b` + * @retval >0 If `b` is less-than `a` + * @retval 0 If `a` and `b` are equal durations + */ +static inline enum mlib_cmp_result +mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept +{ + return mlib_cmp (a._rep, b._rep); +} + +/** + * @brief Test whether two durations are equal + */ +static inline bool +mlib_duration_eq (const mlib_duration a, const mlib_duration b) mlib_noexcept +{ + return mlib_duration_cmp (a, b) == mlib_equal; +} + +/** + * @brief Obtain an mlib_duration that corresponds to a `timespec` value + * + * @note The `timespec` type may represent times outside of the range of, or + * more precise than, what is representable in `mlib_duration`. In such case, + * the returned duration will be the nearest representable duration, rounded + * toward zero. + */ +static inline mlib_duration +mlib_duration_from_timespec (const struct timespec ts) mlib_noexcept +{ + return mlib_duration_add (mlib_seconds (ts.tv_sec), mlib_nanoseconds (ts.tv_nsec)); +} + +/** + * @brief Create a C `struct timespec` that corresponds to the given duration + * + * @param d The duration to be converted + * @return struct timespec A timespec that represents the same durations + */ +static inline struct timespec +mlib_duration_to_timespec (const mlib_duration d) mlib_noexcept +{ + // Number of full seconds to wait + const mlib_duration_rep_t n_full_seconds = mlib_seconds_count (d); + // Duration with full seconds removed: + const mlib_duration usec_part = mlib_duration_sub (d, mlib_seconds (n_full_seconds)); + // Number of microseconds in the duration, minus all full seconds + const mlib_duration_rep_t n_remaining_microseconds = mlib_microseconds_count (usec_part); + // Compute the number of nanoseconds: + const int32_t n_nsec = mlib_assert_mul (int32_t, n_remaining_microseconds, 1000); + struct timespec ret; + ret.tv_sec = n_full_seconds; + ret.tv_nsec = n_nsec; + return ret; +} + +mlib_extern_c_end (); + +#endif // MLIB_DURATION_H_INCLUDED diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 239c7bbc35..3f2535be6a 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -876,6 +877,90 @@ _test_str_view (void) } } +static void +_test_duration (void) +{ + mlib_duration d = mlib_duration_zero (); + mlib_check (mlib_microseconds_count (d), eq, 0); + + // Comparison + mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (4)) == 0); + mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (5)) < 0); + mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (-5)) > 0); + // Equality tests + mlib_check (mlib_duration_eq (mlib_seconds (4), mlib_milliseconds (4000))); + mlib_check (!mlib_duration_eq (mlib_seconds (4), mlib_milliseconds (4001))); + + // Overflow saturates: + d = mlib_seconds (mlib_maxof (mlib_duration_rep_t)); + mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + + d = mlib_duration_mul (d, 16); + mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + + // Rounds toward zero + d = mlib_milliseconds (1050); + mlib_check (mlib_seconds_count (d), eq, 1); + d = mlib_milliseconds (-1050); + mlib_check (mlib_seconds_count (d), eq, -1); + d = mlib_microseconds (1729); + mlib_check (mlib_milliseconds_count (d), eq, 1); + d = mlib_microseconds (-1729); + mlib_check (mlib_milliseconds_count (d), eq, -1); + + d = mlib_duration_add (mlib_seconds (1), mlib_milliseconds (729)); + mlib_check (mlib_microseconds_count (d), eq, 1729000); + d = mlib_duration_add (mlib_seconds (-3), mlib_duration_min ()); + mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + d = mlib_duration_add (mlib_seconds (4), mlib_duration_max ()); + mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + + d = mlib_duration_sub (mlib_seconds (4), mlib_milliseconds (2271)); + mlib_check (mlib_milliseconds_count (d), eq, 1729); + // Overflow saturates: + d = mlib_duration_sub (mlib_milliseconds (-4), mlib_duration_max ()); + mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + d = mlib_duration_sub (mlib_milliseconds (4), mlib_duration_min ()); + mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + + d = mlib_duration_mul (mlib_seconds (4), 5); + mlib_check (mlib_duration_eq (d, mlib_seconds (20))); + d = mlib_duration_mul (mlib_duration_max (), 2); + mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + d = mlib_duration_mul (mlib_duration_max (), -2); + mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + d = mlib_duration_mul (mlib_duration_min (), 2); + mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + d = mlib_duration_mul (mlib_duration_min (), -2); + mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + + d = mlib_duration_div (mlib_duration_max (), -1); + mlib_check (mlib_duration_cmp (d, mlib_duration_zero ()) < 0); + d = mlib_duration_div (mlib_duration_min (), -1); + mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_assert_aborts () { + // Division by zero + d = mlib_duration_div (d, 0); + } + + // To/from timespec + struct timespec ts; + ts.tv_sec = 4; + ts.tv_nsec = 0; + d = mlib_duration_from_timespec (ts); + mlib_check (mlib_duration_eq (d, mlib_seconds (4))); + // + ts.tv_sec = -3; + ts.tv_nsec = -4000; + d = mlib_duration_from_timespec (ts); + mlib_check (mlib_duration_eq (d, mlib_microseconds (-3000004))); + // + ts = mlib_duration_to_timespec (mlib_microseconds (-5000908)); + mlib_check (ts.tv_sec, eq, -5); + mlib_check (ts.tv_nsec, eq, -908000); +} + + void test_mlib_install (TestSuite *suite) { @@ -891,6 +976,7 @@ test_mlib_install (TestSuite *suite) TestSuite_Add (suite, "/mlib/check-cast", _test_cast); TestSuite_Add (suite, "/mlib/ckdint-partial", _test_ckdint_partial); TestSuite_Add (suite, "/mlib/str_view", _test_str_view); + TestSuite_Add (suite, "/mlib/duration", _test_duration); } mlib_diagnostic_pop (); From e8a607b02f4773db1aad99c788a18f53c29f0318 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 15 Apr 2025 12:00:18 -0600 Subject: [PATCH 02/66] Add a shim header for including Win32 APIs --- src/common/src/mlib/windows-lean.h | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/common/src/mlib/windows-lean.h diff --git a/src/common/src/mlib/windows-lean.h b/src/common/src/mlib/windows-lean.h new file mode 100644 index 0000000000..ae63a1af5e --- /dev/null +++ b/src/common/src/mlib/windows-lean.h @@ -0,0 +1,32 @@ +/** + * @file windows-lean.h + * @brief Windows.h inclusion shim + * @date 2025-04-07 + * + * This file will conditionally include ``, and wraps it with + * `WIN32_LEAN_AND_MEAN` and `NOMINMAX`. + * + * @copyright Copyright (c) 2025 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_WINDOWS_LEAN_H_INCLUDED +#define MLIB_WINDOWS_LEAN_H_INCLUDED + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#endif + +#endif // MLIB_WINDOWS_LEAN_H_INCLUDED From 4f66d778087aaf3991e5edf3034fcb2833b71405 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 15 Apr 2025 12:10:57 -0600 Subject: [PATCH 03/66] New time point and sleeping functions --- src/common/src/mlib/time_point.h | 229 +++++++++++++++++++++++++++++++ src/common/tests/test-mlib.c | 42 ++++++ 2 files changed, 271 insertions(+) create mode 100644 src/common/src/mlib/time_point.h diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h new file mode 100644 index 0000000000..35de1a7212 --- /dev/null +++ b/src/common/src/mlib/time_point.h @@ -0,0 +1,229 @@ +#ifndef MLIB_TIME_POINT_H_INCLUDED +#define MLIB_TIME_POINT_H_INCLUDED + +#include +#include +#include + +#ifdef __has_include +#if __has_include() +#include +#endif +#endif + +// Win32 Time APIs +#include "./windows-lean.h" + +// Check for POSIX clock functions functions +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L +#include +#define mlib_have_posix_clocks() 1 +#endif + +#ifndef mlib_have_posix_clocks +#define mlib_have_posix_clocks() 0 +#endif + +#include +#include + +mlib_extern_c_begin (); + +/** + * @brief An abstract point-in-time type + * + * This type represents an abstract stable point in time. + */ +typedef struct mlib_time_point { + /** + * @brief The encoding of the time point as a duration relative to some + * unspecified stable real point in time. + * + * The stable time point may change between program executions, so this + * object should not be stored outside of the program's execution. + */ + mlib_duration _time_since_monotonic_start; +} mlib_time_point; + +/** + * @brief Obtain a point-in-time corresponding to the current time + */ +static inline mlib_time_point +mlib_now (void) mlib_noexcept +{ +#if mlib_have_posix_clocks() + // Use POSIX clock_gettime + struct timespec ts; + // Use the POSIX monotonic clock + int rc = clock_gettime (CLOCK_MONOTONIC, &ts); + // The above call must never fail: + mlib_check (rc, eq, 0); + // Encode the time point: + mlib_time_point ret; + ret._time_since_monotonic_start = mlib_duration_from_timespec (ts); + return ret; +#elif defined(_WIN32) + // Win32 API that returns a count of milliseconds. The time value is + // monotonically increasing. + unsigned long long ms = GetTickCount64 (); + mlib_time_point ret; + ret._time_since_monotonic_start = mlib_milliseconds (ms); + return ret; +#else +#error We don't know how to get the current time on this platform +#endif +} + +/** + * @brief Obtain a point-in-time relative to a base time offset by the given + * duration (which may be negative). + * + * @param from The basis of the time offset + * @param delta The amount of time to shift the resulting time point + * @return mlib_time_point If 'delta' is a positive duration, the result is a + * point-in-time *after* 'from'. If 'delta' is a negative duration, the result + * is a point-in-time *before* 'from'. + */ +static inline mlib_time_point +mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept +{ + mlib_time_point ret; + ret._time_since_monotonic_start = mlib_duration_add (from._time_since_monotonic_start, delta); + return ret; +} + +/** + * @brief Obtain the duration between two points in time. + * + * @param then The target time + * @param from The base time + * @return mlib_duration The amount of time you would need to wait starting + * at 'from' for the time to become 'then' (the result may be a negative + * duration). + * + * Intuition: If "then" is "in the future" relative to "from", you will + * receive a positive duration, indicating an amount of time to wait + * beginning at 'from' to reach 'then'. If "then" is actually *before* + * "from", you will receive a paradoxical *negative* duration, indicating + * the amount of time needed to time-travel backwards to reach "then." + */ +static inline mlib_duration +mlib_time_difference (mlib_time_point then, mlib_time_point from) +{ + return mlib_duration_sub (then._time_since_monotonic_start, from._time_since_monotonic_start); +} + +/** + * @brief Compare two time points to create an ordering. + * + * A time point "in the past" is "less than" a time point "in the future". + * + * @retval <0 If 'a' is before 'b' + * @retval >0 If 'b' is before 'a' + * @retval 0 If 'a' and 'b' are equivalent + */ +static inline enum mlib_cmp_result +mlib_time_cmp (mlib_time_point a, mlib_time_point b) mlib_noexcept +{ + return mlib_duration_cmp (a._time_since_monotonic_start, b._time_since_monotonic_start); +} + +/** + * @brief Pause the calling thread until at least the specified duration has elapsed. + * + * @param d The duration of time to pause the thread. If this duration is zero + * or negative, then this function returns immediately. + * @return int An error code, if any occurred. Returns zero upon success, or + * the system's error number value (`errno` on POSIX, `GetLastError()` on + * Windows) + */ +static inline int +mlib_this_thread_sleep_for (const mlib_duration d) mlib_noexcept +{ + mlib_duration_rep_t duration_usec = mlib_microseconds_count (d); + if (duration_usec <= 0) { + // Don't sleep any time + return 0; + } +#if mlib_have_posix_clocks() + // Convert the microseconds count to the value for the usleep function + useconds_t i = 0; + if (mlib_narrow (&i, mlib_microseconds_count (d))) { + // Too many microseconds. Sleep for the max. + i = mlib_maxof (useconds_t); + } + int rc = usleep (mlib_microseconds_count (d)); + if (rc != 0) { + return errno; + } + return 0; +#elif defined(_WIN32) + DWORD retc = 0; + // Use WaitableTimer + const HANDLE timer = CreateWaitableTimerW (/* no attributes */ NULL, + /* Manual reset */ true, + /* Unnamed */ NULL); + // Check that we actually succeeded in creating a timer. + if (!timer) { + retc = GetLastError (); + goto done; + } + // Convert the number of microseconds into a count of 100ns intervals. Use + // a negative value to request a relative sleep time. + LONGLONG negative_n_100ns_units = 0; + if (mlib_mul (&negative_n_100ns_units, duration_usec, -10)) { + // Too many units. Clamp to the max duration (negative for a relative + // sleep): + negative_n_100ns_units = mlib_minof (LONGLONG); + } + LARGE_INTEGER due_time; + due_time.QuadPart = negative_n_100ns_units; + BOOL okay = SetWaitableTimer (/* The timer to modify */ timer, + /* The time after which it will fire */ &due_time, + /* Interval period 0 = only fire once */ 0, + /* No completion routine */ NULL, + /* No arg for no completion routine */ NULL, + /* Wake up the system if it goes to sleep */ true); + if (!okay) { + // Failed to set the timer. Hmm? + retc = GetLastError (); + goto done; + } + // Do the actual wait + DWORD rc = WaitForSingleObject (timer, INFINITE); + if (rc == WAIT_FAILED) { + // Executing the wait operation failed. + retc = GetLastError (); + goto done; + } + // Check for success: + mlib_check (rc, eq, WAIT_OBJECT_0); +done: + // Done with the timer. + if (timer) { + CloseHandle (timer); + } + return retc; +#else +#error "mlib_this_thread_sleep_for" is not implemented on this platform. +#endif +} + +/** + * @brief Pause the calling thread until the given time point has been reached + * + * @param when The time point at which to resume, at soonest + * @return int A possible error code for the operation. Returns zero upon success. + * + * The `when` is the *soonest* successful wake time. The thread may wake at a later time. + */ +static inline int +mlib_this_thread_sleep_until (const mlib_time_point when) mlib_noexcept +{ + const mlib_duration time_until = mlib_time_difference (when, mlib_now ()); + return mlib_this_thread_sleep_for (time_until); +} + +mlib_extern_c_end (); + +#endif // MLIB_TIME_POINT_H_INCLUDED diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 3f2535be6a..ae21b1bb91 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -960,6 +961,45 @@ _test_duration (void) mlib_check (ts.tv_nsec, eq, -908000); } +static void +_test_time_point (void) +{ + mlib_time_point t = mlib_now (); + + // Offset the time point + mlib_time_point later = mlib_later (t, mlib_seconds (1)); + mlib_check (mlib_time_cmp (t, later) < 0); + + // Difference between two time points is a duration: + mlib_duration diff = mlib_time_difference (later, t); + mlib_check (mlib_milliseconds_count (diff), eq, 1000); + + // The time is only ever monotonically increasing + mlib_foreach_urange (i, 10000) { + (void) i; + mlib_check (mlib_time_cmp (t, mlib_now ()) <= 0); + t = mlib_now (); + } +} + +static void +_test_sleep (void) +{ + mlib_time_point start = mlib_now (); + int rc = mlib_this_thread_sleep_for (mlib_microseconds (10)); + mlib_check (rc, eq, 0); + mlib_duration t = mlib_time_difference (mlib_now (), start); + mlib_check (mlib_microseconds_count (t) >= 10); + + // Sleeping for a negative duration returns immediately with success + start = mlib_now (); + mlib_check (mlib_this_thread_sleep_for (mlib_seconds (-10)), eq, 0); + mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); + + // Sleeping until a point in the past returns immediately as well + mlib_check (mlib_this_thread_sleep_until (start), eq, 0); + mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); +} void test_mlib_install (TestSuite *suite) @@ -977,6 +1017,8 @@ test_mlib_install (TestSuite *suite) TestSuite_Add (suite, "/mlib/ckdint-partial", _test_ckdint_partial); TestSuite_Add (suite, "/mlib/str_view", _test_str_view); TestSuite_Add (suite, "/mlib/duration", _test_duration); + TestSuite_Add (suite, "/mlib/time_point", _test_time_point); + TestSuite_Add (suite, "/mlib/sleep", _test_sleep); } mlib_diagnostic_pop (); From a69e1979b0fa61d94be31559a6859cfc28a7ac20 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 16 Apr 2025 16:41:11 -0600 Subject: [PATCH 04/66] [fixup] Fix preproc error in #error directive --- src/common/src/mlib/time_point.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 35de1a7212..695f8528ad 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -70,7 +70,7 @@ mlib_now (void) mlib_noexcept ret._time_since_monotonic_start = mlib_milliseconds (ms); return ret; #else -#error We don't know how to get the current time on this platform +#error We do not know how to get the current time on this platform #endif } From 381aba5c246e19d237b90952f147aa3d3ad76b30 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 16 Apr 2025 16:38:46 -0600 Subject: [PATCH 05/66] Convert all sleeps to use duration-typed sleeps --- src/common/tests/test-mlib.c | 5 +-- src/libmongoc/src/mongoc/mongoc-async.c | 5 ++- .../src/mongoc/mongoc-client-private.h | 2 ++ .../src/mongoc/mongoc-client-session.c | 5 ++- src/libmongoc/src/mongoc/mongoc-client.c | 2 ++ src/libmongoc/src/mongoc/mongoc-crypt.c | 7 ++-- .../src/mongoc/mongoc-util-private.h | 3 -- src/libmongoc/src/mongoc/mongoc-util.c | 24 ++----------- src/libmongoc/tests/TestSuite.h | 4 ++- src/libmongoc/tests/json-test-operations.c | 15 ++++++-- src/libmongoc/tests/mock_server/mock-server.c | 7 ++-- src/libmongoc/tests/test-happy-eyeballs.c | 11 ++++-- .../tests/test-mongoc-background-monitoring.c | 28 +++++++++------ src/libmongoc/tests/test-mongoc-client-pool.c | 8 ++++- .../tests/test-mongoc-client-session.c | 13 +++++-- .../test-mongoc-client-side-encryption.c | 10 +++++- src/libmongoc/tests/test-mongoc-client.c | 19 ++++++---- src/libmongoc/tests/test-mongoc-cluster.c | 14 ++++++-- src/libmongoc/tests/test-mongoc-counters.c | 12 +++++-- src/libmongoc/tests/test-mongoc-dns.c | 7 ++-- src/libmongoc/tests/test-mongoc-interrupt.c | 10 +++++- .../tests/test-mongoc-max-staleness.c | 15 ++++++-- .../tests/test-mongoc-primary-stepdown.c | 2 +- .../tests/test-mongoc-sample-commands.c | 35 ++++++++++-------- .../tests/test-mongoc-sdam-monitoring.c | 2 +- src/libmongoc/tests/test-mongoc-sdam.c | 10 ++++-- src/libmongoc/tests/test-mongoc-socket.c | 8 +++-- .../tests/test-mongoc-speculative-auth.c | 6 +++- .../tests/test-mongoc-stream-tls-error.c | 5 ++- .../tests/test-mongoc-topology-reconcile.c | 12 ++++++- .../tests/test-mongoc-topology-scanner.c | 21 ++++++++--- src/libmongoc/tests/test-mongoc-topology.c | 36 ++++++++++++------- src/libmongoc/tests/test-mongoc-usleep.c | 15 -------- .../tests/test-mongoc-with-transaction.c | 9 ++++- src/libmongoc/tests/unified/operation.c | 6 ++-- 35 files changed, 263 insertions(+), 130 deletions(-) diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index ae21b1bb91..9aa46f7982 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -986,10 +986,11 @@ static void _test_sleep (void) { mlib_time_point start = mlib_now (); - int rc = mlib_this_thread_sleep_for (mlib_microseconds (10)); + int rc = mlib_this_thread_sleep_for (mlib_milliseconds (50)); mlib_check (rc, eq, 0); mlib_duration t = mlib_time_difference (mlib_now (), start); - mlib_check (mlib_microseconds_count (t) >= 10); + mlib_check (mlib_milliseconds_count (t) >= 50); + mlib_check (mlib_milliseconds_count (t) < 200); // Sleeping for a negative duration returns immediately with success start = mlib_now (); diff --git a/src/libmongoc/src/mongoc/mongoc-async.c b/src/libmongoc/src/mongoc/mongoc-async.c index b42f8feead..4c652f48f3 100644 --- a/src/libmongoc/src/mongoc/mongoc-async.c +++ b/src/libmongoc/src/mongoc/mongoc-async.c @@ -26,6 +26,9 @@ #include +#include +#include + mongoc_async_t * mongoc_async_new (void) @@ -124,7 +127,7 @@ mongoc_async_run (mongoc_async_t *async) } else { /* currently this does not get hit. we always have at least one command * initialized with a stream. */ - _mongoc_usleep (poll_timeout_msec * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (poll_timeout_msec)); } if (nactive > 0) { diff --git a/src/libmongoc/src/mongoc/mongoc-client-private.h b/src/libmongoc/src/mongoc/mongoc-client-private.h index 29a71bd4c5..199f545b76 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-private.h +++ b/src/libmongoc/src/mongoc/mongoc-client-private.h @@ -16,6 +16,8 @@ #include +#include + #ifndef MONGOC_CLIENT_PRIVATE_H #define MONGOC_CLIENT_PRIVATE_H diff --git a/src/libmongoc/src/mongoc/mongoc-client-session.c b/src/libmongoc/src/mongoc/mongoc-client-session.c index cea5c8137b..dbb9543c7c 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-session.c +++ b/src/libmongoc/src/mongoc/mongoc-client-session.c @@ -26,6 +26,9 @@ #include #include +#include +#include + #define WITH_TXN_TIMEOUT_MS (120 * 1000) static void @@ -1172,7 +1175,7 @@ mongoc_client_session_commit_transaction (mongoc_client_session_t *session, bson /* Waste the test timeout, if there is one set. */ if (session->with_txn_timeout_ms) { - _mongoc_usleep (session->with_txn_timeout_ms * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (session->with_txn_timeout_ms)); } RETURN (r); diff --git a/src/libmongoc/src/mongoc/mongoc-client.c b/src/libmongoc/src/mongoc/mongoc-client.c index 3a92c9cdbf..69cc36e6ec 100644 --- a/src/libmongoc/src/mongoc/mongoc-client.c +++ b/src/libmongoc/src/mongoc/mongoc-client.c @@ -17,6 +17,8 @@ #include #include + +#include #ifdef MONGOC_HAVE_DNSAPI /* for DnsQuery_UTF8 */ #include diff --git a/src/libmongoc/src/mongoc/mongoc-crypt.c b/src/libmongoc/src/mongoc/mongoc-crypt.c index a884bf6713..9fb80e300f 100644 --- a/src/libmongoc/src/mongoc/mongoc-crypt.c +++ b/src/libmongoc/src/mongoc/mongoc-crypt.c @@ -35,7 +35,10 @@ #include #include +#include #include +#include +#include #include @@ -586,9 +589,7 @@ _state_need_kms (_state_machine_t *state_machine, bson_error_t *error) } sleep_usec = mongocrypt_kms_ctx_usleep (kms_ctx); - if (sleep_usec > 0) { - _mongoc_usleep (sleep_usec); - } + mlib_this_thread_sleep_for (mlib_microseconds (sleep_usec)); mongoc_stream_destroy (tls_stream); tls_stream = _get_stream (endpoint, sockettimeout, ssl_opt, error); diff --git a/src/libmongoc/src/mongoc/mongoc-util-private.h b/src/libmongoc/src/mongoc/mongoc-util-private.h index f42997959b..2348e9621e 100644 --- a/src/libmongoc/src/mongoc/mongoc-util-private.h +++ b/src/libmongoc/src/mongoc/mongoc-util-private.h @@ -62,9 +62,6 @@ _mongoc_rand_simple (unsigned int *seed); char * _mongoc_hex_md5 (const char *input); -void -_mongoc_usleep (int64_t usec); - /* Get the current time as a number of milliseconds since the Unix Epoch. */ int64_t _mongoc_get_real_time_ms (void); diff --git a/src/libmongoc/src/mongoc/mongoc-util.c b/src/libmongoc/src/mongoc/mongoc-util.c index dab7f71c2b..1fceef483d 100644 --- a/src/libmongoc/src/mongoc/mongoc-util.c +++ b/src/libmongoc/src/mongoc/mongoc-util.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include +#include #ifdef _WIN32 #define _CRT_RAND_S #endif @@ -107,27 +109,7 @@ mongoc_usleep_default_impl (int64_t usec, void *user_data) { BSON_UNUSED (user_data); -#ifdef _WIN32 - LARGE_INTEGER ft; - HANDLE timer; - - BSON_ASSERT (usec >= 0); - - ft.QuadPart = -(10 * usec); - timer = CreateWaitableTimer (NULL, true, NULL); - SetWaitableTimer (timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject (timer, INFINITE); - CloseHandle (timer); -#else - BSON_ASSERT (usec >= 0); - usleep ((useconds_t) usec); -#endif -} - -void -_mongoc_usleep (int64_t usec) -{ - mongoc_usleep_default_impl (usec, NULL); + mlib_this_thread_sleep_for (mlib_microseconds (usec)); } diff --git a/src/libmongoc/tests/TestSuite.h b/src/libmongoc/tests/TestSuite.h index 02d37231c6..78712112e4 100644 --- a/src/libmongoc/tests/TestSuite.h +++ b/src/libmongoc/tests/TestSuite.h @@ -25,6 +25,8 @@ #include +#include + #include #include #include @@ -577,7 +579,7 @@ _test_error (const char *format, ...) BSON_GNUC_PRINTF (1, 2); BSON_FUNC); \ abort (); \ } \ - _mongoc_usleep (10 * 1000); \ + mlib_this_thread_sleep_for (mlib_milliseconds (10)); \ } \ } while (0) diff --git a/src/libmongoc/tests/json-test-operations.c b/src/libmongoc/tests/json-test-operations.c index 6532d885c7..dc9ddc42ef 100644 --- a/src/libmongoc/tests/json-test-operations.c +++ b/src/libmongoc/tests/json-test-operations.c @@ -17,6 +17,12 @@ #include +#include "./TestSuite.h" +#include "./json-test-operations.h" +#include "./json-test.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include #include @@ -32,6 +38,9 @@ #include +#include +#include + #include #include #include @@ -2071,7 +2080,7 @@ wait_for_event (json_test_ctx_t *ctx, const bson_t *operation) test_error ("Unknown event: %s", event_name); } if (!satisfied) { - _mongoc_usleep (WAIT_FOR_EVENT_TICK_MS * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (WAIT_FOR_EVENT_TICK_MS)); } } @@ -2109,7 +2118,7 @@ wait_for_primary_change (json_test_ctx_t *ctx, const bson_t *operation) bson_mutex_unlock (&ctx->mutex); if (!satisfied) { - _mongoc_usleep (10 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (10)); } } @@ -2419,7 +2428,7 @@ json_test_operation (json_test_ctx_t *ctx, mongoc_client_destroy (client); } else if (!strcmp (op_name, "wait")) { - _mongoc_usleep (bson_lookup_int32 (operation, "arguments.ms") * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (bson_lookup_int32 (operation, "arguments.ms"))); } else if (!strcmp (op_name, "recordPrimary")) { /* It doesn't matter who the primary is. We just want to assert in * tests later that the primary changed x times after this operation. diff --git a/src/libmongoc/tests/mock_server/mock-server.c b/src/libmongoc/tests/mock_server/mock-server.c index 52a16578de..cb474006bb 100644 --- a/src/libmongoc/tests/mock_server/mock-server.c +++ b/src/libmongoc/tests/mock_server/mock-server.c @@ -33,7 +33,9 @@ #include #include +#include #include +#include #ifdef BSON_HAVE_STRINGS_H #include @@ -553,7 +555,8 @@ auto_hello (request_t *request, void *data) response_json = bson_as_relaxed_extended_json (&response, 0); if (mock_server_get_rand_delay (request->server)) { - _mongoc_usleep ((int64_t) (rand () % 10) * 1000); + const int random_sleep = rand () % 10; + mlib_this_thread_sleep_for (mlib_milliseconds (random_sleep)); } reply_to_request (request, MONGOC_REPLY_NONE, 0, 0, 1, response_json); @@ -1629,7 +1632,7 @@ mock_server_destroy (mock_server_t *server) } bson_mutex_unlock (&server->mutex); - _mongoc_usleep (1000); + mlib_this_thread_sleep_for (mlib_milliseconds (1)); } bson_mutex_lock (&server->mutex); diff --git a/src/libmongoc/tests/test-happy-eyeballs.c b/src/libmongoc/tests/test-happy-eyeballs.c index dc8260e885..bd01ad1dec 100644 --- a/src/libmongoc/tests/test-happy-eyeballs.c +++ b/src/libmongoc/tests/test-happy-eyeballs.c @@ -1,3 +1,7 @@ +#include "./TestSuite.h" +#include "./mock_server/mock-server.h" +#include "./test-libmongoc.h" + #include #include #include @@ -8,6 +12,9 @@ #include #include +#include +#include + #include #include #include @@ -139,7 +146,7 @@ _mock_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t timeout) /* if there were active poll responses which were all silenced, * sleep for a little while since subsequent calls to poll may not have * any delay. */ - _mongoc_usleep (5 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (5)); } return nactive; } @@ -391,7 +398,7 @@ test_happy_eyeballs_dns_cache (void) mongoc_topology_scanner_node_disconnect (testcase.state.ts->nodes, false); /* wait for DNS cache to expire. */ - _mongoc_usleep (2000 * 1000); + mlib_this_thread_sleep_for (mlib_seconds (2)); /* after running once, the topology scanner should have cached the DNS * result for IPv6. It should complete immediately. */ diff --git a/src/libmongoc/tests/test-mongoc-background-monitoring.c b/src/libmongoc/tests/test-mongoc-background-monitoring.c index 241155a2bd..66c0537322 100644 --- a/src/libmongoc/tests/test-mongoc-background-monitoring.c +++ b/src/libmongoc/tests/test-mongoc-background-monitoring.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "./mock_server/mock-server.h" + #include #include #include @@ -25,6 +27,9 @@ #include +#include +#include + #include #include #include @@ -294,7 +299,8 @@ tf_destroy (test_fixture_t *tf) * Used to make observations that a scan doesn't occur when a test fixture is * configured with a faster heartbeat. */ -#define WAIT_TWO_MIN_HEARTBEAT_MS _mongoc_usleep (2 * FAST_HEARTBEAT_MS * 1000) +#define WAIT_TWO_MIN_HEARTBEAT_MS() \ + mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (FAST_HEARTBEAT_MS), 2)) static void _signal_shutdown (test_fixture_t *tf) @@ -404,7 +410,7 @@ test_connect_hangup (void) OBSERVE (tf, !tf->observations->awaited); /* No retry occurs since the server was never discovered. */ - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); OBSERVE (tf, tf->observations->n_heartbeat_started == 1); tf_destroy (tf); } @@ -429,7 +435,7 @@ test_connect_badreply (void) OBSERVE_SOON (tf, tf->observations->sd_type == MONGOC_SERVER_UNKNOWN); /* No retry occurs since the server was never discovered. */ - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); OBSERVE (tf, tf->observations->n_heartbeat_started == 1); tf_destroy (tf); } @@ -475,7 +481,7 @@ test_connect_requestscan (void) /* Because the request occurred during the scan, no subsequent scan occurs. */ - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); OBSERVE (tf, tf->observations->n_heartbeat_started == 1); OBSERVE (tf, tf->observations->n_heartbeat_succeeded == 1); OBSERVE (tf, tf->observations->n_heartbeat_failed == 0); @@ -653,7 +659,7 @@ test_retry_shutdown (void) reply_to_request_with_ok_and_destroy (request); /* No retry occurs. */ - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); OBSERVE (tf, tf->observations->n_heartbeat_started == 2); OBSERVE (tf, tf->observations->n_heartbeat_succeeded == 2); OBSERVE (tf, tf->observations->n_heartbeat_failed == 0); @@ -703,7 +709,7 @@ test_repeated_requestscan (void) for (i = 0; i < 10; i++) { _request_scan (tf); } - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); OBSERVE (tf, tf->observations->n_heartbeat_started == 1); reply_to_request_with_ok_and_destroy (request); OBSERVE_SOON (tf, tf->observations->n_heartbeat_succeeded == 1); @@ -724,7 +730,7 @@ test_sleep_after_scan (void) OBSERVE (tf, tf->observations->n_heartbeat_started == 1); reply_to_request_with_ok_and_destroy (request); OBSERVE_SOON (tf, tf->observations->n_heartbeat_succeeded == 1); - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); /* No subsequent command send. */ OBSERVE (tf, tf->observations->n_heartbeat_started == 1); tf_destroy (tf); @@ -822,7 +828,7 @@ test_streaming_shutdown (void) OBSERVE (tf, tf->observations->n_heartbeat_started == 2); _signal_shutdown (tf); /* This should cancel the hello immediately. */ - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); /* No further hello commands should be sent. */ OBSERVE (tf, tf->observations->n_heartbeat_started == 2); request_destroy (request); @@ -849,7 +855,7 @@ test_streaming_cancel (void) OBSERVE_SOON (tf, tf->observations->n_server_changed == 1); /* The cancellation closes the connection and waits before creating a new * connection. Check that no new heartbeat was started. */ - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); OBSERVE (tf, tf->observations->n_heartbeat_started == 2); _request_scan (tf); /* The handshake will be handled by the auto responder. */ @@ -999,7 +1005,7 @@ test_moretocome_shutdown (void) * processing the last reply. Requesting shutdown cancels. */ _signal_shutdown (tf); - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); /* No further heartbeats are attempted. */ OBSERVE (tf, tf->observations->n_heartbeat_succeeded == 2); request_destroy (request); @@ -1035,7 +1041,7 @@ test_moretocome_cancel (void) /* The cancellation closes the connection and waits before creating a new * connection. Check that no new heartbeat was started. */ - WAIT_TWO_MIN_HEARTBEAT_MS; + WAIT_TWO_MIN_HEARTBEAT_MS (); OBSERVE (tf, tf->observations->n_heartbeat_started == 3); _request_scan (tf); /* The handshake will be handled by the auto responder. */ diff --git a/src/libmongoc/tests/test-mongoc-client-pool.c b/src/libmongoc/tests/test-mongoc-client-pool.c index f1004b1756..7d67a7863d 100644 --- a/src/libmongoc/tests/test-mongoc-client-pool.c +++ b/src/libmongoc/tests/test-mongoc-client-pool.c @@ -1,3 +1,6 @@ +#include "./TestSuite.h" +#include "./test-libmongoc.h" + #include // BEGIN_IGNORE_DEPRECATIONS #include #include @@ -5,6 +8,9 @@ #include +#include +#include + #include #include @@ -304,7 +310,7 @@ static BSON_THREAD_FUN (worker, arg) pool_timeout_args_t *args = arg; mongoc_client_t *client = mongoc_client_pool_pop (args->pool); BSON_ASSERT (client); - _mongoc_usleep (10); + mlib_this_thread_sleep_for (mlib_microseconds (10)); mongoc_client_pool_push (args->pool, client); bson_mutex_lock (&args->mutex); /* notify main thread that current thread has terminated */ diff --git a/src/libmongoc/tests/test-mongoc-client-session.c b/src/libmongoc/tests/test-mongoc-client-session.c index 373e0a8b59..d94160a7eb 100644 --- a/src/libmongoc/tests/test-mongoc-client-session.c +++ b/src/libmongoc/tests/test-mongoc-client-session.c @@ -1,3 +1,10 @@ +#include "./TestSuite.h" +#include "./json-test.h" +#include "./mock_server/future-functions.h" +#include "./mock_server/mock-server.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include // BEGIN_IGNORE_DEPRECATIONS #include #include @@ -7,7 +14,9 @@ #include #include +#include #include +#include #include #include @@ -229,7 +238,7 @@ _test_session_pool_timeout (bool pooled) mongoc_client_session_destroy (s); BSON_ASSERT (!mongoc_server_session_pool_is_empty (client->topology->session_pool)); - _mongoc_usleep (1500 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (1500)); /* getting a new client session must start a new server session */ s = mongoc_client_start_session (client, NULL, &error); @@ -313,7 +322,7 @@ _test_session_pool_reap (bool pooled) mongoc_client_session_destroy (a); BSON_ASSERT (!mongoc_server_session_pool_is_empty (client->topology->session_pool)); /* session is pooled */ - _mongoc_usleep (1500 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (1500)); /* * returning session B causes session A to be reaped diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index 91225dc587..397b751592 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -14,9 +14,17 @@ * limitations under the License. */ +#include "./json-test.h" +#include "./test-libmongoc.h" + #include #include +#include + +#include +#include + #include #include @@ -6053,7 +6061,7 @@ static BSON_THREAD_FUN (listen_socket, arg) // listen on socket r = mongoc_socket_listen (socket, 100); BSON_ASSERT (r == 0); - _mongoc_usleep (1000); // wait to see if received connection + mlib_this_thread_sleep_for (mlib_milliseconds (1)); mongoc_socket_t *ret = mongoc_socket_accept (socket, bson_get_monotonic_time () + 100); if (ret) { // not null received a connection and test should fail diff --git a/src/libmongoc/tests/test-mongoc-client.c b/src/libmongoc/tests/test-mongoc-client.c index 45722dd1d3..407c30be58 100644 --- a/src/libmongoc/tests/test-mongoc-client.c +++ b/src/libmongoc/tests/test-mongoc-client.c @@ -9,6 +9,9 @@ #include +#include +#include + #include #ifdef MONGOC_ENABLE_SSL #include @@ -798,7 +801,7 @@ test_wire_version (void) WIRE_VERSION_MIN - 1); /* wait until it's time for next heartbeat */ - _mongoc_usleep (600 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (600)); sd = mongoc_client_select_server (client, true, NULL, &error); BSON_ASSERT (!sd); BSON_ASSERT (error.domain == MONGOC_ERROR_PROTOCOL); @@ -814,7 +817,7 @@ test_wire_version (void) WIRE_VERSION_MAX); /* wait until it's time for next heartbeat */ - _mongoc_usleep (600 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (600)); sd = mongoc_client_select_server (client, true, NULL, &error); ASSERT_OR_PRINT (sd, error); mongoc_server_description_destroy (sd); @@ -2214,7 +2217,7 @@ test_mongoc_client_descriptions_pooled (void *unused) /* wait for background thread to discover all members */ start = bson_get_monotonic_time (); do { - _mongoc_usleep (1000); + mlib_this_thread_sleep_for (mlib_milliseconds (1)); /* Windows IPv4 tasks may take longer to connect since connection to the * first address returned by getaddrinfo may be IPv6, and failure to * connect may take a couple seconds. See CDRIVER-3639. */ @@ -2468,7 +2471,7 @@ _test_mongoc_client_select_server_retry (bool retry_succeeds) mongoc_server_description_destroy (sd); /* let socketCheckIntervalMS pass */ - _mongoc_usleep (100 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (100)); /* second selection requires ping, which fails */ future = future_client_select_server (client, true, NULL, &error); @@ -2551,7 +2554,7 @@ _test_mongoc_client_fetch_stream_retry (bool retry_succeeds) future_destroy (future); /* let socketCheckIntervalMS pass */ - _mongoc_usleep (100 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (100)); /* second selection requires ping, which fails */ future = future_client_command_simple (client, "db", tmp_bson ("{'cmd': 1}"), NULL, NULL, &error); @@ -2828,7 +2831,7 @@ _force_hello_with_ping (mongoc_client_t *client, int heartbeat_ms) BSON_ASSERT_PARAM (client); /* Wait until we're overdue to send a hello */ - _mongoc_usleep (heartbeat_ms * 2 * 1000); + mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (heartbeat_ms), 2)); /* Send a ping */ future = future_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, NULL); @@ -2986,7 +2989,9 @@ _test_client_sends_handshake (bool pooled) /* We're in cooldown for the next few seconds, so we're not * allowed to send hellos. Wait for the cooldown to end. */ - _mongoc_usleep ((MONGOC_TOPOLOGY_COOLDOWN_MS + 1000) * 1000); + mlib_this_thread_sleep_for ( // + mlib_duration_add (mlib_milliseconds (MONGOC_TOPOLOGY_COOLDOWN_MS), // + mlib_seconds (1))); future = _force_hello_with_ping (client, heartbeat_ms); } diff --git a/src/libmongoc/tests/test-mongoc-cluster.c b/src/libmongoc/tests/test-mongoc-cluster.c index 255397dd64..39c332d3b2 100644 --- a/src/libmongoc/tests/test-mongoc-cluster.c +++ b/src/libmongoc/tests/test-mongoc-cluster.c @@ -1,3 +1,10 @@ +#include "./TestSuite.h" +#include "./mock_server/future-functions.h" +#include "./mock_server/future.h" +#include "./mock_server/mock-server.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include #include @@ -7,6 +14,9 @@ #include +#include +#include + #include #include #include @@ -500,7 +510,7 @@ _test_cluster_time (bool pooled, command_fn_t command) client = mongoc_client_pool_pop (pool); /* CDRIVER-3596 - prevent client discovery of the pool interfering with * the test operations. */ - _mongoc_usleep (5000 * 1000); /* 5 s */ + mlib_this_thread_sleep_for (mlib_seconds (5)); } else { client = test_framework_new_default_client (); mongoc_client_set_apm_callbacks (client, callbacks, &cluster_time_test); @@ -862,7 +872,7 @@ _test_cluster_time_comparison (bool pooled) mongoc_client_pool_destroy (pool); } else { /* trigger next heartbeat, it should contain newest cluster time */ - _mongoc_usleep (750 * 1000); /* 750 ms */ + mlib_this_thread_sleep_for (mlib_milliseconds (750)); future = future_ping (client, &error); request = mock_server_receives_any_hello_with_match (server, "{'$clusterTime': " diff --git a/src/libmongoc/tests/test-mongoc-counters.c b/src/libmongoc/tests/test-mongoc-counters.c index aefe1a5439..1280293d2a 100644 --- a/src/libmongoc/tests/test-mongoc-counters.c +++ b/src/libmongoc/tests/test-mongoc-counters.c @@ -14,11 +14,19 @@ * limitations under the License. */ +#include "./TestSuite.h" +#include "./mock_server/future-functions.h" +#include "./mock_server/mock-server.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include #include #include +#include +#include #include #include @@ -456,7 +464,7 @@ test_counters_streams_timeout (void) reset_all_counters (); future = future_client_command_simple (client, "test", tmp_bson ("{'ping': 1}"), NULL, NULL, &err); request = mock_server_receives_msg (server, MONGOC_QUERY_NONE, tmp_bson ("{'ping': 1}")); - _mongoc_usleep (350); + mlib_this_thread_sleep_for (mlib_microseconds (350)); request_destroy (request); ret = future_get_bool (future); BSON_ASSERT (!ret); @@ -1221,7 +1229,7 @@ wait_for_background_threads (rpc_op_egress_counters expected) return; } - _mongoc_usleep (100000); // 100 ms. + mlib_this_thread_sleep_for (mlib_milliseconds (100)); current = rpc_op_egress_counters_current (); } diff --git a/src/libmongoc/tests/test-mongoc-dns.c b/src/libmongoc/tests/test-mongoc-dns.c index 20b1f3f433..17d0c8d93b 100644 --- a/src/libmongoc/tests/test-mongoc-dns.c +++ b/src/libmongoc/tests/test-mongoc-dns.c @@ -7,6 +7,9 @@ #include #include +#include +#include + #ifdef MONGOC_ENABLE_SSL #include @@ -791,7 +794,7 @@ _prose_test_update_srv_single (void *resource) client = resource; - _mongoc_usleep (2000 * RESCAN_INTERVAL_MS); + mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (RESCAN_INTERVAL_MS), 2)); /* Avoid ping given `loadBalanced=true`; see prose test 9. */ if (!mongoc_uri_get_option_as_bool (client->uri, MONGOC_URI_LOADBALANCED, false)) { @@ -804,7 +807,7 @@ _prose_test_update_srv_pooled (void *resource) { BSON_ASSERT_PARAM (resource); - _mongoc_usleep (2000 * RESCAN_INTERVAL_MS); + mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (RESCAN_INTERVAL_MS), 2)); } typedef struct { diff --git a/src/libmongoc/tests/test-mongoc-interrupt.c b/src/libmongoc/tests/test-mongoc-interrupt.c index 12f82754b6..f87369a0e5 100644 --- a/src/libmongoc/tests/test-mongoc-interrupt.c +++ b/src/libmongoc/tests/test-mongoc-interrupt.c @@ -14,12 +14,20 @@ * limitations under the License. */ +#include "./TestSuite.h" +#include "./mock_server/future.h" +#include "./mock_server/mock-server.h" +#include "./test-libmongoc.h" + #include #include #include #include +#include +#include + #include #include #include @@ -39,7 +47,7 @@ BSON_THREAD_FUN (_interrupt, future_void) future = future_void; interrupt = future_get_param (future, 0)->value.void_ptr_value; - _mongoc_usleep (10 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (10)); _mongoc_interrupt_interrupt (interrupt); return_value.type = future_value_void_type; future_resolve (future, return_value); diff --git a/src/libmongoc/tests/test-mongoc-max-staleness.c b/src/libmongoc/tests/test-mongoc-max-staleness.c index ffca735f71..b04b177451 100644 --- a/src/libmongoc/tests/test-mongoc-max-staleness.c +++ b/src/libmongoc/tests/test-mongoc-max-staleness.c @@ -1,8 +1,17 @@ +#include "./TestSuite.h" +#include "./json-test.h" +#include "./mock_server/future-functions.h" +#include "./mock_server/mock-server.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include #include +#include + #include #include #include @@ -223,15 +232,15 @@ _test_last_write_date (bool pooled) r = mongoc_collection_insert_one (collection, tmp_bson ("{}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - _mongoc_usleep (1000 * 1000); + mlib_this_thread_sleep_for (mlib_seconds (1)); s0 = mongoc_topology_select (client->topology, MONGOC_SS_WRITE, TEST_SS_LOG_CONTEXT, NULL, NULL, &error); ASSERT_OR_PRINT (s0, error); - _mongoc_usleep (1000 * 1000); + mlib_this_thread_sleep_for (mlib_seconds (1)); r = mongoc_collection_insert_one (collection, tmp_bson ("{}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - _mongoc_usleep (1000 * 1000); + mlib_this_thread_sleep_for (mlib_seconds (1)); s1 = mongoc_topology_select (client->topology, MONGOC_SS_WRITE, TEST_SS_LOG_CONTEXT, NULL, NULL, &error); ASSERT_OR_PRINT (s1, error); ASSERT_CMPINT64 (s1->last_write_date_ms, !=, (int64_t) -1); diff --git a/src/libmongoc/tests/test-mongoc-primary-stepdown.c b/src/libmongoc/tests/test-mongoc-primary-stepdown.c index bfabdf2873..917b2155e3 100644 --- a/src/libmongoc/tests/test-mongoc-primary-stepdown.c +++ b/src/libmongoc/tests/test-mongoc-primary-stepdown.c @@ -124,7 +124,7 @@ _run_test_single_or_pooled (_test_fn_t test, bool use_pooled) _setup_test_with_client (client); /* Wait one second to be assured that the RTT connection has been * established as well. */ - _mongoc_usleep (1000 * 1000); + mlib_this_thread_sleep_for (mlib_seconds (1)); test (client); mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); diff --git a/src/libmongoc/tests/test-mongoc-sample-commands.c b/src/libmongoc/tests/test-mongoc-sample-commands.c index a0d7f05cde..953fa47033 100644 --- a/src/libmongoc/tests/test-mongoc-sample-commands.c +++ b/src/libmongoc/tests/test-mongoc-sample-commands.c @@ -33,6 +33,11 @@ #include #include #include +#include "./TestSuite.h" +#include +#include +#include "./test-libmongoc.h" +#include "./test-conveniences.h" typedef void (*sample_command_fn_t) (mongoc_database_t *db); @@ -816,7 +821,7 @@ test_example_20 (mongoc_database_t *db) } /* End Example 20 */ -done: +done: /* Start Example 20 Post */ bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); @@ -2889,7 +2894,7 @@ BSON_THREAD_FUN (insert_docs, p) } bson_mutex_unlock (&ctx->lock); - _mongoc_usleep (100 * 1000); /* 100 ms */ + mlib_this_thread_sleep_for (mlib_milliseconds (100)); } } @@ -3050,7 +3055,7 @@ test_sample_causal_consistency (mongoc_client_t *client) uint32_t increment; bson_error_t error; bool res; - + ASSERT (client); if (!test_framework_skip_if_no_txns ()) { @@ -3441,12 +3446,12 @@ test_sample_projection_with_aggregation_expressions (mongoc_database_t *db) opts = BCON_NEW ("projection", "{", "_id", BCON_INT32(0), "item", BCON_INT32(1), - "status", "{", - "$switch", "{", - "branches", "[", + "status", "{", + "$switch", "{", + "branches", "[", "{", "case", "{", - "$eq", "[", + "$eq", "[", "$status", BCON_UTF8("A"), "]", "}", @@ -3454,7 +3459,7 @@ test_sample_projection_with_aggregation_expressions (mongoc_database_t *db) "}", "{", "case", "{", - "$eq", "[", + "$eq", "[", "$status", BCON_UTF8("D"), "]", "}", @@ -3464,11 +3469,11 @@ test_sample_projection_with_aggregation_expressions (mongoc_database_t *db) "default", BCON_UTF8("No status found"), "}", "}", - "area", "{", - "$concat", "[", - "{", - "$toString", "{", - "$multiply", "[", + "area", "{", + "$concat", "[", + "{", + "$toString", "{", + "$multiply", "[", BCON_UTF8("$size.h"), BCON_UTF8("$size.w"), "]", @@ -3478,7 +3483,7 @@ test_sample_projection_with_aggregation_expressions (mongoc_database_t *db) BCON_UTF8("$size.uom"), "]", "}", - "reportNumber", "{", + "reportNumber", "{", "$literal", BCON_INT32(1), "}", "}"); @@ -3635,7 +3640,7 @@ insert_employee (mongoc_client_t *client, int employee) mongoc_collection_t *events; bson_error_t error; bool r; - + ASSERT (client); employees = mongoc_client_get_collection (client, "hr", "employees"); diff --git a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c index 0365b48e33..66beb97202 100644 --- a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c +++ b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c @@ -1073,7 +1073,7 @@ smm_wait (smm_t *t, size_t count) if (now - started > 10 * 1000 * 1000) { break; } - _mongoc_usleep (500 * 1000); // Sleep for 500ms. + mlib_this_thread_sleep_for (mlib_milliseconds (500)); } return false; } diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index d6924944ab..b85e13dbf6 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -1,3 +1,6 @@ +#include "./json-test.h" +#include "./test-libmongoc.h" + #include #include #include @@ -7,6 +10,9 @@ #include +#include +#include + #include #include @@ -796,7 +802,7 @@ test_prose_rtt (void *unused) /* Sleep for RTT_TEST_INITIAL_SLEEP_SEC seconds to allow multiple heartbeats * to succeed. */ - _mongoc_usleep (RTT_TEST_INITIAL_SLEEP_SEC * 1000 * 1000); + mlib_this_thread_sleep_for (mlib_seconds (RTT_TEST_INITIAL_SLEEP_SEC)); /* Set a failpoint to make hello commands take longer. */ bson_init (&cmd); @@ -834,7 +840,7 @@ test_prose_rtt (void *unused) satisfied = true; } mongoc_server_description_destroy (sd); - _mongoc_usleep (RTT_TEST_TICK_MS * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (RTT_TEST_TICK_MS)); } if (!satisfied) { diff --git a/src/libmongoc/tests/test-mongoc-socket.c b/src/libmongoc/tests/test-mongoc-socket.c index d9626a4eb2..6ce1f71eb4 100644 --- a/src/libmongoc/tests/test-mongoc-socket.c +++ b/src/libmongoc/tests/test-mongoc-socket.c @@ -1,3 +1,5 @@ +#include "./test-libmongoc.h" + #include #include #include @@ -6,6 +8,8 @@ #include #include +#include +#include #include #include @@ -77,7 +81,7 @@ static BSON_THREAD_FUN (socket_test_server, data_) strcpy (buf, "pong"); - _mongoc_usleep (data->server_sleep_ms * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (data->server_sleep_ms)); r = mongoc_stream_writev (stream, &iov, 1, TIMEOUT); /* if we sleep the client times out, else assert the client reads the data */ @@ -157,7 +161,7 @@ static BSON_THREAD_FUN (socket_test_client, data_) start = bson_get_monotonic_time (); while (!mongoc_stream_check_closed (stream)) { ASSERT_CMPINT64 (bson_get_monotonic_time (), <, start + 1000 * 1000); - _mongoc_usleep (1000); + mlib_this_thread_sleep_for (mlib_milliseconds (1)); } BSON_ASSERT (!mongoc_stream_timed_out (stream)); } else { diff --git a/src/libmongoc/tests/test-mongoc-speculative-auth.c b/src/libmongoc/tests/test-mongoc-speculative-auth.c index 1a83c82a79..e33676f0ab 100644 --- a/src/libmongoc/tests/test-mongoc-speculative-auth.c +++ b/src/libmongoc/tests/test-mongoc-speculative-auth.c @@ -15,6 +15,9 @@ */ #include + +#include +#include #ifdef _POSIX_VERSION #include #endif @@ -101,7 +104,8 @@ _auto_hello_without_speculative_auth (request_t *request, void *data) quotes_replaced = single_quotes_to_double (response_json); if (mock_server_get_rand_delay (request->server)) { - _mongoc_usleep ((int64_t) (rand () % 10) * 1000); + int rand_ms = rand () % 10; + mlib_this_thread_sleep_for (mlib_milliseconds (rand_ms)); } reply_to_request (request, MONGOC_REPLY_NONE, 0, 0, 1, response_json); diff --git a/src/libmongoc/tests/test-mongoc-stream-tls-error.c b/src/libmongoc/tests/test-mongoc-stream-tls-error.c index c4c68a0f7a..e6746f639f 100644 --- a/src/libmongoc/tests/test-mongoc-stream-tls-error.c +++ b/src/libmongoc/tests/test-mongoc-stream-tls-error.c @@ -4,6 +4,9 @@ #include #include +#include +#include + #ifdef MONGOC_ENABLE_SSL_OPENSSL #include #endif @@ -77,7 +80,7 @@ static BSON_THREAD_FUN (ssl_error_server, ptr) switch (data->behavior) { case SSL_TEST_BEHAVIOR_STALL_BEFORE_HANDSHAKE: - _mongoc_usleep (data->handshake_stall_ms * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (data->handshake_stall_ms)); break; case SSL_TEST_BEHAVIOR_HANGUP_AFTER_HANDSHAKE: r = mongoc_stream_tls_handshake_block (ssl_stream, data->host, TIMEOUT, &error); diff --git a/src/libmongoc/tests/test-mongoc-topology-reconcile.c b/src/libmongoc/tests/test-mongoc-topology-reconcile.c index 25e9d6edc1..1aa96fdd23 100644 --- a/src/libmongoc/tests/test-mongoc-topology-reconcile.c +++ b/src/libmongoc/tests/test-mongoc-topology-reconcile.c @@ -1,3 +1,10 @@ +#include "./TestSuite.h" +#include "./mock_server/future-functions.h" +#include "./mock_server/future.h" +#include "./mock_server/mock-server.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include #include @@ -6,6 +13,9 @@ #include #include +#include +#include + #include #include #include @@ -248,7 +258,7 @@ _test_topology_reconcile_sharded (bool pooled) request_destroy (request); /* make sure the mongos response is processed first */ - _mongoc_usleep (1000 * 1000); + mlib_this_thread_sleep_for (mlib_seconds (1)); /* replica set secondary - topology removes it */ request = mock_server_receives_any_hello (secondary); diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index 809a055193..9aba9118e2 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -1,3 +1,11 @@ +#include "./TestSuite.h" +#include "./mock_server/future-functions.h" +#include "./mock_server/future.h" +#include "./mock_server/mock-rs.h" +#include "./mock_server/mock-server.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include #include @@ -8,6 +16,9 @@ #include #include +#include +#include + #include #include #include @@ -185,7 +196,7 @@ test_topology_scanner_discovery (void) request_destroy (request); /* let client process that response */ - _mongoc_usleep (250 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (250)); /* a check of the secondary is scheduled in this scan */ request = mock_server_receives_any_hello (secondary); @@ -259,13 +270,13 @@ test_topology_scanner_oscillate (void) request_destroy (request); /* let client process that response */ - _mongoc_usleep (250 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (250)); request = mock_server_receives_any_hello (server1); reply_to_request_simple (request, server1_response); /* we don't schedule another check of server0 */ - _mongoc_usleep (250 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (250)); BSON_ASSERT (!future_get_mongoc_server_description_ptr (future)); BSON_ASSERT (scanner->async->ncmds == 0); @@ -348,7 +359,7 @@ slow_initiator (const mongoc_uri_t *uri, const mongoc_host_list_t *host, void *u data = (initiator_data_t *) user_data; if (host->port == data->slow_port) { - _mongoc_usleep (500 * 1000); /* 500 ms is longer than connectTimeoutMS */ + mlib_this_thread_sleep_for (mlib_milliseconds (500)); /* 500 ms is longer than connectTimeoutMS */ } return mongoc_client_default_stream_initiator (uri, host, data->client, err); @@ -659,7 +670,7 @@ _test_topology_scanner_does_not_renegotiate (bool pooled) r = mongoc_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - _mongoc_usleep (1500 * 1000); /* 1.5 seconds */ + mlib_this_thread_sleep_for (mlib_milliseconds (1500)); r = mongoc_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); diff --git a/src/libmongoc/tests/test-mongoc-topology.c b/src/libmongoc/tests/test-mongoc-topology.c index 36adf70e9b..b67d1ebee2 100644 --- a/src/libmongoc/tests/test-mongoc-topology.c +++ b/src/libmongoc/tests/test-mongoc-topology.c @@ -1,3 +1,9 @@ +#include "./mock_server/future-functions.h" +#include "./mock_server/future.h" +#include "./mock_server/mock-server.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include #include @@ -8,6 +14,9 @@ #include +#include +#include + #include #include #include @@ -389,7 +398,8 @@ _test_server_selection (bool try_once) BSON_ASSERT (client->topology->stale); future_destroy (future); - _mongoc_usleep (510 * 1000); /* one heartbeat, plus a few milliseconds */ + /* one heartbeat, plus a few milliseconds */ + mlib_this_thread_sleep_for (mlib_milliseconds (510)); /* second selection, now we try hello again */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -713,7 +723,7 @@ test_cooldown_standalone (void) MONGOC_ERROR_SERVER_SELECTION_FAILURE, "No servers yet eligible for rescan"); - _mongoc_usleep (1000 * 1000); /* 1 second */ + mlib_this_thread_sleep_for (mlib_seconds (1)); /* third selection doesn't try to call hello: we're still in cooldown */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -726,7 +736,8 @@ test_cooldown_standalone (void) future_destroy (future); mock_server_set_request_timeout_msec (server, get_future_timeout_ms ()); - _mongoc_usleep (5100 * 1000); /* 5.1 seconds */ + // 5.1 seconds + mlib_this_thread_sleep_for (mlib_milliseconds (5100)); /* cooldown ends, now we try hello again, this time succeeding */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -830,7 +841,7 @@ test_cooldown_rs (void) BSON_ASSERT (!future_get_mongoc_server_description_ptr (future)); future_destroy (future); - _mongoc_usleep (1000 * 1000); /* 1 second */ + mlib_this_thread_sleep_for (mlib_seconds (1)); /* second selection doesn't try hello on server 1: it's in cooldown */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -848,7 +859,8 @@ test_cooldown_rs (void) BSON_ASSERT (!future_get_mongoc_server_description_ptr (future)); future_destroy (future); - _mongoc_usleep (5100 * 1000); /* 5.1 seconds. longer than 5 sec cooldown. */ + // 5.1 seconds, longer than the 5sec cooldown + mlib_this_thread_sleep_for (mlib_milliseconds (5100)); /* cooldown ends, now we try hello on server 1, this time succeeding */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -1230,7 +1242,7 @@ test_rtt (void *ctx) future = future_client_command_simple (client, "db", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); request = mock_server_receives_any_hello (server); - _mongoc_usleep (1000 * 1000); /* one second */ + mlib_this_thread_sleep_for (mlib_seconds (1)); reply_to_request ( request, MONGOC_REPLY_NONE, @@ -1410,7 +1422,7 @@ _test_hello_retry_single (bool hangup, size_t n_failures) receives_command (server, future); /* wait for the next server check */ - _mongoc_usleep (600 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (600)); /* start a {foo: 1} command, server check fails and retries immediately */ future = future_command (client, &error); @@ -1661,7 +1673,7 @@ test_incompatible_error (void) WIRE_VERSION_MAX + 1); /* wait until it's time for next heartbeat */ - _mongoc_usleep (600 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (600)); ASSERT (!mongoc_client_command_simple ( client, "admin", tmp_bson ("{'" HANDSHAKE_CMD_LEGACY_HELLO "': 1}"), NULL, NULL, &error)); @@ -1940,7 +1952,7 @@ _test_request_scan_on_error ( /* a scan is requested immediately. wait for the scan to finish. */ WAIT_UNTIL (checks_cmp (&checks, "n_started", '=', 4)); } else { - _mongoc_usleep (minHBMS * 2); + mlib_this_thread_sleep_for (mlib_duration_mul (mlib_microseconds (minHBMS), 2)); BSON_ASSERT (checks_cmp (&checks, "n_started", '=', 2)); } } else { @@ -2341,7 +2353,7 @@ _test_hello_ok (bool pooled) /* Send off another ping for non-pooled clients, making sure to wait long * enough to require another heartbeat. */ - _mongoc_usleep (600 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (600)); future = future_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); } @@ -2369,7 +2381,7 @@ _test_hello_ok (bool pooled) /* Send off another ping for non-pooled clients, making sure to wait long * enough to require another heartbeat. */ - _mongoc_usleep (600 * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (600)); future = future_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); } @@ -2483,7 +2495,7 @@ test_failure_to_setup_after_retry (void) } // Wait until ready for next topology scan. - _mongoc_usleep (overridden_heartbeat_ms * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (overridden_heartbeat_ms)); // Send another command. future = future_client_command_simple (client, "test", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); diff --git a/src/libmongoc/tests/test-mongoc-usleep.c b/src/libmongoc/tests/test-mongoc-usleep.c index 8b9d8d1a47..df741b06c9 100644 --- a/src/libmongoc/tests/test-mongoc-usleep.c +++ b/src/libmongoc/tests/test-mongoc-usleep.c @@ -10,19 +10,6 @@ #include -static void -test_mongoc_usleep_basic (void) -{ - int64_t start; - int64_t duration; - - start = bson_get_monotonic_time (); - _mongoc_usleep (50 * 1000); /* 50 ms */ - duration = bson_get_monotonic_time () - start; - ASSERT_CMPINT ((int) duration, >, 0); - ASSERT_CMPTIME ((int) duration, 200 * 1000); -} - static void custom_usleep_impl (int64_t usec, void *user_data) { @@ -31,7 +18,6 @@ custom_usleep_impl (int64_t usec, void *user_data) } } - // `test_mongoc_usleep_custom` tests a custom sleep function set in // `mongoc_client_set_usleep_impl` is applied when topology scanning sleeps. static void @@ -104,6 +90,5 @@ test_mongoc_usleep_custom (void) void test_usleep_install (TestSuite *suite) { - TestSuite_Add (suite, "/Sleep/basic", test_mongoc_usleep_basic); TestSuite_AddMockServerTest (suite, "/Sleep/custom", test_mongoc_usleep_custom); } diff --git a/src/libmongoc/tests/test-mongoc-with-transaction.c b/src/libmongoc/tests/test-mongoc-with-transaction.c index fb5205993b..3867121356 100644 --- a/src/libmongoc/tests/test-mongoc-with-transaction.c +++ b/src/libmongoc/tests/test-mongoc-with-transaction.c @@ -1,7 +1,14 @@ +#include "./json-test.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include #include +#include +#include + #include #include #include @@ -17,7 +24,7 @@ with_transaction_fail_transient_txn (mongoc_client_session_t *session, void *ctx BSON_UNUSED (ctx); BSON_UNUSED (error); - _mongoc_usleep (session->with_txn_timeout_ms * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (session->with_txn_timeout_ms)); *reply = bson_new (); BSON_APPEND_ARRAY_BUILDER_BEGIN (*reply, "errorLabels", &labels); diff --git a/src/libmongoc/tests/unified/operation.c b/src/libmongoc/tests/unified/operation.c index bec48aa551..d3e54a9d36 100644 --- a/src/libmongoc/tests/unified/operation.c +++ b/src/libmongoc/tests/unified/operation.c @@ -27,6 +27,8 @@ #include #include +#include +#include #include @@ -3326,7 +3328,7 @@ operation_wait (test_t *test, operation_t *op, result_t *result, bson_error_t *e bson_iter_init_find (&iter, op->arguments, "ms"); ASSERT (BSON_ITER_HOLDS_INT (&iter)); const int64_t sleep_msec = bson_iter_as_int64 (&iter); - _mongoc_usleep (sleep_msec * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (sleep_msec)); result_from_ok (result); return true; @@ -3626,7 +3628,7 @@ log_filter_hide_server_selection_operation (const mongoc_structured_log_entry_t static void _operation_hidden_wait (test_t *test, entity_t *client, const char *name) { - _mongoc_usleep (WAIT_FOR_EVENT_TICK_MS * 1000); + mlib_this_thread_sleep_for (mlib_milliseconds (WAIT_FOR_EVENT_TICK_MS)); // @todo Re-examine this once we have support for connection pools in the unified test // runner. Without pooling, all events we could be waiting on would be coming From 9fbc46344d50ac66d24cd203e7fe0ed6a7200155 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 16 Apr 2025 16:58:20 -0600 Subject: [PATCH 06/66] Support infix syntax for duration comparisons --- src/common/src/mlib/duration.h | 11 +++-------- src/common/tests/test-mlib.c | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 8895cf7d65..55b80af01a 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -222,14 +222,9 @@ mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept return mlib_cmp (a._rep, b._rep); } -/** - * @brief Test whether two durations are equal - */ -static inline bool -mlib_duration_eq (const mlib_duration a, const mlib_duration b) mlib_noexcept -{ - return mlib_duration_cmp (a, b) == mlib_equal; -} +#define mlib_duration_cmp(...) MLIB_ARGC_PICK (_mlibDurationCmp, __VA_ARGS__) +#define _mlibDurationCmp_argc_2 mlib_duration_cmp +#define _mlibDurationCmp_argc_3(Left, Op, Right) (mlib_duration_cmp ((Left), (Right)) Op 0) /** * @brief Obtain an mlib_duration that corresponds to a `timespec` value diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 9aa46f7982..ab6b7a67f0 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -888,16 +888,16 @@ _test_duration (void) mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (4)) == 0); mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (5)) < 0); mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (-5)) > 0); - // Equality tests - mlib_check (mlib_duration_eq (mlib_seconds (4), mlib_milliseconds (4000))); - mlib_check (!mlib_duration_eq (mlib_seconds (4), mlib_milliseconds (4001))); + mlib_check (mlib_duration_cmp (mlib_seconds (4), ==, mlib_seconds (4))); + mlib_check (mlib_duration_cmp (mlib_seconds (4), <, mlib_seconds (5))); + mlib_check (mlib_duration_cmp (mlib_seconds (4), >, mlib_seconds (-5))); // Overflow saturates: d = mlib_seconds (mlib_maxof (mlib_duration_rep_t)); - mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration_mul (d, 16); - mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); // Rounds toward zero d = mlib_milliseconds (1050); @@ -912,33 +912,33 @@ _test_duration (void) d = mlib_duration_add (mlib_seconds (1), mlib_milliseconds (729)); mlib_check (mlib_microseconds_count (d), eq, 1729000); d = mlib_duration_add (mlib_seconds (-3), mlib_duration_min ()); - mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); d = mlib_duration_add (mlib_seconds (4), mlib_duration_max ()); - mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration_sub (mlib_seconds (4), mlib_milliseconds (2271)); mlib_check (mlib_milliseconds_count (d), eq, 1729); // Overflow saturates: d = mlib_duration_sub (mlib_milliseconds (-4), mlib_duration_max ()); - mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); d = mlib_duration_sub (mlib_milliseconds (4), mlib_duration_min ()); - mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration_mul (mlib_seconds (4), 5); - mlib_check (mlib_duration_eq (d, mlib_seconds (20))); + mlib_check (mlib_duration_cmp (d, ==, mlib_seconds (20))); d = mlib_duration_mul (mlib_duration_max (), 2); - mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration_mul (mlib_duration_max (), -2); - mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); d = mlib_duration_mul (mlib_duration_min (), 2); - mlib_check (mlib_duration_eq (d, mlib_duration_min ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); d = mlib_duration_mul (mlib_duration_min (), -2); - mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration_div (mlib_duration_max (), -1); mlib_check (mlib_duration_cmp (d, mlib_duration_zero ()) < 0); d = mlib_duration_div (mlib_duration_min (), -1); - mlib_check (mlib_duration_eq (d, mlib_duration_max ())); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); mlib_assert_aborts () { // Division by zero d = mlib_duration_div (d, 0); @@ -949,12 +949,12 @@ _test_duration (void) ts.tv_sec = 4; ts.tv_nsec = 0; d = mlib_duration_from_timespec (ts); - mlib_check (mlib_duration_eq (d, mlib_seconds (4))); + mlib_check (mlib_duration_cmp (d, ==, mlib_seconds (4))); // ts.tv_sec = -3; ts.tv_nsec = -4000; d = mlib_duration_from_timespec (ts); - mlib_check (mlib_duration_eq (d, mlib_microseconds (-3000004))); + mlib_check (mlib_duration_cmp (d, ==, mlib_microseconds (-3000004))); // ts = mlib_duration_to_timespec (mlib_microseconds (-5000908)); mlib_check (ts.tv_sec, eq, -5); From d403401caf4c145a2d6cedf126727c54e34a2590 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 16 Apr 2025 17:00:40 -0600 Subject: [PATCH 07/66] Infix syntax for time comparison --- src/common/src/mlib/time_point.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 695f8528ad..d81ce4f881 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -128,6 +128,10 @@ mlib_time_cmp (mlib_time_point a, mlib_time_point b) mlib_noexcept return mlib_duration_cmp (a._time_since_monotonic_start, b._time_since_monotonic_start); } +#define mlib_time_cmp(...) MLIB_ARGC_PICK (_mlib_time_cmp, __VA_ARGS__) +#define _mlib_time_cmp_argc_2 mlib_time_cmp +#define _mlib_time_cmp_argc_3(L, Op, R) (mlib_time_cmp ((L), (R)) Op 0) + /** * @brief Pause the calling thread until at least the specified duration has elapsed. * From 86411967eb12678fdeff81f7605b299442c13225 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 17 Apr 2025 18:12:01 -0600 Subject: [PATCH 08/66] Fix detection of POSIX clocks on libmuslc --- src/common/src/mlib/time_point.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index d81ce4f881..59e9a7a23d 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -1,21 +1,21 @@ #ifndef MLIB_TIME_POINT_H_INCLUDED #define MLIB_TIME_POINT_H_INCLUDED -#include -#include -#include - #ifdef __has_include #if __has_include() #include #endif #endif +#include +#include +#include + // Win32 Time APIs #include "./windows-lean.h" // Check for POSIX clock functions functions -#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L +#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_DEFAULT_SOURCE) && !defined(_WIN32)) #include #define mlib_have_posix_clocks() 1 #endif From 33c73e9b692ed44d1b1b4e1adc6c05ad93bffe68 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 17 Apr 2025 18:51:20 -0600 Subject: [PATCH 09/66] [fixup] Header ordering for Win32 --- src/libmongoc/src/mongoc/mongoc-util.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-util.c b/src/libmongoc/src/mongoc/mongoc-util.c index 1fceef483d..102b55ea37 100644 --- a/src/libmongoc/src/mongoc/mongoc-util.c +++ b/src/libmongoc/src/mongoc/mongoc-util.c @@ -14,8 +14,7 @@ * limitations under the License. */ -#include -#include + #ifdef _WIN32 #define _CRT_RAND_S #endif @@ -35,8 +34,10 @@ #include #include +#include #include #include +#include #include From 6b68bcbdda0965f298e07b1b89425373e43cc8dc Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 18 Apr 2025 12:17:35 -0600 Subject: [PATCH 10/66] More integer comparison assertions --- src/common/src/mlib/test.h | 22 +++++++++++++++++++++- src/common/tests/test-mlib.c | 28 ++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/common/src/mlib/test.h b/src/common/src/mlib/test.h index 1e1071d026..8c0d22b4dd 100644 --- a/src/common/src/mlib/test.h +++ b/src/common/src/mlib/test.h @@ -144,10 +144,30 @@ typedef struct mlib_source_location { // Integer not-equal: #define _mlibCheckCondition_neq(A, B) \ _mlibCheckIntCmp ( \ - mlib_equal, false, "!=", mlib_upsize_integer (A), mlib_upsize_integer (B), #A, #B, mlib_this_source_location ()) + mlib_equal, false, "≠", mlib_upsize_integer (A), mlib_upsize_integer (B), #A, #B, mlib_this_source_location ()) // Simple assertion with an explanatory string #define _mlibCheckCondition_because(Cond, Msg) \ _mlibCheckConditionBecause (Cond, #Cond, Msg, mlib_this_source_location ()) +// Integer comparisons: +#define _mlibCheckCondition_lt(A, B) \ + _mlibCheckIntCmp ( \ + mlib_less, true, "<", mlib_upsize_integer (A), mlib_upsize_integer (B), #A, #B, mlib_this_source_location ()) +#define _mlibCheckCondition_lte(A, B) \ + _mlibCheckIntCmp (mlib_greater, \ + false, \ + "≤", \ + mlib_upsize_integer (A), \ + mlib_upsize_integer (B), \ + #A, \ + #B, \ + mlib_this_source_location ()) +#define _mlibCheckCondition_gt(A, B) \ + _mlibCheckIntCmp ( \ + mlib_greater, true, ">", mlib_upsize_integer (A), mlib_upsize_integer (B), #A, #B, mlib_this_source_location ()) +#define _mlibCheckCondition_gte(A, B) \ + _mlibCheckIntCmp ( \ + mlib_less, false, "≥", mlib_upsize_integer (A), mlib_upsize_integer (B), #A, #B, mlib_this_source_location ()) + /// Check evaluator when given a single boolean static inline void diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index ab6b7a67f0..420abf8832 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -52,6 +52,26 @@ _test_checks (void) mlib_assert_aborts () { mlib_check (false, because, "this will fail"); } + // lt + mlib_check (1, lt, 4); + mlib_assert_aborts () { + mlib_check (4, lt, 1); + } + mlib_check (1, lte, 4); + mlib_check (1, lte, 1); + mlib_assert_aborts () { + mlib_check (4, lte, 3); + } + // gt + mlib_check (4, gt, 2); + mlib_assert_aborts () { + mlib_check (3, gt, 5); + } + mlib_check (3, gte, 2); + mlib_check (3, gte, 3); + mlib_assert_aborts () { + mlib_check (3, gte, 5); + } } static void @@ -968,7 +988,7 @@ _test_time_point (void) // Offset the time point mlib_time_point later = mlib_later (t, mlib_seconds (1)); - mlib_check (mlib_time_cmp (t, later) < 0); + mlib_check (mlib_time_cmp (t, <, later)); // Difference between two time points is a duration: mlib_duration diff = mlib_time_difference (later, t); @@ -977,7 +997,7 @@ _test_time_point (void) // The time is only ever monotonically increasing mlib_foreach_urange (i, 10000) { (void) i; - mlib_check (mlib_time_cmp (t, mlib_now ()) <= 0); + mlib_check (mlib_time_cmp (t, <=, mlib_now ())); t = mlib_now (); } } @@ -989,8 +1009,8 @@ _test_sleep (void) int rc = mlib_this_thread_sleep_for (mlib_milliseconds (50)); mlib_check (rc, eq, 0); mlib_duration t = mlib_time_difference (mlib_now (), start); - mlib_check (mlib_milliseconds_count (t) >= 50); - mlib_check (mlib_milliseconds_count (t) < 200); + mlib_check (mlib_milliseconds_count (t), gte, 50); + mlib_check (mlib_milliseconds_count (t), lt, 200); // Sleeping for a negative duration returns immediately with success start = mlib_now (); From d08ebe262edf73c5b69955133e89f852c0015124 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 18 Apr 2025 12:19:14 -0600 Subject: [PATCH 11/66] A deadline timer type --- src/common/src/mlib/timer.h | 147 +++++++++++++++++++++++++++++++++++ src/common/tests/test-mlib.c | 33 +++++++- 2 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/common/src/mlib/timer.h diff --git a/src/common/src/mlib/timer.h b/src/common/src/mlib/timer.h new file mode 100644 index 0000000000..e8ae5bbf01 --- /dev/null +++ b/src/common/src/mlib/timer.h @@ -0,0 +1,147 @@ +/** + * @file mlib/timer.h + * @brief Timer types and functions + * @date 2025-04-18 + * + * This file contains APIs for creating fixed-deadline timer object that represent + * a stable expiration point. + * + * @copyright Copyright (c) 2025 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_TIMER_H_INCLUDED +#define MLIB_TIMER_H_INCLUDED + +#include +#include +#include + +mlib_extern_c_begin (); + +/** + * @brief Represents an expiry timer. The timer stores some point-in-time + * after which it is considered to have "expired." + */ +typedef struct mlib_timer { + /** + * @brief The point-in-time at which the timer will be considered expired. + * + * This field can be updated or modified to change the expiration time of + * the timer. + */ + mlib_time_point expires_at; +} mlib_timer; + +/** + * @brief Create a deadline timer that expires at the given point-in-time + * + * @param t The point-in-time at which the returned timer should be expired + * @return mlib_timer + */ +static inline mlib_timer +mlib_expires_at (const mlib_time_point t) mlib_noexcept +{ + mlib_timer ret; + ret.expires_at = t; + return ret; +} + +/** + * @brief Create a deadline timer that expires after the given duration has + * elapsed from the point-in-time at which this function is called + */ +static inline mlib_timer +mlib_expiring_after (const mlib_duration dur) mlib_noexcept +{ + const mlib_time_point later = mlib_later (mlib_now (), dur); + return mlib_expires_at (later); +} + +/** + * @brief Obtain the duration of time that is remaining until the given timer + * expires. If the timer has expired, the returned duration will be zero (never + * negative) + */ +static inline mlib_duration +mlib_timer_remaining (const mlib_timer timer) mlib_noexcept +{ + // The duration until the expiry time of the timer + const mlib_duration remain = mlib_time_difference (timer.expires_at, mlib_now ()); + if (mlib_duration_cmp (remain, <, mlib_duration_zero ())) { + // No time remaining. Return a zero duration (not a negative duration) + return mlib_duration_zero (); + } + return remain; +} + +/** + * @brief Test for timer expiration + * + * @param timer The timer to be tested + * @param once (Optional) A pointer to an optional once-flag that will be set + * to `true` (see below) + * + * The function behaves as follows: + * + * - If `once` is a null pointer, then returns a boolean indicating whether the + * timer has expired. + * - Otherwise, if `*once` is `true`: + * - If `timer` has expired, returns `true` + * - `*once` is set to `true` + * - returns `false` + * + * The intent of the `once` flag is to support loops that check for expiry, + * where at least one iteration of the loop *must* be attempted, even if the + * timer has expired. For example: + * + * ``` + * void do_thing() { + * bool once = 0; + * while (!mlib_timer_is_expired(timer, &once)) { + * try_thing(timer); + * } + * } + * ``` + * + * In the above, `try_thing` will be called *at least once*, even if the timer + * is already expired. + */ +static inline bool +mlib_timer_is_expired (const mlib_timer timer, bool *once) mlib_noexcept +{ + // Is the timer already expired? + const bool no_time_remaining = mlib_time_cmp (timer.expires_at, <=, mlib_now ()); + if (!once) { + // Just return `true` if there is zero time remaining + return no_time_remaining; + } else { + // Tweak behavior based on the `*once` value + if (!*once) { + // This is the first time we have been called with the given once-flag + *once = true; + // Don't count an expiration, even if we have zero time left, because + // the caller wants to try some operation at least once + return false; + } + return no_time_remaining; + } +} + +mlib_extern_c_end (); + +#define mlib_timer_is_expired(...) MLIB_ARGC_PICK (_mlibTimerIsExpired, __VA_ARGS__) +#define _mlibTimerIsExpired_argc_1(Timer) mlib_timer_is_expired ((Timer), NULL) +#define _mlibTimerIsExpired_argc_2(Timer, OncePtr) mlib_timer_is_expired ((Timer), (OncePtr)) + +#endif // MLIB_TIMER_H_INCLUDED diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 420abf8832..9fef753256 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -1009,7 +1010,7 @@ _test_sleep (void) int rc = mlib_this_thread_sleep_for (mlib_milliseconds (50)); mlib_check (rc, eq, 0); mlib_duration t = mlib_time_difference (mlib_now (), start); - mlib_check (mlib_milliseconds_count (t), gte, 50); + mlib_check (mlib_milliseconds_count (t), gte, 45); mlib_check (mlib_milliseconds_count (t), lt, 200); // Sleeping for a negative duration returns immediately with success @@ -1022,6 +1023,35 @@ _test_sleep (void) mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); } +static void +_test_timer (void) +{ + mlib_timer tm = mlib_expiring_after (mlib_milliseconds (200)); + mlib_check (!mlib_timer_is_expired (tm)); + mlib_this_thread_sleep_for (mlib_milliseconds (250)); + mlib_check (mlib_timer_is_expired (tm)); + + // Test the once-var condition + bool cond = false; + // Says not expired on the first call + mlib_check (!mlib_timer_is_expired (tm, &cond)); + mlib_check (cond); // Was set to `true` + // Says it was expired on this call: + mlib_check (mlib_timer_is_expired (tm, &cond)); + + // Try with a not-yet-expired timer + cond = false; + tm = mlib_expiring_after (mlib_seconds (10)); + mlib_check (!mlib_timer_is_expired (tm)); + mlib_check (!mlib_timer_is_expired (tm, &cond)); + // cond was set to `true`, even though we are not yet expired + mlib_check (cond); + + // Create a timer that expired in the past + tm = mlib_expires_at (mlib_later (mlib_now (), mlib_seconds (-10))); + mlib_check (mlib_timer_is_expired (tm)); +} + void test_mlib_install (TestSuite *suite) { @@ -1040,6 +1070,7 @@ test_mlib_install (TestSuite *suite) TestSuite_Add (suite, "/mlib/duration", _test_duration); TestSuite_Add (suite, "/mlib/time_point", _test_time_point); TestSuite_Add (suite, "/mlib/sleep", _test_sleep); + TestSuite_Add (suite, "/mlib/timer", _test_timer); } mlib_diagnostic_pop (); From eb4482da3bb3222371a10ac5ca054385adc38963 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 18 Apr 2025 12:21:24 -0600 Subject: [PATCH 12/66] More doc and copyright comments --- src/common/src/mlib/duration.h | 35 ++++++++++++++++++++++++++++++++ src/common/src/mlib/time_point.h | 30 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 55b80af01a..e24206702d 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -1,3 +1,31 @@ +/** + * @file mlib/duration.h + * @brief Duration types and functions + * @date 2025-04-17 + * + * This file contains types and functions for working with a "duration" type, + * which represents an elapsed amount of time, possibly negative. + * + * The type `mlib_duration_rep_t` is a typedef of the intregral type that is + * used to represent duration units. + * + * The `mlib_duration` is a trivial object that represents a duration of time. + * The internal representation should not be inspected outside of this file. + * + * @copyright Copyright (c) 2025 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef MLIB_DURATION_H_INCLUDED #define MLIB_DURATION_H_INCLUDED @@ -215,6 +243,13 @@ mlib_duration_div (mlib_duration a, int div) mlib_noexcept * @retval <0 If `a` is less-than `b` * @retval >0 If `b` is less-than `a` * @retval 0 If `a` and `b` are equal durations + * + * @note This is a function-like macro that can be called with an infix operator + * as the second argument to do natural duration comparisons: + * + * ``` + * mlib_duration_cmp(a, <=, b) + * ``` */ static inline enum mlib_cmp_result mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 59e9a7a23d..ee1ad9918a 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -1,3 +1,26 @@ +/** + * @file mlib/time_point.h + * @brief A point-in-time type + * @date 2025-04-17 + * + * The `mlib_time_point` type represents a stable point-in-time. The time point + * itself is relative to a monotonic clock for the program, so it should not be + * transmitted or persisted outside of the execution of a program that uses it. + * + * @copyright Copyright (c) 2025 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef MLIB_TIME_POINT_H_INCLUDED #define MLIB_TIME_POINT_H_INCLUDED @@ -121,6 +144,13 @@ mlib_time_difference (mlib_time_point then, mlib_time_point from) * @retval <0 If 'a' is before 'b' * @retval >0 If 'b' is before 'a' * @retval 0 If 'a' and 'b' are equivalent + * + * @note This is a function-like macro that can be called with an infix operator + * as the second argument to do natural time-point comparisons: + * + * ``` + * mlib_time_cmp(a, <=, b) + * ``` */ static inline enum mlib_cmp_result mlib_time_cmp (mlib_time_point a, mlib_time_point b) mlib_noexcept From 3f4d6450f9c2226595a1c7eff6ca5a67e40f9bac Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 18 Apr 2025 12:49:07 -0600 Subject: [PATCH 13/66] Use Win32 QueryPerformanceCounter for getting times --- src/common/src/mlib/time_point.h | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index ee1ad9918a..203a411f41 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -86,11 +86,26 @@ mlib_now (void) mlib_noexcept ret._time_since_monotonic_start = mlib_duration_from_timespec (ts); return ret; #elif defined(_WIN32) - // Win32 API that returns a count of milliseconds. The time value is - // monotonically increasing. - unsigned long long ms = GetTickCount64 (); + // Win32 APIs for the high-performance monotonic counter. These APIs never fail after Windows XP + LARGE_INTEGER freq; + QueryPerformanceFrequency (&freq); + LARGE_INTEGER lits; + QueryPerformanceCounter (&lits); + // Number of ticks of the perf counter + const int64_t ticks = lits.QuadPart; + // Number of ticks that the counter emits in one second + const int64_t ticks_per_second = freq.QuadPart; + // Do some math that avoids an integer overflow when converting to microseconds. + // Just one million, used to convert time units to microseconds. + const int64_t one_million = 1000000; + // Number of whole seconds that have elapsed: + const int64_t whole_seconds = ticks / ticks_per_second; + // Times one million: + const int64_t whole_seconds_1m = whole_seconds * one_million; + // Number of microseconds beyond the last whole second: + const int64_t subsecond_us = ((ticks % ticks_per_second) * one_million) / ticks_per_second; mlib_time_point ret; - ret._time_since_monotonic_start = mlib_milliseconds (ms); + ret._time_since_monotonic_start = mlib_microseconds (whole_seconds_1m + subsecond_us); return ret; #else #error We do not know how to get the current time on this platform From fda17363501f6d1a0d589abb3e522f5ff17a27a5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 21 Apr 2025 10:30:32 -0600 Subject: [PATCH 14/66] Change windows-lean for a general platform header --- .../src/mlib/{windows-lean.h => platform.h} | 30 +++++++++++++------ src/common/src/mlib/time_point.h | 10 +------ 2 files changed, 22 insertions(+), 18 deletions(-) rename src/common/src/mlib/{windows-lean.h => platform.h} (56%) diff --git a/src/common/src/mlib/windows-lean.h b/src/common/src/mlib/platform.h similarity index 56% rename from src/common/src/mlib/windows-lean.h rename to src/common/src/mlib/platform.h index ae63a1af5e..62c7d23bc0 100644 --- a/src/common/src/mlib/windows-lean.h +++ b/src/common/src/mlib/platform.h @@ -1,10 +1,10 @@ /** - * @file windows-lean.h - * @brief Windows.h inclusion shim - * @date 2025-04-07 + * @file mlib/platform.h + * @brief Operating System Headers and Definitions + * @date 2025-04-21 * - * This file will conditionally include ``, and wraps it with - * `WIN32_LEAN_AND_MEAN` and `NOMINMAX`. + * This file will conditionally include the general system headers available + * for the current host platform. * * @copyright Copyright (c) 2025 * @@ -20,13 +20,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef MLIB_WINDOWS_LEAN_H_INCLUDED -#define MLIB_WINDOWS_LEAN_H_INCLUDED + +#ifndef MLIB_PLATFORM_H_INCLUDED +#define MLIB_PLATFORM_H_INCLUDED #ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN #define NOMINMAX #include +#include +#else +#include +#include +#include +#endif + +// Feature detection +#ifdef __has_include +#if __has_include() +#include +#endif #endif -#endif // MLIB_WINDOWS_LEAN_H_INCLUDED +#endif // MLIB_PLATFORM_H_INCLUDED diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 203a411f41..f7e204858c 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -24,18 +24,10 @@ #ifndef MLIB_TIME_POINT_H_INCLUDED #define MLIB_TIME_POINT_H_INCLUDED -#ifdef __has_include -#if __has_include() -#include -#endif -#endif - #include #include #include - -// Win32 Time APIs -#include "./windows-lean.h" +#include // Check for POSIX clock functions functions #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_DEFAULT_SOURCE) && !defined(_WIN32)) From edd30fcde618d8e4b14801711121dcea0e92ba81 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 21 Apr 2025 11:28:48 -0600 Subject: [PATCH 15/66] Set an appropriate Windows version --- src/common/src/mlib/platform.h | 37 +++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/common/src/mlib/platform.h b/src/common/src/mlib/platform.h index 62c7d23bc0..bab511e1f5 100644 --- a/src/common/src/mlib/platform.h +++ b/src/common/src/mlib/platform.h @@ -24,21 +24,38 @@ #ifndef MLIB_PLATFORM_H_INCLUDED #define MLIB_PLATFORM_H_INCLUDED +// clang-format off + +// Windows headers #ifdef _WIN32 -#define NOMINMAX -#include -#include -#else -#include -#include -#include + // Check that our WINNT version isn't too old to be used + #if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x601) + #undef _WIN32_WINNT + #endif + #ifndef _WIN32_WINNT + // Request a new-enough version of the Win32 API (required for MinGW) + #define _WIN32_WINNT 0x601 + #endif + #define NOMINMAX + // Winsock must be included before windows.h + #include + #include +#endif + +// POSIX headers +#if __unix__ + #include + #include + #include #endif // Feature detection #ifdef __has_include -#if __has_include() -#include -#endif + #if __has_include() + #include + #endif #endif +// clang-format on + #endif // MLIB_PLATFORM_H_INCLUDED From e7f39f8e6583cc1992ba075697261c9d6fdba859 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 21 Apr 2025 11:54:26 -0600 Subject: [PATCH 16/66] [fixup] macos compat --- src/common/src/mlib/platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/src/mlib/platform.h b/src/common/src/mlib/platform.h index bab511e1f5..0e44253ca0 100644 --- a/src/common/src/mlib/platform.h +++ b/src/common/src/mlib/platform.h @@ -43,7 +43,7 @@ #endif // POSIX headers -#if __unix__ +#if defined(__unix__) || defined(__unix) || defined(__APPLE__) #include #include #include From aa23a28d914baf2fa17fafd85487d930c106094d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 10 Jul 2025 12:48:23 -0600 Subject: [PATCH 17/66] Make duration API significantly more concise. This uses preprocessor trickery to support more terse conversion and arithmetic of duration types. --- src/common/src/mlib/config.h | 4 +- src/common/src/mlib/duration.h | 217 +++++++++++------- src/common/src/mlib/time_point.h | 16 +- src/common/src/mlib/timer.h | 9 +- src/common/tests/test-mlib.c | 103 +++++---- src/libmongoc/src/mongoc/mongoc-async.c | 2 +- .../src/mongoc/mongoc-client-session.c | 2 +- src/libmongoc/src/mongoc/mongoc-crypt.c | 2 +- src/libmongoc/src/mongoc/mongoc-util.c | 2 +- src/libmongoc/tests/TestSuite.h | 2 +- src/libmongoc/tests/json-test-operations.c | 6 +- src/libmongoc/tests/mock_server/mock-server.c | 4 +- src/libmongoc/tests/test-happy-eyeballs.c | 4 +- .../tests/test-mongoc-background-monitoring.c | 3 +- src/libmongoc/tests/test-mongoc-client-pool.c | 2 +- .../tests/test-mongoc-client-session.c | 4 +- .../test-mongoc-client-side-encryption.c | 2 +- src/libmongoc/tests/test-mongoc-client.c | 19 +- src/libmongoc/tests/test-mongoc-cluster.c | 4 +- src/libmongoc/tests/test-mongoc-counters.c | 4 +- src/libmongoc/tests/test-mongoc-dns.c | 4 +- src/libmongoc/tests/test-mongoc-interrupt.c | 2 +- .../tests/test-mongoc-max-staleness.c | 6 +- .../tests/test-mongoc-primary-stepdown.c | 2 +- .../tests/test-mongoc-sample-commands.c | 2 +- .../tests/test-mongoc-sdam-monitoring.c | 2 +- src/libmongoc/tests/test-mongoc-sdam.c | 4 +- src/libmongoc/tests/test-mongoc-socket.c | 4 +- .../tests/test-mongoc-speculative-auth.c | 2 +- .../tests/test-mongoc-stream-tls-error.c | 2 +- .../tests/test-mongoc-topology-reconcile.c | 2 +- .../tests/test-mongoc-topology-scanner.c | 10 +- src/libmongoc/tests/test-mongoc-topology.c | 24 +- .../tests/test-mongoc-with-transaction.c | 2 +- src/libmongoc/tests/unified/operation.c | 4 +- 35 files changed, 280 insertions(+), 203 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 2b7ed9045d..8f4a867614 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -147,8 +147,8 @@ * @brief Expand to a call expression `Prefix##_argc_N(...)`, where `N` is the * number of macro arguments. */ -#define MLIB_ARGC_PICK(Prefix, ...) \ - MLIB_JUST (MLIB_PASTE_3 (Prefix, _argc_, MLIB_ARG_COUNT (__VA_ARGS__)) (__VA_ARGS__)) +#define MLIB_ARGC_PICK(Prefix, ...) MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__) +#define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3 (Prefix, _argc_, MLIB_ARG_COUNT (__VA_ARGS__)) #ifdef __cplusplus #define mlib_is_cxx() 1 diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index e24206702d..81c036c23a 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -66,23 +66,15 @@ typedef struct mlib_duration { * @brief A macro that expands to an `mlib_duration` representing no elapsed * time */ -#define mlib_duration_zero() mlib_init (mlib_duration){0} +#define mlib_duration_zero() (mlib_init (mlib_duration){0}) /** * @brief A macro that expands to the maximum positive duration */ -#define mlib_duration_max() \ - mlib_init (mlib_duration) \ - { \ - mlib_maxof (mlib_duration_rep_t) \ - } +#define mlib_duration_max() (mlib_init (mlib_duration){mlib_maxof (mlib_duration_rep_t)}) /** * @brief A macro that expands to the minimum duration (a negative duration) */ -#define mlib_duration_min() \ - mlib_init (mlib_duration) \ - { \ - mlib_minof (mlib_duration_rep_t) \ - } +#define mlib_duration_min() (mlib_init (mlib_duration){mlib_minof (mlib_duration_rep_t)}) /** * @brief Obtain the count of microseconds represented by the duration (round @@ -115,62 +107,88 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept } /** - * @brief Create a duration object that represents the given number of - * nanoseconds + * @brief Duration creation and manipulation shorthands + * + * This function-like macro is used to create and manipulate durations on-the-fly. + * It can be called with the following syntaxes: + * + * - `mlib_duration()` (no arguments) + * creates a zero-valued duration + * - `mlib_duration()` + * copies the duration object `` + * - `mlib_duration(, )` + * Creates a duration of `` instances of ``. + * - `mlib_duration(, , )` + * Manipulates a duration according to ``. + * + * In the above, `` may be a parenthsized `mlib_duration` argument list or a + * duration object; `` must be an integral expression and `` is one of `ns`, `us,` + * `ms`, or `sec` to create a duration of `` instances of ``, and + * `` is one of: + * + * - `plus`/`minus` to add/subtract two durations + * - `mul`/`div` to multiply/divide a duration by a scalar factor. + * - `min`/`max` to get the minimum/maximum between two durations. */ -static inline mlib_duration -mlib_nanoseconds (const mlib_duration_rep_t n) mlib_noexcept -{ - // We encode as a count of microseconds, so we lose precision here. - mlib_duration ret; - ret._rep = n / 1000; - return ret; -} +#define mlib_duration(...) (MLIB_EVAL_8 (_mlibDurationMagic (__VA_ARGS__))) +#define _mlibDurationMagic(...) MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__) MLIB_NOTHING () (__VA_ARGS__) +// Wraps a `` argument, and expands to the magic only if it is parenthesized +#define _mlibDurationArgument(...) \ + MLIB_IF_ELSE (MLIB_IS_EMPTY (_mlibExpandToNothingIfFollowedByParens __VA_ARGS__)) ( \ + _mlibDurationMagic __VA_ARGS__) (__VA_ARGS__) +#define _mlibExpandToNothingIfFollowedByParens(...) + +// Zero arguments, just return a zero duration: +#define _mlib_duration_argc_0() mlib_init (mlib_duration){0} +// One argument, just copy the duration: +#define _mlib_duration_argc_1(D) _mlibDurationCopy (D) +// Two arguments, the second arg is a unit suffix: +#define _mlib_duration_argc_2(Count, Unit) mlib_duration_with_unit (Count, Unit) +// Three arguments, an infix operation: +#define _mlib_duration_argc_3(Duration, Operator, Operand) \ + MLIB_PASTE (_mlibDurationInfixOperator_, Operator) (_mlibDurationArgument (Duration), Operand) -/** - * @brief Create a duration object that represents the given number of - * microseconds - */ static inline mlib_duration -mlib_microseconds (const mlib_duration_rep_t n) mlib_noexcept +_mlibDurationInfixOperator_mul (const mlib_duration dur, int fac) mlib_noexcept { - mlib_duration ret; - ret._rep = n; + mlib_duration ret = {0}; + if (mlib_mul (&ret._rep, dur._rep, fac)) { + if ((dur._rep < 0) != (fac < 0)) { + // Different signs: Neg × Pos = Neg + ret = mlib_duration_min (); + } else { + // Same signs: Pos × Pos = Pos + // Neg × Neg = Pos + ret = mlib_duration_max (); + } + } return ret; } -/** - * @brief Create a duration object that represents the given number of - * milliseconds - */ static inline mlib_duration -mlib_milliseconds (const mlib_duration_rep_t n) mlib_noexcept +_mlibDurationInfixOperator_div (mlib_duration a, int div) mlib_noexcept { - mlib_duration_rep_t clamp = 0; - if (mlib_mul (&clamp, n, 1000)) { - clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); + mlib_check (div, neq, 0); + if (div == -1 && a._rep == mlib_minof (mlib_duration_rep_t)) { + // MIN / -1 is UB, but the saturating result is the max + a = mlib_duration_max (); + } else { + a._rep /= div; } - return mlib_microseconds (clamp); + return a; } -/** - * @brief Create a duration object that represents the given number of seconds - */ static inline mlib_duration -mlib_seconds (const mlib_duration_rep_t n) mlib_noexcept +_mlibDurationCopy (mlib_duration d) { - mlib_duration_rep_t clamp = 0; - if (mlib_mul (&clamp, n, 1000 * 1000)) { - clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); - } - return mlib_microseconds (clamp); + return d; } -/** - * @brief Create a new duration that represents the sum of two other durations - */ +// Addition impl +#define _mlibDurationInfixOperator_plus(Duration, RHS) \ + _mlibDurationInfixOperator_plus_impl ((Duration), _mlibDurationArgument (RHS)) static inline mlib_duration -mlib_duration_add (const mlib_duration a, const mlib_duration b) mlib_noexcept +_mlibDurationInfixOperator_plus_impl (const mlib_duration a, const mlib_duration b) mlib_noexcept { mlib_duration ret = {0}; if (mlib_add (&ret._rep, a._rep, b._rep)) { @@ -183,12 +201,11 @@ mlib_duration_add (const mlib_duration a, const mlib_duration b) mlib_noexcept return ret; } -/** - * @brief Create a duration from subtracting the right-hand duration from - * the left-hand duration (computes their difference) - */ +// Subtraction impl +#define _mlibDurationInfixOperator_minus(Duration, Subtrahend) \ + _mlibDurationInfixOperator_minus_impl (Duration, _mlibDurationArgument (Subtrahend)) static inline mlib_duration -mlib_duration_sub (const mlib_duration a, const mlib_duration b) mlib_noexcept +_mlibDurationInfixOperator_minus_impl (const mlib_duration a, const mlib_duration b) mlib_noexcept { mlib_duration ret = {0}; if (mlib_sub (&ret._rep, a._rep, b._rep)) { @@ -201,42 +218,69 @@ mlib_duration_sub (const mlib_duration a, const mlib_duration b) mlib_noexcept return ret; } -/** - * @brief Multiply a duration by some factor - */ +#define _mlibDurationInfixOperator_min(Duration, RHS) \ + _mlibDurationInfixOperator_min_impl (Duration, _mlibDurationArgument (RHS)) static inline mlib_duration -mlib_duration_mul (const mlib_duration dur, int fac) mlib_noexcept +_mlibDurationInfixOperator_min_impl (mlib_duration lhs, mlib_duration rhs) { - mlib_duration ret = {0}; - if (mlib_mul (&ret._rep, dur._rep, fac)) { - if ((dur._rep < 0) != (fac < 0)) { - // Different signs: Neg × Pos = Neg - ret = mlib_duration_min (); - } else { - // Same signs: Pos × Pos = Pos - // Neg × Neg = Pos - ret = mlib_duration_max (); - } + if (lhs._rep < rhs._rep) { + return lhs; + } + return rhs; +} + +#define _mlibDurationInfixOperator_max(Duration, RHS) \ + _mlibDurationInfixOperator_max_impl (Duration, _mlibDurationArgument (RHS)) +static inline mlib_duration +_mlibDurationInfixOperator_max_impl (mlib_duration lhs, mlib_duration rhs) +{ + if (lhs._rep > rhs._rep) { + return lhs; } + return rhs; +} + +// Create a duration object with a unit identifier suffix +#define mlib_duration_with_unit(Count, Unit) MLIB_PASTE (_mlibCreationDurationWithUnitSuffix_, Unit) (Count) + +static inline mlib_duration +_mlibCreationDurationWithUnitSuffix_ns (const mlib_duration_rep_t n) mlib_noexcept +{ + // We encode as a count of microseconds, so we lose precision here. + mlib_duration ret; + ret._rep = n / 1000; return ret; } -/** - * @brief Divide a duration by some divisor - */ static inline mlib_duration -mlib_duration_div (mlib_duration a, int div) mlib_noexcept +_mlibCreationDurationWithUnitSuffix_us (const mlib_duration_rep_t n) mlib_noexcept { - mlib_check (div, neq, 0); - if (div == -1 && a._rep == mlib_minof (mlib_duration_rep_t)) { - // MIN / -1 is UB, but the saturating result is the max - a = mlib_duration_max (); - } else { - a._rep /= div; + mlib_duration ret; + ret._rep = n; + return ret; +} + +static inline mlib_duration +_mlibCreationDurationWithUnitSuffix_ms (const mlib_duration_rep_t n) mlib_noexcept +{ + mlib_duration_rep_t clamp = 0; + if (mlib_mul (&clamp, n, 1000)) { + clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); } - return a; + return mlib_duration (clamp, us); } +static inline mlib_duration +_mlibCreationDurationWithUnitSuffix_sec (mlib_duration_rep_t n) +{ + mlib_duration_rep_t clamp = 0; + if (mlib_mul (&clamp, n, 1000 * 1000)) { + clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); + } + return mlib_duration (clamp, us); +} + + /** * @brief Compare two durations * @@ -248,8 +292,10 @@ mlib_duration_div (mlib_duration a, int div) mlib_noexcept * as the second argument to do natural duration comparisons: * * ``` - * mlib_duration_cmp(a, <=, b) + * mlib_duration_cmp(, , ) * ``` + * + * Where each `` should be an arglist for @see mlib_duration */ static inline enum mlib_cmp_result mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept @@ -259,7 +305,8 @@ mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept #define mlib_duration_cmp(...) MLIB_ARGC_PICK (_mlibDurationCmp, __VA_ARGS__) #define _mlibDurationCmp_argc_2 mlib_duration_cmp -#define _mlibDurationCmp_argc_3(Left, Op, Right) (mlib_duration_cmp ((Left), (Right)) Op 0) +#define _mlibDurationCmp_argc_3(Left, Op, Right) \ + (mlib_duration_cmp (_mlibDurationArgument (Left), _mlibDurationArgument (Right)) Op 0) /** * @brief Obtain an mlib_duration that corresponds to a `timespec` value @@ -272,7 +319,9 @@ mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept static inline mlib_duration mlib_duration_from_timespec (const struct timespec ts) mlib_noexcept { - return mlib_duration_add (mlib_seconds (ts.tv_sec), mlib_nanoseconds (ts.tv_nsec)); + return mlib_duration ((ts.tv_sec, sec), // + plus, + (ts.tv_nsec, ns)); } /** @@ -287,7 +336,7 @@ mlib_duration_to_timespec (const mlib_duration d) mlib_noexcept // Number of full seconds to wait const mlib_duration_rep_t n_full_seconds = mlib_seconds_count (d); // Duration with full seconds removed: - const mlib_duration usec_part = mlib_duration_sub (d, mlib_seconds (n_full_seconds)); + const mlib_duration usec_part = mlib_duration ((d), minus, (n_full_seconds, sec)); // Number of microseconds in the duration, minus all full seconds const mlib_duration_rep_t n_remaining_microseconds = mlib_microseconds_count (usec_part); // Compute the number of nanoseconds: diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index f7e204858c..80b6012d38 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -97,7 +97,7 @@ mlib_now (void) mlib_noexcept // Number of microseconds beyond the last whole second: const int64_t subsecond_us = ((ticks % ticks_per_second) * one_million) / ticks_per_second; mlib_time_point ret; - ret._time_since_monotonic_start = mlib_microseconds (whole_seconds_1m + subsecond_us); + ret._time_since_monotonic_start = mlib_duration ((whole_seconds_1m, us), plus, (subsecond_us, us)); return ret; #else #error We do not know how to get the current time on this platform @@ -118,7 +118,7 @@ static inline mlib_time_point mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept { mlib_time_point ret; - ret._time_since_monotonic_start = mlib_duration_add (from._time_since_monotonic_start, delta); + ret._time_since_monotonic_start = mlib_duration (from._time_since_monotonic_start, plus, delta); return ret; } @@ -140,7 +140,7 @@ mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept static inline mlib_duration mlib_time_difference (mlib_time_point then, mlib_time_point from) { - return mlib_duration_sub (then._time_since_monotonic_start, from._time_since_monotonic_start); + return mlib_duration (then._time_since_monotonic_start, minus, from._time_since_monotonic_start); } /** @@ -179,7 +179,7 @@ mlib_time_cmp (mlib_time_point a, mlib_time_point b) mlib_noexcept * Windows) */ static inline int -mlib_this_thread_sleep_for (const mlib_duration d) mlib_noexcept +mlib_sleep_for (const mlib_duration d) mlib_noexcept { mlib_duration_rep_t duration_usec = mlib_microseconds_count (d); if (duration_usec <= 0) { @@ -246,10 +246,12 @@ mlib_this_thread_sleep_for (const mlib_duration d) mlib_noexcept } return retc; #else -#error "mlib_this_thread_sleep_for" is not implemented on this platform. +#error "mlib_sleep_for" is not implemented on this platform. #endif } +#define mlib_sleep_for(...) mlib_sleep_for (mlib_duration (__VA_ARGS__)) + /** * @brief Pause the calling thread until the given time point has been reached * @@ -259,10 +261,10 @@ mlib_this_thread_sleep_for (const mlib_duration d) mlib_noexcept * The `when` is the *soonest* successful wake time. The thread may wake at a later time. */ static inline int -mlib_this_thread_sleep_until (const mlib_time_point when) mlib_noexcept +mlib_sleep_until (const mlib_time_point when) mlib_noexcept { const mlib_duration time_until = mlib_time_difference (when, mlib_now ()); - return mlib_this_thread_sleep_for (time_until); + return mlib_sleep_for (time_until); } mlib_extern_c_end (); diff --git a/src/common/src/mlib/timer.h b/src/common/src/mlib/timer.h index e8ae5bbf01..141862088f 100644 --- a/src/common/src/mlib/timer.h +++ b/src/common/src/mlib/timer.h @@ -68,6 +68,8 @@ mlib_expiring_after (const mlib_duration dur) mlib_noexcept return mlib_expires_at (later); } +#define mlib_expiring_after(...) mlib_expiring_after (mlib_duration (__VA_ARGS__)) + /** * @brief Obtain the duration of time that is remaining until the given timer * expires. If the timer has expired, the returned duration will be zero (never @@ -78,9 +80,9 @@ mlib_timer_remaining (const mlib_timer timer) mlib_noexcept { // The duration until the expiry time of the timer const mlib_duration remain = mlib_time_difference (timer.expires_at, mlib_now ()); - if (mlib_duration_cmp (remain, <, mlib_duration_zero ())) { + if (mlib_duration_cmp (remain, <, mlib_duration ())) { // No time remaining. Return a zero duration (not a negative duration) - return mlib_duration_zero (); + return mlib_duration (); } return remain; } @@ -98,8 +100,7 @@ mlib_timer_remaining (const mlib_timer timer) mlib_noexcept * timer has expired. * - Otherwise, if `*once` is `true`: * - If `timer` has expired, returns `true` - * - `*once` is set to `true` - * - returns `false` + * - Otherwise, `*once` is set to `true` and returns `false` * * The intent of the `once` flag is to support loops that check for expiry, * where at least one iteration of the loop *must* be attempted, even if the diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 9fef753256..9b225f48a3 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -905,64 +905,89 @@ _test_duration (void) mlib_duration d = mlib_duration_zero (); mlib_check (mlib_microseconds_count (d), eq, 0); + // Creating durations with the macro name + d = mlib_duration (); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration ())); + d = mlib_duration (d); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration ())); + + d = mlib_duration (10, ms); + mlib_check (mlib_duration_cmp (d, ==, (10, ms))); + d = mlib_duration (10, us); + mlib_check (mlib_duration_cmp (d, ==, (10, us))); + d = mlib_duration (10, sec); + mlib_check (mlib_duration_cmp (d, ==, (10, sec))); + + d = mlib_duration ((10, sec), mul, 3); + mlib_check (mlib_duration_cmp (d, ==, (30, sec))); + + d = mlib_duration ((10, sec), plus, (40, ms)); + mlib_check (mlib_duration_cmp (d, ==, (10040, ms))); + + d = mlib_duration ((10, sec), div, 20); + mlib_check (mlib_duration_cmp (d, ==, (500, ms))); + + d = mlib_duration ((4, sec), min, (400, ms)); + mlib_check (mlib_duration_cmp (d, ==, (400, ms))); + // Comparison - mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (4)) == 0); - mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (5)) < 0); - mlib_check (mlib_duration_cmp (mlib_seconds (4), mlib_seconds (-5)) > 0); - mlib_check (mlib_duration_cmp (mlib_seconds (4), ==, mlib_seconds (4))); - mlib_check (mlib_duration_cmp (mlib_seconds (4), <, mlib_seconds (5))); - mlib_check (mlib_duration_cmp (mlib_seconds (4), >, mlib_seconds (-5))); + mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (4, sec)) == 0); + mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (5, sec)) < 0); + mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (-5, sec)) > 0); + mlib_check (mlib_duration_cmp ((4, sec), ==, (4, sec))); + mlib_check (mlib_duration_cmp ((4, sec), <, (5, sec))); + mlib_check (mlib_duration_cmp ((4, sec), >, (-5, sec))); // Overflow saturates: - d = mlib_seconds (mlib_maxof (mlib_duration_rep_t)); + d = mlib_duration (mlib_maxof (mlib_duration_rep_t), sec); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); - d = mlib_duration_mul (d, 16); + d = mlib_duration (d, mul, 16); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); // Rounds toward zero - d = mlib_milliseconds (1050); + d = mlib_duration (1050, ms); mlib_check (mlib_seconds_count (d), eq, 1); - d = mlib_milliseconds (-1050); + d = mlib_duration (-1050, ms); mlib_check (mlib_seconds_count (d), eq, -1); - d = mlib_microseconds (1729); + d = mlib_duration (1729, us); mlib_check (mlib_milliseconds_count (d), eq, 1); - d = mlib_microseconds (-1729); + d = mlib_duration (-1729, us); mlib_check (mlib_milliseconds_count (d), eq, -1); - d = mlib_duration_add (mlib_seconds (1), mlib_milliseconds (729)); - mlib_check (mlib_microseconds_count (d), eq, 1729000); - d = mlib_duration_add (mlib_seconds (-3), mlib_duration_min ()); + // d = mlib_duration (1, sec, plus, 729, ms); + // mlib_check (mlib_microseconds_count (d), eq, 1729000); + d = mlib_duration ((-3, sec), plus, mlib_duration_min ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); - d = mlib_duration_add (mlib_seconds (4), mlib_duration_max ()); + d = mlib_duration ((4, sec), plus, mlib_duration_max ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); - d = mlib_duration_sub (mlib_seconds (4), mlib_milliseconds (2271)); + d = mlib_duration ((4, sec), minus, (2271, ms)); mlib_check (mlib_milliseconds_count (d), eq, 1729); // Overflow saturates: - d = mlib_duration_sub (mlib_milliseconds (-4), mlib_duration_max ()); + d = mlib_duration ((-4, ms), minus, mlib_duration_max ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); - d = mlib_duration_sub (mlib_milliseconds (4), mlib_duration_min ()); + d = mlib_duration ((4, ms), minus, mlib_duration_min ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); - d = mlib_duration_mul (mlib_seconds (4), 5); - mlib_check (mlib_duration_cmp (d, ==, mlib_seconds (20))); - d = mlib_duration_mul (mlib_duration_max (), 2); + d = mlib_duration ((4, sec), mul, 5); + mlib_check (mlib_duration_cmp (d, ==, (20, sec))); + d = mlib_duration (mlib_duration_max (), mul, 2); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); - d = mlib_duration_mul (mlib_duration_max (), -2); + d = mlib_duration (mlib_duration_max (), mul, -2); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); - d = mlib_duration_mul (mlib_duration_min (), 2); + d = mlib_duration (mlib_duration_min (), mul, 2); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); - d = mlib_duration_mul (mlib_duration_min (), -2); + d = mlib_duration (mlib_duration_min (), mul, -2); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); - d = mlib_duration_div (mlib_duration_max (), -1); + d = mlib_duration (mlib_duration_max (), div, -1); mlib_check (mlib_duration_cmp (d, mlib_duration_zero ()) < 0); - d = mlib_duration_div (mlib_duration_min (), -1); + d = mlib_duration (mlib_duration_min (), div, -1); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); mlib_assert_aborts () { // Division by zero - d = mlib_duration_div (d, 0); + d = mlib_duration (d, div, 0); } // To/from timespec @@ -970,14 +995,14 @@ _test_duration (void) ts.tv_sec = 4; ts.tv_nsec = 0; d = mlib_duration_from_timespec (ts); - mlib_check (mlib_duration_cmp (d, ==, mlib_seconds (4))); + mlib_check (mlib_duration_cmp (d, ==, (4, sec))); // ts.tv_sec = -3; ts.tv_nsec = -4000; d = mlib_duration_from_timespec (ts); - mlib_check (mlib_duration_cmp (d, ==, mlib_microseconds (-3000004))); + mlib_check (mlib_duration_cmp (d, ==, mlib_duration ((-3000004, us), plus, (0, us)))); // - ts = mlib_duration_to_timespec (mlib_microseconds (-5000908)); + ts = mlib_duration_to_timespec (mlib_duration (-5000908, us)); mlib_check (ts.tv_sec, eq, -5); mlib_check (ts.tv_nsec, eq, -908000); } @@ -988,7 +1013,7 @@ _test_time_point (void) mlib_time_point t = mlib_now (); // Offset the time point - mlib_time_point later = mlib_later (t, mlib_seconds (1)); + mlib_time_point later = mlib_later (t, mlib_duration (1, sec)); mlib_check (mlib_time_cmp (t, <, later)); // Difference between two time points is a duration: @@ -1007,7 +1032,7 @@ static void _test_sleep (void) { mlib_time_point start = mlib_now (); - int rc = mlib_this_thread_sleep_for (mlib_milliseconds (50)); + int rc = mlib_sleep_for (mlib_duration (50, ms)); mlib_check (rc, eq, 0); mlib_duration t = mlib_time_difference (mlib_now (), start); mlib_check (mlib_milliseconds_count (t), gte, 45); @@ -1015,20 +1040,20 @@ _test_sleep (void) // Sleeping for a negative duration returns immediately with success start = mlib_now (); - mlib_check (mlib_this_thread_sleep_for (mlib_seconds (-10)), eq, 0); + mlib_check (mlib_sleep_for (mlib_duration (-10, sec)), eq, 0); mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); // Sleeping until a point in the past returns immediately as well - mlib_check (mlib_this_thread_sleep_until (start), eq, 0); + mlib_check (mlib_sleep_until (start), eq, 0); mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); } static void _test_timer (void) { - mlib_timer tm = mlib_expiring_after (mlib_milliseconds (200)); + mlib_timer tm = mlib_expiring_after (200, ms); mlib_check (!mlib_timer_is_expired (tm)); - mlib_this_thread_sleep_for (mlib_milliseconds (250)); + mlib_sleep_for (250, ms); mlib_check (mlib_timer_is_expired (tm)); // Test the once-var condition @@ -1041,14 +1066,14 @@ _test_timer (void) // Try with a not-yet-expired timer cond = false; - tm = mlib_expiring_after (mlib_seconds (10)); + tm = mlib_expiring_after (10, sec); mlib_check (!mlib_timer_is_expired (tm)); mlib_check (!mlib_timer_is_expired (tm, &cond)); // cond was set to `true`, even though we are not yet expired mlib_check (cond); // Create a timer that expired in the past - tm = mlib_expires_at (mlib_later (mlib_now (), mlib_seconds (-10))); + tm = mlib_expires_at (mlib_later (mlib_now (), mlib_duration (-10, sec))); mlib_check (mlib_timer_is_expired (tm)); } diff --git a/src/libmongoc/src/mongoc/mongoc-async.c b/src/libmongoc/src/mongoc/mongoc-async.c index 4c652f48f3..f520112d6b 100644 --- a/src/libmongoc/src/mongoc/mongoc-async.c +++ b/src/libmongoc/src/mongoc/mongoc-async.c @@ -127,7 +127,7 @@ mongoc_async_run (mongoc_async_t *async) } else { /* currently this does not get hit. we always have at least one command * initialized with a stream. */ - mlib_this_thread_sleep_for (mlib_milliseconds (poll_timeout_msec)); + mlib_sleep_for (poll_timeout_msec, ms); } if (nactive > 0) { diff --git a/src/libmongoc/src/mongoc/mongoc-client-session.c b/src/libmongoc/src/mongoc/mongoc-client-session.c index dbb9543c7c..88998bf908 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-session.c +++ b/src/libmongoc/src/mongoc/mongoc-client-session.c @@ -1175,7 +1175,7 @@ mongoc_client_session_commit_transaction (mongoc_client_session_t *session, bson /* Waste the test timeout, if there is one set. */ if (session->with_txn_timeout_ms) { - mlib_this_thread_sleep_for (mlib_milliseconds (session->with_txn_timeout_ms)); + mlib_sleep_for (session->with_txn_timeout_ms, ms); } RETURN (r); diff --git a/src/libmongoc/src/mongoc/mongoc-crypt.c b/src/libmongoc/src/mongoc/mongoc-crypt.c index 9fb80e300f..81bb3ae494 100644 --- a/src/libmongoc/src/mongoc/mongoc-crypt.c +++ b/src/libmongoc/src/mongoc/mongoc-crypt.c @@ -589,7 +589,7 @@ _state_need_kms (_state_machine_t *state_machine, bson_error_t *error) } sleep_usec = mongocrypt_kms_ctx_usleep (kms_ctx); - mlib_this_thread_sleep_for (mlib_microseconds (sleep_usec)); + mlib_sleep_for (sleep_usec, us); mongoc_stream_destroy (tls_stream); tls_stream = _get_stream (endpoint, sockettimeout, ssl_opt, error); diff --git a/src/libmongoc/src/mongoc/mongoc-util.c b/src/libmongoc/src/mongoc/mongoc-util.c index 102b55ea37..3f27977a51 100644 --- a/src/libmongoc/src/mongoc/mongoc-util.c +++ b/src/libmongoc/src/mongoc/mongoc-util.c @@ -110,7 +110,7 @@ mongoc_usleep_default_impl (int64_t usec, void *user_data) { BSON_UNUSED (user_data); - mlib_this_thread_sleep_for (mlib_microseconds (usec)); + mlib_sleep_for (usec, us); } diff --git a/src/libmongoc/tests/TestSuite.h b/src/libmongoc/tests/TestSuite.h index 78712112e4..ed362c268c 100644 --- a/src/libmongoc/tests/TestSuite.h +++ b/src/libmongoc/tests/TestSuite.h @@ -579,7 +579,7 @@ _test_error (const char *format, ...) BSON_GNUC_PRINTF (1, 2); BSON_FUNC); \ abort (); \ } \ - mlib_this_thread_sleep_for (mlib_milliseconds (10)); \ + mlib_sleep_for (10, ms); \ } \ } while (0) diff --git a/src/libmongoc/tests/json-test-operations.c b/src/libmongoc/tests/json-test-operations.c index dc9ddc42ef..386d6f3f80 100644 --- a/src/libmongoc/tests/json-test-operations.c +++ b/src/libmongoc/tests/json-test-operations.c @@ -2080,7 +2080,7 @@ wait_for_event (json_test_ctx_t *ctx, const bson_t *operation) test_error ("Unknown event: %s", event_name); } if (!satisfied) { - mlib_this_thread_sleep_for (mlib_milliseconds (WAIT_FOR_EVENT_TICK_MS)); + mlib_sleep_for (WAIT_FOR_EVENT_TICK_MS, ms); } } @@ -2118,7 +2118,7 @@ wait_for_primary_change (json_test_ctx_t *ctx, const bson_t *operation) bson_mutex_unlock (&ctx->mutex); if (!satisfied) { - mlib_this_thread_sleep_for (mlib_milliseconds (10)); + mlib_sleep_for (10, ms); } } @@ -2428,7 +2428,7 @@ json_test_operation (json_test_ctx_t *ctx, mongoc_client_destroy (client); } else if (!strcmp (op_name, "wait")) { - mlib_this_thread_sleep_for (mlib_milliseconds (bson_lookup_int32 (operation, "arguments.ms"))); + mlib_sleep_for (bson_lookup_int32 (operation, "arguments.ms"), ms); } else if (!strcmp (op_name, "recordPrimary")) { /* It doesn't matter who the primary is. We just want to assert in * tests later that the primary changed x times after this operation. diff --git a/src/libmongoc/tests/mock_server/mock-server.c b/src/libmongoc/tests/mock_server/mock-server.c index cb474006bb..c149a8fe68 100644 --- a/src/libmongoc/tests/mock_server/mock-server.c +++ b/src/libmongoc/tests/mock_server/mock-server.c @@ -556,7 +556,7 @@ auto_hello (request_t *request, void *data) if (mock_server_get_rand_delay (request->server)) { const int random_sleep = rand () % 10; - mlib_this_thread_sleep_for (mlib_milliseconds (random_sleep)); + mlib_sleep_for (random_sleep, ms); } reply_to_request (request, MONGOC_REPLY_NONE, 0, 0, 1, response_json); @@ -1632,7 +1632,7 @@ mock_server_destroy (mock_server_t *server) } bson_mutex_unlock (&server->mutex); - mlib_this_thread_sleep_for (mlib_milliseconds (1)); + mlib_sleep_for (1, ms); } bson_mutex_lock (&server->mutex); diff --git a/src/libmongoc/tests/test-happy-eyeballs.c b/src/libmongoc/tests/test-happy-eyeballs.c index bd01ad1dec..50fdfd306a 100644 --- a/src/libmongoc/tests/test-happy-eyeballs.c +++ b/src/libmongoc/tests/test-happy-eyeballs.c @@ -146,7 +146,7 @@ _mock_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t timeout) /* if there were active poll responses which were all silenced, * sleep for a little while since subsequent calls to poll may not have * any delay. */ - mlib_this_thread_sleep_for (mlib_milliseconds (5)); + mlib_sleep_for (5, ms); } return nactive; } @@ -398,7 +398,7 @@ test_happy_eyeballs_dns_cache (void) mongoc_topology_scanner_node_disconnect (testcase.state.ts->nodes, false); /* wait for DNS cache to expire. */ - mlib_this_thread_sleep_for (mlib_seconds (2)); + mlib_sleep_for (2, sec); /* after running once, the topology scanner should have cached the DNS * result for IPv6. It should complete immediately. */ diff --git a/src/libmongoc/tests/test-mongoc-background-monitoring.c b/src/libmongoc/tests/test-mongoc-background-monitoring.c index 66c0537322..170ad526e4 100644 --- a/src/libmongoc/tests/test-mongoc-background-monitoring.c +++ b/src/libmongoc/tests/test-mongoc-background-monitoring.c @@ -299,8 +299,7 @@ tf_destroy (test_fixture_t *tf) * Used to make observations that a scan doesn't occur when a test fixture is * configured with a faster heartbeat. */ -#define WAIT_TWO_MIN_HEARTBEAT_MS() \ - mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (FAST_HEARTBEAT_MS), 2)) +#define WAIT_TWO_MIN_HEARTBEAT_MS() mlib_sleep_for ((FAST_HEARTBEAT_MS, ms), mul, 2) static void _signal_shutdown (test_fixture_t *tf) diff --git a/src/libmongoc/tests/test-mongoc-client-pool.c b/src/libmongoc/tests/test-mongoc-client-pool.c index 7d67a7863d..6c040f3682 100644 --- a/src/libmongoc/tests/test-mongoc-client-pool.c +++ b/src/libmongoc/tests/test-mongoc-client-pool.c @@ -310,7 +310,7 @@ static BSON_THREAD_FUN (worker, arg) pool_timeout_args_t *args = arg; mongoc_client_t *client = mongoc_client_pool_pop (args->pool); BSON_ASSERT (client); - mlib_this_thread_sleep_for (mlib_microseconds (10)); + mlib_sleep_for (10, us); mongoc_client_pool_push (args->pool, client); bson_mutex_lock (&args->mutex); /* notify main thread that current thread has terminated */ diff --git a/src/libmongoc/tests/test-mongoc-client-session.c b/src/libmongoc/tests/test-mongoc-client-session.c index d94160a7eb..adb8dd76bc 100644 --- a/src/libmongoc/tests/test-mongoc-client-session.c +++ b/src/libmongoc/tests/test-mongoc-client-session.c @@ -238,7 +238,7 @@ _test_session_pool_timeout (bool pooled) mongoc_client_session_destroy (s); BSON_ASSERT (!mongoc_server_session_pool_is_empty (client->topology->session_pool)); - mlib_this_thread_sleep_for (mlib_milliseconds (1500)); + mlib_sleep_for (1500, ms); /* getting a new client session must start a new server session */ s = mongoc_client_start_session (client, NULL, &error); @@ -322,7 +322,7 @@ _test_session_pool_reap (bool pooled) mongoc_client_session_destroy (a); BSON_ASSERT (!mongoc_server_session_pool_is_empty (client->topology->session_pool)); /* session is pooled */ - mlib_this_thread_sleep_for (mlib_milliseconds (1500)); + mlib_sleep_for (1500, ms); /* * returning session B causes session A to be reaped diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index 397b751592..6d092ff182 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -6061,7 +6061,7 @@ static BSON_THREAD_FUN (listen_socket, arg) // listen on socket r = mongoc_socket_listen (socket, 100); BSON_ASSERT (r == 0); - mlib_this_thread_sleep_for (mlib_milliseconds (1)); + mlib_sleep_for (1, ms); mongoc_socket_t *ret = mongoc_socket_accept (socket, bson_get_monotonic_time () + 100); if (ret) { // not null received a connection and test should fail diff --git a/src/libmongoc/tests/test-mongoc-client.c b/src/libmongoc/tests/test-mongoc-client.c index 407c30be58..746f32e387 100644 --- a/src/libmongoc/tests/test-mongoc-client.c +++ b/src/libmongoc/tests/test-mongoc-client.c @@ -801,7 +801,7 @@ test_wire_version (void) WIRE_VERSION_MIN - 1); /* wait until it's time for next heartbeat */ - mlib_this_thread_sleep_for (mlib_milliseconds (600)); + mlib_sleep_for (600, ms); sd = mongoc_client_select_server (client, true, NULL, &error); BSON_ASSERT (!sd); BSON_ASSERT (error.domain == MONGOC_ERROR_PROTOCOL); @@ -817,7 +817,7 @@ test_wire_version (void) WIRE_VERSION_MAX); /* wait until it's time for next heartbeat */ - mlib_this_thread_sleep_for (mlib_milliseconds (600)); + mlib_sleep_for (600, ms); sd = mongoc_client_select_server (client, true, NULL, &error); ASSERT_OR_PRINT (sd, error); mongoc_server_description_destroy (sd); @@ -2217,7 +2217,7 @@ test_mongoc_client_descriptions_pooled (void *unused) /* wait for background thread to discover all members */ start = bson_get_monotonic_time (); do { - mlib_this_thread_sleep_for (mlib_milliseconds (1)); + mlib_sleep_for (1, ms); /* Windows IPv4 tasks may take longer to connect since connection to the * first address returned by getaddrinfo may be IPv6, and failure to * connect may take a couple seconds. See CDRIVER-3639. */ @@ -2471,7 +2471,7 @@ _test_mongoc_client_select_server_retry (bool retry_succeeds) mongoc_server_description_destroy (sd); /* let socketCheckIntervalMS pass */ - mlib_this_thread_sleep_for (mlib_milliseconds (100)); + mlib_sleep_for (100, ms); /* second selection requires ping, which fails */ future = future_client_select_server (client, true, NULL, &error); @@ -2554,7 +2554,7 @@ _test_mongoc_client_fetch_stream_retry (bool retry_succeeds) future_destroy (future); /* let socketCheckIntervalMS pass */ - mlib_this_thread_sleep_for (mlib_milliseconds (100)); + mlib_sleep_for (100, ms); /* second selection requires ping, which fails */ future = future_client_command_simple (client, "db", tmp_bson ("{'cmd': 1}"), NULL, NULL, &error); @@ -2831,7 +2831,7 @@ _force_hello_with_ping (mongoc_client_t *client, int heartbeat_ms) BSON_ASSERT_PARAM (client); /* Wait until we're overdue to send a hello */ - mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (heartbeat_ms), 2)); + mlib_sleep_for ((heartbeat_ms, ms), mul, 2); /* Send a ping */ future = future_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, NULL); @@ -2989,9 +2989,10 @@ _test_client_sends_handshake (bool pooled) /* We're in cooldown for the next few seconds, so we're not * allowed to send hellos. Wait for the cooldown to end. */ - mlib_this_thread_sleep_for ( // - mlib_duration_add (mlib_milliseconds (MONGOC_TOPOLOGY_COOLDOWN_MS), // - mlib_seconds (1))); + mlib_sleep_for ( // + (MONGOC_TOPOLOGY_COOLDOWN_MS, ms), + plus, + (1, sec)); future = _force_hello_with_ping (client, heartbeat_ms); } diff --git a/src/libmongoc/tests/test-mongoc-cluster.c b/src/libmongoc/tests/test-mongoc-cluster.c index 39c332d3b2..42b4ec3416 100644 --- a/src/libmongoc/tests/test-mongoc-cluster.c +++ b/src/libmongoc/tests/test-mongoc-cluster.c @@ -510,7 +510,7 @@ _test_cluster_time (bool pooled, command_fn_t command) client = mongoc_client_pool_pop (pool); /* CDRIVER-3596 - prevent client discovery of the pool interfering with * the test operations. */ - mlib_this_thread_sleep_for (mlib_seconds (5)); + mlib_sleep_for (5, sec); } else { client = test_framework_new_default_client (); mongoc_client_set_apm_callbacks (client, callbacks, &cluster_time_test); @@ -872,7 +872,7 @@ _test_cluster_time_comparison (bool pooled) mongoc_client_pool_destroy (pool); } else { /* trigger next heartbeat, it should contain newest cluster time */ - mlib_this_thread_sleep_for (mlib_milliseconds (750)); + mlib_sleep_for (750, ms); future = future_ping (client, &error); request = mock_server_receives_any_hello_with_match (server, "{'$clusterTime': " diff --git a/src/libmongoc/tests/test-mongoc-counters.c b/src/libmongoc/tests/test-mongoc-counters.c index 1280293d2a..1c4c8629ce 100644 --- a/src/libmongoc/tests/test-mongoc-counters.c +++ b/src/libmongoc/tests/test-mongoc-counters.c @@ -464,7 +464,7 @@ test_counters_streams_timeout (void) reset_all_counters (); future = future_client_command_simple (client, "test", tmp_bson ("{'ping': 1}"), NULL, NULL, &err); request = mock_server_receives_msg (server, MONGOC_QUERY_NONE, tmp_bson ("{'ping': 1}")); - mlib_this_thread_sleep_for (mlib_microseconds (350)); + mlib_sleep_for (350, us); request_destroy (request); ret = future_get_bool (future); BSON_ASSERT (!ret); @@ -1229,7 +1229,7 @@ wait_for_background_threads (rpc_op_egress_counters expected) return; } - mlib_this_thread_sleep_for (mlib_milliseconds (100)); + mlib_sleep_for (100, ms); current = rpc_op_egress_counters_current (); } diff --git a/src/libmongoc/tests/test-mongoc-dns.c b/src/libmongoc/tests/test-mongoc-dns.c index 17d0c8d93b..c99f8b3f8d 100644 --- a/src/libmongoc/tests/test-mongoc-dns.c +++ b/src/libmongoc/tests/test-mongoc-dns.c @@ -794,7 +794,7 @@ _prose_test_update_srv_single (void *resource) client = resource; - mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (RESCAN_INTERVAL_MS), 2)); + mlib_sleep_for ((RESCAN_INTERVAL_MS, ms), mul, 2); /* Avoid ping given `loadBalanced=true`; see prose test 9. */ if (!mongoc_uri_get_option_as_bool (client->uri, MONGOC_URI_LOADBALANCED, false)) { @@ -807,7 +807,7 @@ _prose_test_update_srv_pooled (void *resource) { BSON_ASSERT_PARAM (resource); - mlib_this_thread_sleep_for (mlib_duration_mul (mlib_milliseconds (RESCAN_INTERVAL_MS), 2)); + mlib_sleep_for ((RESCAN_INTERVAL_MS, ms), mul, 2); } typedef struct { diff --git a/src/libmongoc/tests/test-mongoc-interrupt.c b/src/libmongoc/tests/test-mongoc-interrupt.c index f87369a0e5..243767a87a 100644 --- a/src/libmongoc/tests/test-mongoc-interrupt.c +++ b/src/libmongoc/tests/test-mongoc-interrupt.c @@ -47,7 +47,7 @@ BSON_THREAD_FUN (_interrupt, future_void) future = future_void; interrupt = future_get_param (future, 0)->value.void_ptr_value; - mlib_this_thread_sleep_for (mlib_milliseconds (10)); + mlib_sleep_for (10, ms); _mongoc_interrupt_interrupt (interrupt); return_value.type = future_value_void_type; future_resolve (future, return_value); diff --git a/src/libmongoc/tests/test-mongoc-max-staleness.c b/src/libmongoc/tests/test-mongoc-max-staleness.c index b04b177451..4799da9637 100644 --- a/src/libmongoc/tests/test-mongoc-max-staleness.c +++ b/src/libmongoc/tests/test-mongoc-max-staleness.c @@ -232,15 +232,15 @@ _test_last_write_date (bool pooled) r = mongoc_collection_insert_one (collection, tmp_bson ("{}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); s0 = mongoc_topology_select (client->topology, MONGOC_SS_WRITE, TEST_SS_LOG_CONTEXT, NULL, NULL, &error); ASSERT_OR_PRINT (s0, error); - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); r = mongoc_collection_insert_one (collection, tmp_bson ("{}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); s1 = mongoc_topology_select (client->topology, MONGOC_SS_WRITE, TEST_SS_LOG_CONTEXT, NULL, NULL, &error); ASSERT_OR_PRINT (s1, error); ASSERT_CMPINT64 (s1->last_write_date_ms, !=, (int64_t) -1); diff --git a/src/libmongoc/tests/test-mongoc-primary-stepdown.c b/src/libmongoc/tests/test-mongoc-primary-stepdown.c index 917b2155e3..60c730f587 100644 --- a/src/libmongoc/tests/test-mongoc-primary-stepdown.c +++ b/src/libmongoc/tests/test-mongoc-primary-stepdown.c @@ -124,7 +124,7 @@ _run_test_single_or_pooled (_test_fn_t test, bool use_pooled) _setup_test_with_client (client); /* Wait one second to be assured that the RTT connection has been * established as well. */ - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); test (client); mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); diff --git a/src/libmongoc/tests/test-mongoc-sample-commands.c b/src/libmongoc/tests/test-mongoc-sample-commands.c index 953fa47033..45150a8c50 100644 --- a/src/libmongoc/tests/test-mongoc-sample-commands.c +++ b/src/libmongoc/tests/test-mongoc-sample-commands.c @@ -2894,7 +2894,7 @@ BSON_THREAD_FUN (insert_docs, p) } bson_mutex_unlock (&ctx->lock); - mlib_this_thread_sleep_for (mlib_milliseconds (100)); + mlib_sleep_for (100, ms); } } diff --git a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c index 66beb97202..b9df4a8e57 100644 --- a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c +++ b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c @@ -1073,7 +1073,7 @@ smm_wait (smm_t *t, size_t count) if (now - started > 10 * 1000 * 1000) { break; } - mlib_this_thread_sleep_for (mlib_milliseconds (500)); + mlib_sleep_for (500, ms); } return false; } diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index b85e13dbf6..64042cba66 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -802,7 +802,7 @@ test_prose_rtt (void *unused) /* Sleep for RTT_TEST_INITIAL_SLEEP_SEC seconds to allow multiple heartbeats * to succeed. */ - mlib_this_thread_sleep_for (mlib_seconds (RTT_TEST_INITIAL_SLEEP_SEC)); + mlib_sleep_for (RTT_TEST_INITIAL_SLEEP_SEC, sec); /* Set a failpoint to make hello commands take longer. */ bson_init (&cmd); @@ -840,7 +840,7 @@ test_prose_rtt (void *unused) satisfied = true; } mongoc_server_description_destroy (sd); - mlib_this_thread_sleep_for (mlib_milliseconds (RTT_TEST_TICK_MS)); + mlib_sleep_for (RTT_TEST_TICK_MS, ms); } if (!satisfied) { diff --git a/src/libmongoc/tests/test-mongoc-socket.c b/src/libmongoc/tests/test-mongoc-socket.c index 6ce1f71eb4..a17900fde3 100644 --- a/src/libmongoc/tests/test-mongoc-socket.c +++ b/src/libmongoc/tests/test-mongoc-socket.c @@ -81,7 +81,7 @@ static BSON_THREAD_FUN (socket_test_server, data_) strcpy (buf, "pong"); - mlib_this_thread_sleep_for (mlib_milliseconds (data->server_sleep_ms)); + mlib_sleep_for (data->server_sleep_ms, ms); r = mongoc_stream_writev (stream, &iov, 1, TIMEOUT); /* if we sleep the client times out, else assert the client reads the data */ @@ -161,7 +161,7 @@ static BSON_THREAD_FUN (socket_test_client, data_) start = bson_get_monotonic_time (); while (!mongoc_stream_check_closed (stream)) { ASSERT_CMPINT64 (bson_get_monotonic_time (), <, start + 1000 * 1000); - mlib_this_thread_sleep_for (mlib_milliseconds (1)); + mlib_sleep_for (1, ms); } BSON_ASSERT (!mongoc_stream_timed_out (stream)); } else { diff --git a/src/libmongoc/tests/test-mongoc-speculative-auth.c b/src/libmongoc/tests/test-mongoc-speculative-auth.c index e33676f0ab..051c45eac0 100644 --- a/src/libmongoc/tests/test-mongoc-speculative-auth.c +++ b/src/libmongoc/tests/test-mongoc-speculative-auth.c @@ -105,7 +105,7 @@ _auto_hello_without_speculative_auth (request_t *request, void *data) if (mock_server_get_rand_delay (request->server)) { int rand_ms = rand () % 10; - mlib_this_thread_sleep_for (mlib_milliseconds (rand_ms)); + mlib_sleep_for (rand_ms, ms); } reply_to_request (request, MONGOC_REPLY_NONE, 0, 0, 1, response_json); diff --git a/src/libmongoc/tests/test-mongoc-stream-tls-error.c b/src/libmongoc/tests/test-mongoc-stream-tls-error.c index e6746f639f..ed1aef78a8 100644 --- a/src/libmongoc/tests/test-mongoc-stream-tls-error.c +++ b/src/libmongoc/tests/test-mongoc-stream-tls-error.c @@ -80,7 +80,7 @@ static BSON_THREAD_FUN (ssl_error_server, ptr) switch (data->behavior) { case SSL_TEST_BEHAVIOR_STALL_BEFORE_HANDSHAKE: - mlib_this_thread_sleep_for (mlib_milliseconds (data->handshake_stall_ms)); + mlib_sleep_for (data->handshake_stall_ms, ms); break; case SSL_TEST_BEHAVIOR_HANGUP_AFTER_HANDSHAKE: r = mongoc_stream_tls_handshake_block (ssl_stream, data->host, TIMEOUT, &error); diff --git a/src/libmongoc/tests/test-mongoc-topology-reconcile.c b/src/libmongoc/tests/test-mongoc-topology-reconcile.c index 1aa96fdd23..3619d9b4a5 100644 --- a/src/libmongoc/tests/test-mongoc-topology-reconcile.c +++ b/src/libmongoc/tests/test-mongoc-topology-reconcile.c @@ -258,7 +258,7 @@ _test_topology_reconcile_sharded (bool pooled) request_destroy (request); /* make sure the mongos response is processed first */ - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); /* replica set secondary - topology removes it */ request = mock_server_receives_any_hello (secondary); diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index 9aba9118e2..94d280c5b9 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -196,7 +196,7 @@ test_topology_scanner_discovery (void) request_destroy (request); /* let client process that response */ - mlib_this_thread_sleep_for (mlib_milliseconds (250)); + mlib_sleep_for (250, ms); /* a check of the secondary is scheduled in this scan */ request = mock_server_receives_any_hello (secondary); @@ -270,13 +270,13 @@ test_topology_scanner_oscillate (void) request_destroy (request); /* let client process that response */ - mlib_this_thread_sleep_for (mlib_milliseconds (250)); + mlib_sleep_for (250, ms); request = mock_server_receives_any_hello (server1); reply_to_request_simple (request, server1_response); /* we don't schedule another check of server0 */ - mlib_this_thread_sleep_for (mlib_milliseconds (250)); + mlib_sleep_for (250, ms); BSON_ASSERT (!future_get_mongoc_server_description_ptr (future)); BSON_ASSERT (scanner->async->ncmds == 0); @@ -359,7 +359,7 @@ slow_initiator (const mongoc_uri_t *uri, const mongoc_host_list_t *host, void *u data = (initiator_data_t *) user_data; if (host->port == data->slow_port) { - mlib_this_thread_sleep_for (mlib_milliseconds (500)); /* 500 ms is longer than connectTimeoutMS */ + mlib_sleep_for (500, ms); /* 500 ms is longer than connectTimeoutMS */ } return mongoc_client_default_stream_initiator (uri, host, data->client, err); @@ -670,7 +670,7 @@ _test_topology_scanner_does_not_renegotiate (bool pooled) r = mongoc_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - mlib_this_thread_sleep_for (mlib_milliseconds (1500)); + mlib_sleep_for (1500, ms); r = mongoc_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); diff --git a/src/libmongoc/tests/test-mongoc-topology.c b/src/libmongoc/tests/test-mongoc-topology.c index b67d1ebee2..fb829773f6 100644 --- a/src/libmongoc/tests/test-mongoc-topology.c +++ b/src/libmongoc/tests/test-mongoc-topology.c @@ -399,7 +399,7 @@ _test_server_selection (bool try_once) future_destroy (future); /* one heartbeat, plus a few milliseconds */ - mlib_this_thread_sleep_for (mlib_milliseconds (510)); + mlib_sleep_for (510, ms); /* second selection, now we try hello again */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -723,7 +723,7 @@ test_cooldown_standalone (void) MONGOC_ERROR_SERVER_SELECTION_FAILURE, "No servers yet eligible for rescan"); - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); /* third selection doesn't try to call hello: we're still in cooldown */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -737,7 +737,7 @@ test_cooldown_standalone (void) mock_server_set_request_timeout_msec (server, get_future_timeout_ms ()); // 5.1 seconds - mlib_this_thread_sleep_for (mlib_milliseconds (5100)); + mlib_sleep_for (5100, ms); /* cooldown ends, now we try hello again, this time succeeding */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -841,7 +841,7 @@ test_cooldown_rs (void) BSON_ASSERT (!future_get_mongoc_server_description_ptr (future)); future_destroy (future); - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); /* second selection doesn't try hello on server 1: it's in cooldown */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -860,7 +860,7 @@ test_cooldown_rs (void) future_destroy (future); // 5.1 seconds, longer than the 5sec cooldown - mlib_this_thread_sleep_for (mlib_milliseconds (5100)); + mlib_sleep_for (5100, ms); /* cooldown ends, now we try hello on server 1, this time succeeding */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -1242,7 +1242,7 @@ test_rtt (void *ctx) future = future_client_command_simple (client, "db", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); request = mock_server_receives_any_hello (server); - mlib_this_thread_sleep_for (mlib_seconds (1)); + mlib_sleep_for (1, sec); reply_to_request ( request, MONGOC_REPLY_NONE, @@ -1422,7 +1422,7 @@ _test_hello_retry_single (bool hangup, size_t n_failures) receives_command (server, future); /* wait for the next server check */ - mlib_this_thread_sleep_for (mlib_milliseconds (600)); + mlib_sleep_for (600, ms); /* start a {foo: 1} command, server check fails and retries immediately */ future = future_command (client, &error); @@ -1673,7 +1673,7 @@ test_incompatible_error (void) WIRE_VERSION_MAX + 1); /* wait until it's time for next heartbeat */ - mlib_this_thread_sleep_for (mlib_milliseconds (600)); + mlib_sleep_for (600, ms); ASSERT (!mongoc_client_command_simple ( client, "admin", tmp_bson ("{'" HANDSHAKE_CMD_LEGACY_HELLO "': 1}"), NULL, NULL, &error)); @@ -1952,7 +1952,7 @@ _test_request_scan_on_error ( /* a scan is requested immediately. wait for the scan to finish. */ WAIT_UNTIL (checks_cmp (&checks, "n_started", '=', 4)); } else { - mlib_this_thread_sleep_for (mlib_duration_mul (mlib_microseconds (minHBMS), 2)); + mlib_sleep_for ((minHBMS, us), mul, 2); BSON_ASSERT (checks_cmp (&checks, "n_started", '=', 2)); } } else { @@ -2353,7 +2353,7 @@ _test_hello_ok (bool pooled) /* Send off another ping for non-pooled clients, making sure to wait long * enough to require another heartbeat. */ - mlib_this_thread_sleep_for (mlib_milliseconds (600)); + mlib_sleep_for (600, ms); future = future_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); } @@ -2381,7 +2381,7 @@ _test_hello_ok (bool pooled) /* Send off another ping for non-pooled clients, making sure to wait long * enough to require another heartbeat. */ - mlib_this_thread_sleep_for (mlib_milliseconds (600)); + mlib_sleep_for (600, ms); future = future_client_command_simple (client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); } @@ -2495,7 +2495,7 @@ test_failure_to_setup_after_retry (void) } // Wait until ready for next topology scan. - mlib_this_thread_sleep_for (mlib_milliseconds (overridden_heartbeat_ms)); + mlib_sleep_for (overridden_heartbeat_ms, ms); // Send another command. future = future_client_command_simple (client, "test", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); diff --git a/src/libmongoc/tests/test-mongoc-with-transaction.c b/src/libmongoc/tests/test-mongoc-with-transaction.c index 3867121356..94a5712c28 100644 --- a/src/libmongoc/tests/test-mongoc-with-transaction.c +++ b/src/libmongoc/tests/test-mongoc-with-transaction.c @@ -24,7 +24,7 @@ with_transaction_fail_transient_txn (mongoc_client_session_t *session, void *ctx BSON_UNUSED (ctx); BSON_UNUSED (error); - mlib_this_thread_sleep_for (mlib_milliseconds (session->with_txn_timeout_ms)); + mlib_sleep_for (session->with_txn_timeout_ms, ms); *reply = bson_new (); BSON_APPEND_ARRAY_BUILDER_BEGIN (*reply, "errorLabels", &labels); diff --git a/src/libmongoc/tests/unified/operation.c b/src/libmongoc/tests/unified/operation.c index d3e54a9d36..ab8845480a 100644 --- a/src/libmongoc/tests/unified/operation.c +++ b/src/libmongoc/tests/unified/operation.c @@ -3328,7 +3328,7 @@ operation_wait (test_t *test, operation_t *op, result_t *result, bson_error_t *e bson_iter_init_find (&iter, op->arguments, "ms"); ASSERT (BSON_ITER_HOLDS_INT (&iter)); const int64_t sleep_msec = bson_iter_as_int64 (&iter); - mlib_this_thread_sleep_for (mlib_milliseconds (sleep_msec)); + mlib_sleep_for (sleep_msec, ms); result_from_ok (result); return true; @@ -3628,7 +3628,7 @@ log_filter_hide_server_selection_operation (const mongoc_structured_log_entry_t static void _operation_hidden_wait (test_t *test, entity_t *client, const char *name) { - mlib_this_thread_sleep_for (mlib_milliseconds (WAIT_FOR_EVENT_TICK_MS)); + mlib_sleep_for (WAIT_FOR_EVENT_TICK_MS, ms); // @todo Re-examine this once we have support for connection pools in the unified test // runner. Without pooling, all events we could be waiting on would be coming From feb483680e9953304eb614e7d8da025cca8d2a5c Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 11 Jul 2025 09:02:35 -0600 Subject: [PATCH 18/66] Clean up some macro magic --- src/common/src/mlib/config.h | 7 +++++++ src/common/src/mlib/duration.h | 11 ++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 8f4a867614..8189d20629 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -92,8 +92,15 @@ MLIB_JUST(_mlibPickSixteenth \ MLIB_NOTHING("MSVC workaround") \ (__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, ~)) +// Expands to a single comma if invoked as a function-like macro #define _mlibCommaIfParens(...) , +/** + * @brief Expands to `1` if the given argument list is a parenthesized + * group of tokens otherwize `0` + */ +#define MLIB_IS_PARENTHESIZED(X) _mlibHasComma(_mlibCommaIfParens X MLIB_NOTHING(#X)) + /** * A helper for isEmpty(): If given (0, 0, 0, 1), expands as: * - first: _mlibHasComma(_mlibIsEmptyCase_0001) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 81c036c23a..92be1c134d 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -130,16 +130,17 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept * - `mul`/`div` to multiply/divide a duration by a scalar factor. * - `min`/`max` to get the minimum/maximum between two durations. */ -#define mlib_duration(...) (MLIB_EVAL_8 (_mlibDurationMagic (__VA_ARGS__))) +#define mlib_duration(...) MLIB_EVAL_8 (_mlibDurationMagic (__VA_ARGS__)) #define _mlibDurationMagic(...) MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__) MLIB_NOTHING () (__VA_ARGS__) // Wraps a `` argument, and expands to the magic only if it is parenthesized -#define _mlibDurationArgument(...) \ - MLIB_IF_ELSE (MLIB_IS_EMPTY (_mlibExpandToNothingIfFollowedByParens __VA_ARGS__)) ( \ - _mlibDurationMagic __VA_ARGS__) (__VA_ARGS__) +#define _mlibDurationArgument(X) \ + /* If given a parenthesized expression, act as an invocation of `mlib_duration() */ \ + MLIB_IF_ELSE (MLIB_IS_PARENTHESIZED (X) MLIB_NOTHING (#X)) \ + /* then: */ (_mlibDurationMagic X) /* else: */ (X) #define _mlibExpandToNothingIfFollowedByParens(...) // Zero arguments, just return a zero duration: -#define _mlib_duration_argc_0() mlib_init (mlib_duration){0} +#define _mlib_duration_argc_0() (mlib_init (mlib_duration){0}) // One argument, just copy the duration: #define _mlib_duration_argc_1(D) _mlibDurationCopy (D) // Two arguments, the second arg is a unit suffix: From d5704defafaf65e99b451bf46d3ab539aab73ba5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 11 Jul 2025 10:33:23 -0600 Subject: [PATCH 19/66] Fix up macro expansion to support nested expressions --- src/common/src/mlib/config.h | 3 +- src/common/src/mlib/duration.h | 140 ++++++++++++++++++++------------- src/common/tests/test-mlib.c | 9 +++ 3 files changed, 97 insertions(+), 55 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 8189d20629..20a828a546 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -154,7 +154,8 @@ * @brief Expand to a call expression `Prefix##_argc_N(...)`, where `N` is the * number of macro arguments. */ -#define MLIB_ARGC_PICK(Prefix, ...) MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__) +#define MLIB_ARGC_PICK(Prefix, ...) \ + MLIB_EVAL_1 (MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) MLIB_NOTHING ("Force arglist to expand first") (__VA_ARGS__)) #define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3 (Prefix, _argc_, MLIB_ARG_COUNT (__VA_ARGS__)) #ifdef __cplusplus diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 92be1c134d..4cc4938811 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -122,21 +122,27 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept * Manipulates a duration according to ``. * * In the above, `` may be a parenthsized `mlib_duration` argument list or a - * duration object; `` must be an integral expression and `` is one of `ns`, `us,` - * `ms`, or `sec` to create a duration of `` instances of ``, and - * `` is one of: + * duration object; `` must be an integral expression and `` is a + * unit suffix identiifer (see: `mlib_duration_with_unit`) to create a duration + * of `` instances of ``, and `` is one of: * - * - `plus`/`minus` to add/subtract two durations + * - `plus`/`minus` to add/subtract two durations. * - `mul`/`div` to multiply/divide a duration by a scalar factor. * - `min`/`max` to get the minimum/maximum between two durations. + * + * All duration arithmetic/conversion operations use well-defined saturating + * arithmetic, and never wrap or trap. */ -#define mlib_duration(...) MLIB_EVAL_8 (_mlibDurationMagic (__VA_ARGS__)) -#define _mlibDurationMagic(...) MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__) MLIB_NOTHING () (__VA_ARGS__) +#define mlib_duration(...) MLIB_EVAL_16 (_mlibDurationMagic (__VA_ARGS__)) +#define _mlibDurationMagic(...) \ + MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__) \ + MLIB_NOTHING ("force the argument list to expand first") (__VA_ARGS__) // Wraps a `` argument, and expands to the magic only if it is parenthesized #define _mlibDurationArgument(X) \ /* If given a parenthesized expression, act as an invocation of `mlib_duration() */ \ - MLIB_IF_ELSE (MLIB_IS_PARENTHESIZED (X) MLIB_NOTHING (#X)) \ - /* then: */ (_mlibDurationMagic X) /* else: */ (X) + MLIB_IF_ELSE (MLIB_IS_PARENTHESIZED (X)) \ + MLIB_NOTHING ("force the 'then' branch to expand first") \ + /* then: */ (_mlibDurationMagic MLIB_NOTHING () X) /* else: */ (X) #define _mlibExpandToNothingIfFollowedByParens(...) // Zero arguments, just return a zero duration: @@ -146,9 +152,19 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept // Two arguments, the second arg is a unit suffix: #define _mlib_duration_argc_2(Count, Unit) mlib_duration_with_unit (Count, Unit) // Three arguments, an infix operation: -#define _mlib_duration_argc_3(Duration, Operator, Operand) \ - MLIB_PASTE (_mlibDurationInfixOperator_, Operator) (_mlibDurationArgument (Duration), Operand) +#define _mlib_duration_argc_3(Duration, Operator, Operand) \ + MLIB_PASTE (_mlibDurationInfixOperator_, Operator) \ + MLIB_NOTHING ("force the first argument to expand first") \ + (_mlibDurationArgument (Duration), Operand) +// By-value copy a duration +static inline mlib_duration +_mlibDurationCopy (mlib_duration d) +{ + return d; +} + +// Duration scalar multiply static inline mlib_duration _mlibDurationInfixOperator_mul (const mlib_duration dur, int fac) mlib_noexcept { @@ -166,6 +182,7 @@ _mlibDurationInfixOperator_mul (const mlib_duration dur, int fac) mlib_noexcept return ret; } +// Duration scalar divide static inline mlib_duration _mlibDurationInfixOperator_div (mlib_duration a, int div) mlib_noexcept { @@ -179,17 +196,10 @@ _mlibDurationInfixOperator_div (mlib_duration a, int div) mlib_noexcept return a; } +// Duration addition +#define _mlibDurationInfixOperator_plus(Duration, RHS) _mlibDurationAdd ((Duration), _mlibDurationArgument (RHS)) static inline mlib_duration -_mlibDurationCopy (mlib_duration d) -{ - return d; -} - -// Addition impl -#define _mlibDurationInfixOperator_plus(Duration, RHS) \ - _mlibDurationInfixOperator_plus_impl ((Duration), _mlibDurationArgument (RHS)) -static inline mlib_duration -_mlibDurationInfixOperator_plus_impl (const mlib_duration a, const mlib_duration b) mlib_noexcept +_mlibDurationAdd (const mlib_duration a, const mlib_duration b) mlib_noexcept { mlib_duration ret = {0}; if (mlib_add (&ret._rep, a._rep, b._rep)) { @@ -202,11 +212,11 @@ _mlibDurationInfixOperator_plus_impl (const mlib_duration a, const mlib_duration return ret; } -// Subtraction impl +// Duration subtraction #define _mlibDurationInfixOperator_minus(Duration, Subtrahend) \ - _mlibDurationInfixOperator_minus_impl (Duration, _mlibDurationArgument (Subtrahend)) + _mlibDurationSubtract (Duration, _mlibDurationArgument (Subtrahend)) static inline mlib_duration -_mlibDurationInfixOperator_minus_impl (const mlib_duration a, const mlib_duration b) mlib_noexcept +_mlibDurationSubtract (const mlib_duration a, const mlib_duration b) mlib_noexcept { mlib_duration ret = {0}; if (mlib_sub (&ret._rep, a._rep, b._rep)) { @@ -219,10 +229,9 @@ _mlibDurationInfixOperator_minus_impl (const mlib_duration a, const mlib_duratio return ret; } -#define _mlibDurationInfixOperator_min(Duration, RHS) \ - _mlibDurationInfixOperator_min_impl (Duration, _mlibDurationArgument (RHS)) +#define _mlibDurationInfixOperator_min(Duration, RHS) _mlibDurationMinBetween (Duration, _mlibDurationArgument (RHS)) static inline mlib_duration -_mlibDurationInfixOperator_min_impl (mlib_duration lhs, mlib_duration rhs) +_mlibDurationMinBetween (mlib_duration lhs, mlib_duration rhs) { if (lhs._rep < rhs._rep) { return lhs; @@ -230,10 +239,9 @@ _mlibDurationInfixOperator_min_impl (mlib_duration lhs, mlib_duration rhs) return rhs; } -#define _mlibDurationInfixOperator_max(Duration, RHS) \ - _mlibDurationInfixOperator_max_impl (Duration, _mlibDurationArgument (RHS)) +#define _mlibDurationInfixOperator_max(Duration, RHS) _mlibDurationMaxBetween (Duration, _mlibDurationArgument (RHS)) static inline mlib_duration -_mlibDurationInfixOperator_max_impl (mlib_duration lhs, mlib_duration rhs) +_mlibDurationMaxBetween (mlib_duration lhs, mlib_duration rhs) { if (lhs._rep > rhs._rep) { return lhs; @@ -241,46 +249,70 @@ _mlibDurationInfixOperator_max_impl (mlib_duration lhs, mlib_duration rhs) return rhs; } -// Create a duration object with a unit identifier suffix -#define mlib_duration_with_unit(Count, Unit) MLIB_PASTE (_mlibCreationDurationWithUnitSuffix_, Unit) (Count) +/** + * @brief Create a duration object from a count of some unit of time + * + * @param Count An integral expression + * @param Unit A unit suffix identifier, must be one of: + * + * - `ns` (nanoseconds) + * - `us` (microseconds) + * - `ms` (milliseconds) + * - `sec` (seconds) + * - `min` (minutes) + * + * Other unit suffixes will generate a compile-time error + */ +#define mlib_duration_with_unit(Count, Unit) \ + MLIB_PASTE (_mlibCreateDurationFromUnitCount_, Unit) (mlib_upsize_integer (Count)) static inline mlib_duration -_mlibCreationDurationWithUnitSuffix_ns (const mlib_duration_rep_t n) mlib_noexcept +_mlibCreateDurationFromUnitCount_us (const mlib_upsized_integer n) mlib_noexcept { - // We encode as a count of microseconds, so we lose precision here. mlib_duration ret; - ret._rep = n / 1000; + if (n.is_signed) { + // The duration rep is the same as the signed max type, so we don't need to do any + // special arithmetic to encode it + mlib_static_assert (sizeof (mlib_duration_rep_t) == sizeof (n.bits.as_signed)); + ret._rep = mlib_assert_narrow (mlib_duration_rep_t, n.bits.as_signed); + } else { + if (mlib_narrow (&ret._rep, n.bits.as_unsigned)) { + // Unsigned value is too large to fit in our signed repr, so just use the max repr + ret = mlib_duration_max (); + } + } return ret; } static inline mlib_duration -_mlibCreationDurationWithUnitSuffix_us (const mlib_duration_rep_t n) mlib_noexcept +_mlibCreateDurationFromUnitCount_ns (mlib_upsized_integer n) mlib_noexcept { - mlib_duration ret; - ret._rep = n; - return ret; + // We encode as a count of microseconds, so we lose precision here. + if (n.is_signed) { + n.bits.as_signed /= 1000; + } else { + n.bits.as_unsigned /= 1000; + } + return _mlibCreateDurationFromUnitCount_us (n); } static inline mlib_duration -_mlibCreationDurationWithUnitSuffix_ms (const mlib_duration_rep_t n) mlib_noexcept +_mlibCreateDurationFromUnitCount_ms (const mlib_upsized_integer n) mlib_noexcept { - mlib_duration_rep_t clamp = 0; - if (mlib_mul (&clamp, n, 1000)) { - clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); - } - return mlib_duration (clamp, us); + return mlib_duration (_mlibCreateDurationFromUnitCount_us (n), mul, 1000); } static inline mlib_duration -_mlibCreationDurationWithUnitSuffix_sec (mlib_duration_rep_t n) +_mlibCreateDurationFromUnitCount_sec (const mlib_upsized_integer n) { - mlib_duration_rep_t clamp = 0; - if (mlib_mul (&clamp, n, 1000 * 1000)) { - clamp = n > 0 ? mlib_maxof (mlib_duration_rep_t) : mlib_minof (mlib_duration_rep_t); - } - return mlib_duration (clamp, us); + return mlib_duration (_mlibCreateDurationFromUnitCount_us (n), mul, 1000 * 1000); } +static inline mlib_duration +_mlibCreateDurationFromUnitCount_min (const mlib_upsized_integer n) +{ + return mlib_duration (_mlibCreateDurationFromUnitCount_us (n), mul, 60 * 1000 * 1000); +} /** * @brief Compare two durations @@ -304,7 +336,7 @@ mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept return mlib_cmp (a._rep, b._rep); } -#define mlib_duration_cmp(...) MLIB_ARGC_PICK (_mlibDurationCmp, __VA_ARGS__) +#define mlib_duration_cmp(...) MLIB_EVAL_4 (MLIB_ARGC_PICK (_mlibDurationCmp, __VA_ARGS__)) #define _mlibDurationCmp_argc_2 mlib_duration_cmp #define _mlibDurationCmp_argc_3(Left, Op, Right) \ (mlib_duration_cmp (_mlibDurationArgument (Left), _mlibDurationArgument (Right)) Op 0) @@ -334,10 +366,10 @@ mlib_duration_from_timespec (const struct timespec ts) mlib_noexcept static inline struct timespec mlib_duration_to_timespec (const mlib_duration d) mlib_noexcept { - // Number of full seconds to wait + // Number of full seconds in the duration const mlib_duration_rep_t n_full_seconds = mlib_seconds_count (d); - // Duration with full seconds removed: - const mlib_duration usec_part = mlib_duration ((d), minus, (n_full_seconds, sec)); + // Duration with full seconds removed + const mlib_duration usec_part = mlib_duration (d, minus, (n_full_seconds, sec)); // Number of microseconds in the duration, minus all full seconds const mlib_duration_rep_t n_remaining_microseconds = mlib_microseconds_count (usec_part); // Compute the number of nanoseconds: diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 4f236e610d..95a59f72b0 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -941,6 +941,15 @@ _test_duration (void) d = mlib_duration ((4, sec), min, (400, ms)); mlib_check (mlib_duration_cmp (d, ==, (400, ms))); + // Clamp to at most five minutes + d = mlib_duration ( + // At least 5 seconds: + (d, max, (5, sec)), + // At most 90 seconds: + min, + (90, sec)); + mlib_check (mlib_duration_cmp (d, ==, (5, sec))); + // Comparison mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (4, sec)) == 0); mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (5, sec)) < 0); From af1e828f744ce5bcee52ba3a432d81e001b773ad Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 11 Jul 2025 10:39:05 -0600 Subject: [PATCH 20/66] More duration for mlib_later --- src/common/src/mlib/time_point.h | 2 ++ src/common/tests/test-mlib.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 80b6012d38..091281be58 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -122,6 +122,8 @@ mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept return ret; } +#define mlib_later(From, Delta) mlib_later (From, MLIB_EVAL_4 (_mlibDurationArgument (Delta))) + /** * @brief Obtain the duration between two points in time. * diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 95a59f72b0..d1ed6a6344 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1033,7 +1033,7 @@ _test_time_point (void) mlib_time_point t = mlib_now (); // Offset the time point - mlib_time_point later = mlib_later (t, mlib_duration (1, sec)); + mlib_time_point later = mlib_later (t, (1, sec)); mlib_check (mlib_time_cmp (t, <, later)); // Difference between two time points is a duration: @@ -1060,7 +1060,7 @@ _test_sleep (void) // Sleeping for a negative duration returns immediately with success start = mlib_now (); - mlib_check (mlib_sleep_for (mlib_duration (-10, sec)), eq, 0); + mlib_check (mlib_sleep_for (-10, sec), eq, 0); mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); // Sleeping until a point in the past returns immediately as well From c44fdfa3628f76cfab3c0a64cd652a94fd8ce3fe Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 11 Jul 2025 10:58:55 -0600 Subject: [PATCH 21/66] Fix MSVC preproc disagreements --- src/common/src/mlib/config.h | 2 +- src/common/src/mlib/duration.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 20a828a546..58dad09036 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -155,7 +155,7 @@ * number of macro arguments. */ #define MLIB_ARGC_PICK(Prefix, ...) \ - MLIB_EVAL_1 (MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) MLIB_NOTHING ("Force arglist to expand first") (__VA_ARGS__)) + MLIB_EVAL_4 (MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__)) #define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3 (Prefix, _argc_, MLIB_ARG_COUNT (__VA_ARGS__)) #ifdef __cplusplus diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 4cc4938811..e07fedd0e4 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -269,7 +269,7 @@ _mlibDurationMaxBetween (mlib_duration lhs, mlib_duration rhs) static inline mlib_duration _mlibCreateDurationFromUnitCount_us (const mlib_upsized_integer n) mlib_noexcept { - mlib_duration ret; + mlib_duration ret = mlib_duration (); if (n.is_signed) { // The duration rep is the same as the signed max type, so we don't need to do any // special arithmetic to encode it From a953be27a390f64260887450428182701b0d0b08 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 11 Jul 2025 11:00:23 -0600 Subject: [PATCH 22/66] Formatting --- src/common/src/mlib/config.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 58dad09036..75a0c368db 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -154,8 +154,7 @@ * @brief Expand to a call expression `Prefix##_argc_N(...)`, where `N` is the * number of macro arguments. */ -#define MLIB_ARGC_PICK(Prefix, ...) \ - MLIB_EVAL_4 (MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__)) +#define MLIB_ARGC_PICK(Prefix, ...) MLIB_EVAL_4 (MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__)) #define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3 (Prefix, _argc_, MLIB_ARG_COUNT (__VA_ARGS__)) #ifdef __cplusplus From f5501a4861622a2513f4fadb4b8334bc04388632 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 14 Jul 2025 11:22:42 -0600 Subject: [PATCH 23/66] Clean up macro definitions --- src/common/src/mlib/duration.h | 39 +++++++++++++++++----------------- src/common/tests/test-mlib.c | 4 ++-- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index e07fedd0e4..cbaf8abbe9 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -62,11 +62,6 @@ typedef struct mlib_duration { mlib_duration_rep_t _rep; } mlib_duration; -/** - * @brief A macro that expands to an `mlib_duration` representing no elapsed - * time - */ -#define mlib_duration_zero() (mlib_init (mlib_duration){0}) /** * @brief A macro that expands to the maximum positive duration */ @@ -134,9 +129,10 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept * arithmetic, and never wrap or trap. */ #define mlib_duration(...) MLIB_EVAL_16 (_mlibDurationMagic (__VA_ARGS__)) -#define _mlibDurationMagic(...) \ - MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__) \ - MLIB_NOTHING ("force the argument list to expand first") (__VA_ARGS__) +#define _mlibDurationMagic(...) \ + MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__) \ + MLIB_NOTHING ("force the argument list to expand first") \ + (__VA_ARGS__) // Wraps a `` argument, and expands to the magic only if it is parenthesized #define _mlibDurationArgument(X) \ /* If given a parenthesized expression, act as an invocation of `mlib_duration() */ \ @@ -152,10 +148,10 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept // Two arguments, the second arg is a unit suffix: #define _mlib_duration_argc_2(Count, Unit) mlib_duration_with_unit (Count, Unit) // Three arguments, an infix operation: -#define _mlib_duration_argc_3(Duration, Operator, Operand) \ - MLIB_PASTE (_mlibDurationInfixOperator_, Operator) \ - MLIB_NOTHING ("force the first argument to expand first") \ - (_mlibDurationArgument (Duration), Operand) +#define _mlib_duration_argc_3(Duration, Operator, Operand) \ + MLIB_PASTE (_mlibDurationInfixOperator_, Operator) \ + MLIB_NOTHING ("force the arguments to expand first") \ + (Duration, Operand) // By-value copy a duration static inline mlib_duration @@ -165,8 +161,9 @@ _mlibDurationCopy (mlib_duration d) } // Duration scalar multiply +#define _mlibDurationInfixOperator_mul(LHS, Fac) _mlibDurationMultiply (_mlibDurationArgument (LHS), (Fac)) static inline mlib_duration -_mlibDurationInfixOperator_mul (const mlib_duration dur, int fac) mlib_noexcept +_mlibDurationMultiply (const mlib_duration dur, int fac) mlib_noexcept { mlib_duration ret = {0}; if (mlib_mul (&ret._rep, dur._rep, fac)) { @@ -183,8 +180,9 @@ _mlibDurationInfixOperator_mul (const mlib_duration dur, int fac) mlib_noexcept } // Duration scalar divide +#define _mlibDurationInfixOperator_div(LHS, Div) _mlibDurationDivide (_mlibDurationArgument (LHS), (Div)) static inline mlib_duration -_mlibDurationInfixOperator_div (mlib_duration a, int div) mlib_noexcept +_mlibDurationDivide (mlib_duration a, int div) mlib_noexcept { mlib_check (div, neq, 0); if (div == -1 && a._rep == mlib_minof (mlib_duration_rep_t)) { @@ -197,7 +195,8 @@ _mlibDurationInfixOperator_div (mlib_duration a, int div) mlib_noexcept } // Duration addition -#define _mlibDurationInfixOperator_plus(Duration, RHS) _mlibDurationAdd ((Duration), _mlibDurationArgument (RHS)) +#define _mlibDurationInfixOperator_plus(LHS, RHS) \ + _mlibDurationAdd (_mlibDurationArgument (LHS), _mlibDurationArgument (RHS)) static inline mlib_duration _mlibDurationAdd (const mlib_duration a, const mlib_duration b) mlib_noexcept { @@ -213,8 +212,8 @@ _mlibDurationAdd (const mlib_duration a, const mlib_duration b) mlib_noexcept } // Duration subtraction -#define _mlibDurationInfixOperator_minus(Duration, Subtrahend) \ - _mlibDurationSubtract (Duration, _mlibDurationArgument (Subtrahend)) +#define _mlibDurationInfixOperator_minus(LHS, RHS) \ + _mlibDurationSubtract (_mlibDurationArgument (LHS), _mlibDurationArgument (RHS)) static inline mlib_duration _mlibDurationSubtract (const mlib_duration a, const mlib_duration b) mlib_noexcept { @@ -229,7 +228,8 @@ _mlibDurationSubtract (const mlib_duration a, const mlib_duration b) mlib_noexce return ret; } -#define _mlibDurationInfixOperator_min(Duration, RHS) _mlibDurationMinBetween (Duration, _mlibDurationArgument (RHS)) +#define _mlibDurationInfixOperator_min(Duration, RHS) \ + _mlibDurationMinBetween (_mlibDurationArgument (Duration), _mlibDurationArgument (RHS)) static inline mlib_duration _mlibDurationMinBetween (mlib_duration lhs, mlib_duration rhs) { @@ -239,7 +239,8 @@ _mlibDurationMinBetween (mlib_duration lhs, mlib_duration rhs) return rhs; } -#define _mlibDurationInfixOperator_max(Duration, RHS) _mlibDurationMaxBetween (Duration, _mlibDurationArgument (RHS)) +#define _mlibDurationInfixOperator_max(Duration, RHS) \ + _mlibDurationMaxBetween (_mlibDurationArgument (Duration), _mlibDurationArgument (RHS)) static inline mlib_duration _mlibDurationMaxBetween (mlib_duration lhs, mlib_duration rhs) { diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index d1ed6a6344..b15caaa2d8 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -913,7 +913,7 @@ _test_str_view (void) static void _test_duration (void) { - mlib_duration d = mlib_duration_zero (); + mlib_duration d = mlib_duration (); mlib_check (mlib_microseconds_count (d), eq, 0); // Creating durations with the macro name @@ -1002,7 +1002,7 @@ _test_duration (void) mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration (mlib_duration_max (), div, -1); - mlib_check (mlib_duration_cmp (d, mlib_duration_zero ()) < 0); + mlib_check (mlib_duration_cmp (d, mlib_duration ()) < 0); d = mlib_duration (mlib_duration_min (), div, -1); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); mlib_assert_aborts () { From 7dbccb6970b5730bc1fd9961b726868b7b3f59ce Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Mon, 14 Jul 2025 11:26:02 -0600 Subject: [PATCH 24/66] Use mlib_now() in bson monotonic time --- src/common/src/mlib/time_point.h | 10 +++++----- src/libbson/src/bson/bson-clock.c | 25 +++++-------------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 091281be58..5c428e4149 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -57,7 +57,7 @@ typedef struct mlib_time_point { * The stable time point may change between program executions, so this * object should not be stored outside of the program's execution. */ - mlib_duration _time_since_monotonic_start; + mlib_duration time_since_monotonic_start; } mlib_time_point; /** @@ -75,7 +75,7 @@ mlib_now (void) mlib_noexcept mlib_check (rc, eq, 0); // Encode the time point: mlib_time_point ret; - ret._time_since_monotonic_start = mlib_duration_from_timespec (ts); + ret.time_since_monotonic_start = mlib_duration_from_timespec (ts); return ret; #elif defined(_WIN32) // Win32 APIs for the high-performance monotonic counter. These APIs never fail after Windows XP @@ -118,7 +118,7 @@ static inline mlib_time_point mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept { mlib_time_point ret; - ret._time_since_monotonic_start = mlib_duration (from._time_since_monotonic_start, plus, delta); + ret.time_since_monotonic_start = mlib_duration (from.time_since_monotonic_start, plus, delta); return ret; } @@ -142,7 +142,7 @@ mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept static inline mlib_duration mlib_time_difference (mlib_time_point then, mlib_time_point from) { - return mlib_duration (then._time_since_monotonic_start, minus, from._time_since_monotonic_start); + return mlib_duration (then.time_since_monotonic_start, minus, from.time_since_monotonic_start); } /** @@ -164,7 +164,7 @@ mlib_time_difference (mlib_time_point then, mlib_time_point from) static inline enum mlib_cmp_result mlib_time_cmp (mlib_time_point a, mlib_time_point b) mlib_noexcept { - return mlib_duration_cmp (a._time_since_monotonic_start, b._time_since_monotonic_start); + return mlib_duration_cmp (a.time_since_monotonic_start, b.time_since_monotonic_start); } #define mlib_time_cmp(...) MLIB_ARGC_PICK (_mlib_time_cmp, __VA_ARGS__) diff --git a/src/libbson/src/bson/bson-clock.c b/src/libbson/src/bson/bson-clock.c index ee62d51e84..e452b5db5d 100644 --- a/src/libbson/src/bson/bson-clock.c +++ b/src/libbson/src/bson/bson-clock.c @@ -17,6 +17,9 @@ #include #include +#include +#include + #if defined(BSON_HAVE_CLOCK_GETTIME) #include @@ -110,24 +113,6 @@ bson_gettimeofday (struct timeval *tv) /* OUT */ int64_t bson_get_monotonic_time (void) { -#if defined(BSON_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; - /* ts.tv_sec may be a four-byte integer on 32 bit machines, so cast to - * int64_t to avoid truncation. */ - clock_gettime (CLOCK_MONOTONIC, &ts); - return (((int64_t) ts.tv_sec * 1000000) + (ts.tv_nsec / 1000)); -#elif defined(_WIN32) - /* Despite it's name, this is in milliseconds! */ - int64_t ticks = GetTickCount64 (); - return (ticks * 1000); -#elif defined(__hpux__) - int64_t nanosec = gethrtime (); - return (nanosec / 1000UL); -#else -#pragma message "Monotonic clock is not yet supported on your platform." - struct timeval tv; - - bson_gettimeofday (&tv); - return ((int64_t) tv.tv_sec * 1000000) + tv.tv_usec; -#endif + mlib_time_point now = mlib_now (); + return mlib_microseconds_count (now.time_since_monotonic_start); } From 864b35296570ea6b7102f1739a3d4d6c526eb867 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 21:27:12 -0600 Subject: [PATCH 25/66] More time convenience APIs --- src/common/src/mlib/config.h | 7 +-- src/common/src/mlib/duration.h | 11 +++-- src/common/src/mlib/time_point.h | 84 ++++++++++++++++++++++++++++++-- src/common/src/mlib/timer.h | 14 +++++- src/common/tests/test-mlib.c | 4 +- 5 files changed, 103 insertions(+), 17 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 75a0c368db..6689ff6f5f 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -96,10 +96,11 @@ #define _mlibCommaIfParens(...) , /** - * @brief Expands to `1` if the given argument list is a parenthesized - * group of tokens otherwize `0` + * @brief Expands to `1` if the given macro argument is a parenthesized group of + * tokens, otherwize `0` */ -#define MLIB_IS_PARENTHESIZED(X) _mlibHasComma(_mlibCommaIfParens X MLIB_NOTHING(#X)) +#define MLIB_IS_PARENTHESIZED(X) \ + _mlibHasComma(_mlibCommaIfParens X MLIB_NOTHING("Inhibit immediate expansion: " #X)) /** * A helper for isEmpty(): If given (0, 0, 0, 1), expands as: diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index cbaf8abbe9..21a5e60f68 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -141,6 +141,9 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept /* then: */ (_mlibDurationMagic MLIB_NOTHING () X) /* else: */ (X) #define _mlibExpandToNothingIfFollowedByParens(...) +// Wrap a macro argument that should support the duration DSL +#define mlib_duration_arg(X) MLIB_EVAL_8 (_mlibDurationArgument (X)) + // Zero arguments, just return a zero duration: #define _mlib_duration_argc_0() (mlib_init (mlib_duration){0}) // One argument, just copy the duration: @@ -337,10 +340,10 @@ mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept return mlib_cmp (a._rep, b._rep); } -#define mlib_duration_cmp(...) MLIB_EVAL_4 (MLIB_ARGC_PICK (_mlibDurationCmp, __VA_ARGS__)) +#define mlib_duration_cmp(...) MLIB_ARGC_PICK (_mlibDurationCmp, __VA_ARGS__) #define _mlibDurationCmp_argc_2 mlib_duration_cmp #define _mlibDurationCmp_argc_3(Left, Op, Right) \ - (mlib_duration_cmp (_mlibDurationArgument (Left), _mlibDurationArgument (Right)) Op 0) + (mlib_duration_cmp (mlib_duration_arg (Left), mlib_duration_arg (Right)) Op 0) /** * @brief Obtain an mlib_duration that corresponds to a `timespec` value @@ -353,9 +356,7 @@ mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept static inline mlib_duration mlib_duration_from_timespec (const struct timespec ts) mlib_noexcept { - return mlib_duration ((ts.tv_sec, sec), // - plus, - (ts.tv_nsec, ns)); + return mlib_duration ((ts.tv_sec, sec), plus, (ts.tv_nsec, ns)); } /** diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 5c428e4149..59506f326d 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -47,19 +47,53 @@ mlib_extern_c_begin (); /** * @brief An abstract point-in-time type * - * This type represents an abstract stable point in time. + * The time point is encoded as a duration relative to some stable reference point + * provided by the system. See the docs for the `time_since_monotonic_start` + * member for more details. + * + * At time of writing, there is no easy way to convert this monotonic time point + * into a human-readable wall time. Thus, the time-point itself is abstract. */ typedef struct mlib_time_point { /** * @brief The encoding of the time point as a duration relative to some * unspecified stable real point in time. * - * The stable time point may change between program executions, so this - * object should not be stored outside of the program's execution. + * It is important to understand the nature of the reference point: `mlib_now()` + * uses the system's monotonic high-resolution clock, which has an unspecified + * reference point in the past. That stable reference point may change between + * program executions, so it is not safe to store/transmit this value outside + * of the current program execution. + * + * If you attempt to store a duration in this member that is with respect to + * some other clock, then the resulting time point object will have an unspecified + * relationship to other time points created with different clocks. For this reason, + * this member should not be set to any absolute values, and should only be adjusted + * relative to its current value. */ mlib_duration time_since_monotonic_start; } mlib_time_point; +/** + * @brief Given two time points, selects the time point that occurs earliest + */ +static inline mlib_time_point +mlib_soonest (mlib_time_point l, mlib_time_point r) +{ + l.time_since_monotonic_start = mlib_duration (l.time_since_monotonic_start, min, r.time_since_monotonic_start); + return l; +} + +/** + * @brief Given two time points, selects the time point that occurs later + */ +static inline mlib_time_point +mlib_latest (mlib_time_point l, mlib_time_point r) +{ + l.time_since_monotonic_start = mlib_duration (l.time_since_monotonic_start, max, r.time_since_monotonic_start); + return l; +} + /** * @brief Obtain a point-in-time corresponding to the current time */ @@ -69,8 +103,14 @@ mlib_now (void) mlib_noexcept #if mlib_have_posix_clocks() // Use POSIX clock_gettime struct timespec ts; - // Use the POSIX monotonic clock +// Use the POSIX monotonic clock +#ifdef CLOCK_MONOTONIC_RAW + // Linux had a bad definition of CLOCK_MONOTONIC, which would jump based on NTP adjustments. + // They replaced it with CLOCK_MONOTONIC_RAW, which is stable and cannot be adjusted. + int rc = clock_gettime (CLOCK_MONOTONIC_RAW, &ts); +#else int rc = clock_gettime (CLOCK_MONOTONIC, &ts); +#endif // The above call must never fail: mlib_check (rc, eq, 0); // Encode the time point: @@ -122,7 +162,25 @@ mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept return ret; } -#define mlib_later(From, Delta) mlib_later (From, MLIB_EVAL_4 (_mlibDurationArgument (Delta))) +#define mlib_later(From, Delta) mlib_later (From, mlib_duration_arg (Delta)) + +/** + * @brief Adjust a time-point in-place + * + * @param time Pointer to the valid time object to be updated + * @param delta The duration to add/remove from the point-in-time + * @return mlib_time_point Returns the previous value of the time point + */ +static inline mlib_time_point +mlib_time_adjust (mlib_time_point *time, mlib_duration delta) mlib_noexcept +{ + mlib_check (time, because, "Pointer-to-time is required"); + mlib_time_point prev = *time; + *time = mlib_later (*time, delta); + return prev; +} + +#define mlib_time_adjust(Time, Duration) mlib_time_adjust (Time, mlib_duration_arg (Duration)) /** * @brief Obtain the duration between two points in time. @@ -145,6 +203,22 @@ mlib_time_difference (mlib_time_point then, mlib_time_point from) return mlib_duration (then.time_since_monotonic_start, minus, from.time_since_monotonic_start); } +/** + * @brief Obtain the amount of time that has elapsed since the time point `t`, + * or a negative duration if the time is in the future. + * + * @param t The time point to be inspected + * @return mlib_duration If `t` is in the past, returns the duration of time + * that has elapsed since that point-in-time. If `t` is in the future, returns + * a negative time representing the amount of time that be be waited until we + * reach `t`. + */ +static inline mlib_duration +mlib_elapsed_since (mlib_time_point t) +{ + return mlib_time_difference (mlib_now (), t); +} + /** * @brief Compare two time points to create an ordering. * diff --git a/src/common/src/mlib/timer.h b/src/common/src/mlib/timer.h index 141862088f..c7e7edca7b 100644 --- a/src/common/src/mlib/timer.h +++ b/src/common/src/mlib/timer.h @@ -62,13 +62,23 @@ mlib_expires_at (const mlib_time_point t) mlib_noexcept * elapsed from the point-in-time at which this function is called */ static inline mlib_timer -mlib_expiring_after (const mlib_duration dur) mlib_noexcept +mlib_expires_after (const mlib_duration dur) mlib_noexcept { const mlib_time_point later = mlib_later (mlib_now (), dur); return mlib_expires_at (later); } -#define mlib_expiring_after(...) mlib_expiring_after (mlib_duration (__VA_ARGS__)) +#define mlib_expires_after(...) mlib_expires_after (mlib_duration (__VA_ARGS__)) + +/** + * @brief Between two timers, return the timer that will expire the soonest + */ +static inline mlib_timer +mlib_soonest_timer (mlib_timer l, mlib_timer r) mlib_noexcept +{ + l.expires_at = mlib_soonest (l.expires_at, r.expires_at); + return l; +} /** * @brief Obtain the duration of time that is remaining until the given timer diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index b15caaa2d8..2e101f7d3e 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1071,7 +1071,7 @@ _test_sleep (void) static void _test_timer (void) { - mlib_timer tm = mlib_expiring_after (200, ms); + mlib_timer tm = mlib_expires_after (200, ms); mlib_check (!mlib_timer_is_expired (tm)); mlib_sleep_for (250, ms); mlib_check (mlib_timer_is_expired (tm)); @@ -1086,7 +1086,7 @@ _test_timer (void) // Try with a not-yet-expired timer cond = false; - tm = mlib_expiring_after (10, sec); + tm = mlib_expires_after (10, sec); mlib_check (!mlib_timer_is_expired (tm)); mlib_check (!mlib_timer_is_expired (tm, &cond)); // cond was set to `true`, even though we are not yet expired From 4865813768b99007e4e061913713a2d09d2ab003 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 21:38:32 -0600 Subject: [PATCH 26/66] Cleanup --- src/common/src/mlib/duration.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 21a5e60f68..a94abbcc7c 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -118,7 +118,7 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept * * In the above, `` may be a parenthsized `mlib_duration` argument list or a * duration object; `` must be an integral expression and `` is a - * unit suffix identiifer (see: `mlib_duration_with_unit`) to create a duration + * unit suffix identifer (see: `mlib_duration_with_unit`) to create a duration * of `` instances of ``, and `` is one of: * * - `plus`/`minus` to add/subtract two durations. @@ -139,14 +139,13 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept MLIB_IF_ELSE (MLIB_IS_PARENTHESIZED (X)) \ MLIB_NOTHING ("force the 'then' branch to expand first") \ /* then: */ (_mlibDurationMagic MLIB_NOTHING () X) /* else: */ (X) -#define _mlibExpandToNothingIfFollowedByParens(...) // Wrap a macro argument that should support the duration DSL #define mlib_duration_arg(X) MLIB_EVAL_8 (_mlibDurationArgument (X)) // Zero arguments, just return a zero duration: #define _mlib_duration_argc_0() (mlib_init (mlib_duration){0}) -// One argument, just copy the duration: +// One argument, just copy the duration. Passing through a function forces the type to be correct #define _mlib_duration_argc_1(D) _mlibDurationCopy (D) // Two arguments, the second arg is a unit suffix: #define _mlib_duration_argc_2(Count, Unit) mlib_duration_with_unit (Count, Unit) From 38fc4304fd97e65af944635f08ddd710fa1bfe62 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 21:57:46 -0600 Subject: [PATCH 27/66] Refactor of async command to use new time APIs The following changes are made, in order of significance: 1. The `mongoc_stream_poll` function declaration now says the units on its timeout. 2. Add a `_mongoc_stream_poll_internal` that takes a deadline timer rather than a timeout duration. 3. Move async-command typedefs into the async-command header. 4. async command creation now takes typed durations instead of integers 5. Async command internally uses timers, time points, and durations, rather than juggling `int64_t` for everything. 7. Heavy comments in async-cmd files are added all over the place to explain what is going on. --- .../src/mongoc/mongoc-async-cmd-private.h | 262 +++++++++++++++++- src/libmongoc/src/mongoc/mongoc-async-cmd.c | 119 ++++---- .../src/mongoc/mongoc-async-private.h | 25 +- src/libmongoc/src/mongoc/mongoc-async.c | 54 ++-- .../src/mongoc/mongoc-stream-private.h | 12 + src/libmongoc/src/mongoc/mongoc-stream.c | 17 +- src/libmongoc/src/mongoc/mongoc-stream.h | 11 +- .../src/mongoc/mongoc-topology-scanner.c | 80 +++--- src/libmongoc/tests/test-mongoc-async.c | 26 +- 9 files changed, 442 insertions(+), 164 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index 49ecd2bc0e..e177c6d198 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -30,34 +30,142 @@ #include +#include +#include +#include + BSON_BEGIN_DECLS typedef enum { + // The command has no stream and needs to connect to a peer MONGOC_ASYNC_CMD_INITIATE, + // The command has connected and has a stream, but needs to run stream setup MONGOC_ASYNC_CMD_SETUP, + // The command has data to send to the peer MONGOC_ASYNC_CMD_SEND, + // The command is ready to receive the response length header MONGOC_ASYNC_CMD_RECV_LEN, + // The command is ready to receive the RPC message MONGOC_ASYNC_CMD_RECV_RPC, + // The command is in an invalid error state MONGOC_ASYNC_CMD_ERROR_STATE, + // The command has been cancelled. MONGOC_ASYNC_CMD_CANCELED_STATE, } mongoc_async_cmd_state_t; +/** + * @brief Command callback/state result code + */ +typedef enum { + MONGOC_ASYNC_CMD_CONNECTED, + MONGOC_ASYNC_CMD_IN_PROGRESS, + MONGOC_ASYNC_CMD_SUCCESS, + MONGOC_ASYNC_CMD_ERROR, + MONGOC_ASYNC_CMD_TIMEOUT, +} mongoc_async_cmd_result_t; + +/** + * @brief Callback type associated with an asynchronous command object. + * + * The callback will be invoked after a new connection is established, and + * again when the command completes. + * + * @param acmd Pointer to the async command object that invoked the callback + * @param result The result/state of the asynchronous command object + * @param bson Result data associated with the command's state, if any. + * @param duration The elapsed duration that the command object has been running. + * This will be zero when the CONNECTED state is invoked. + */ +typedef void (*mongoc_async_cmd_cb_t) (struct _mongoc_async_cmd *acmd, + mongoc_async_cmd_result_t result, + const bson_t *bson, + mlib_duration duration); + +/** + * @brief Callback that is used to open a new stream for a command object. + * + * If the function returns a null pointer, it is considered to have failed. + */ +typedef mongoc_stream_t *(*mongoc_async_cmd_connect_cb) (struct _mongoc_async_cmd *); + +/** + * @brief Stream setup callback for asynchronous commands + * + * This callback will be invoked by the async command runner after a stream has + * been opened, allowing the command creator time to do setup on the stream + * before the command tries to use it. + * + * @param stream Pointer to the valid stream object that was just created + * @param events Pointer to a poll() events bitmask. The function can modify this + * value to change what the stream will await on next + * @param ctx Pointer to arbitrary user data for the setup function. + * @param timeout A timer that gives a deadline for the setup operation + * @return int The function should return -1 on failure, 1 if the stream + * immediately has data to send, or 0 for generic success. + */ +typedef int (*mongoc_async_cmd_setup_t) ( + mongoc_stream_t *stream, int *events, void *ctx, mlib_timer timeout, bson_error_t *error); + + typedef struct _mongoc_async_cmd { + /** + * @brief The stream that is associated with an in-progress command. + * + * This may start as a null pointer, but is updated when a connection is + * established. + */ mongoc_stream_t *stream; + // Non-owning pointer to the asynchrony engine associated with this command mongoc_async_t *async; + /** + * @brief The current state of the asynchronous command. + * + * Used to control the state machine that is executed with async_cmd_run() + */ mongoc_async_cmd_state_t state; + // Bitmask of poll() events that this command is waiting to see int events; - mongoc_async_cmd_initiate_t initiator; + /** + * @brief User-provided callback that will be used to lazily create the I/O stream + * for the command. + */ + mongoc_async_cmd_connect_cb initiator; + /** + * @brief User-provided callback function to do setup on the command's stream + * after the stream has been created automatically. + */ mongoc_async_cmd_setup_t setup; + // Arbitrary userdata pointer passed to the stream setup function void *setup_ctx; + /** + * @brief User-provided command event callback. Invoked with the final result + * and once when the command completes. + */ mongoc_async_cmd_cb_t cb; + // Arbitrary userdata passed when the object was created void *data; + /** + * @brief Timer to when the command should attempt to lazily initiate a new + * connection with the _stream_connect callback. This does not apply if the + * command was given a stream upon construction. + */ + mlib_timer initiate_delay_timer; + /** + * @brief The "start" reference point-in-time for the command object + * + * This is used to determine how long the command has been in progress, + * including for when to consider the command to have timed-out. + */ + mlib_time_point _start_time; + // The timeout duration allotted to this command object + mlib_duration _timeout; + + bson_error_t error; - int64_t initiate_delay_ms; - int64_t connect_started; - int64_t cmd_started; - int64_t timeout_msec; + /** + * @brief The BSON document of the command to be executed on the server. + */ bson_t cmd; mongoc_buffer_t buffer; mongoc_iovec_t *iovec; @@ -65,40 +173,172 @@ typedef struct _mongoc_async_cmd { size_t bytes_written; size_t bytes_to_read; mcd_rpc_message *rpc; + /** + * @brief The response data from the peer. + * + * Initialized with BSON_INITIALIZER, so safe to pass/destroy upon construction. + */ bson_t reply; - bool reply_needs_cleanup; char *ns; + /** + * @brief The DNS address info that was associated with the command when + * it was created. May be null if no DNS result was provided. + */ struct addrinfo *dns_result; struct _mongoc_async_cmd *next; struct _mongoc_async_cmd *prev; } mongoc_async_cmd_t; +/** + * @brief Create a new asynchronous command object associated with a collection + * of async commands + * + * @param async The async engine that will own this new command + * @param stream (Optional) a stream to be associated with the new command. If + * NULL, then a stream will be created lazily for the command object. + * @param dns_result Pointer to a DNS result associated with the command + * @param connect_callback Callback function that will be used to establish a + * new stream for the command object if `stream` is null. + * @param connect_delay The amount of time that the command should wait before + * we try to connect the deferred stream for the command. + * @param setup The stream setup callback for the command object. + * @param setup_ctx Arbitrary data passed to the `setup` callback. + * @param dbname The database name associated with the command. Required for OP_MSG + * @param cmd The BSON data that will be sent in the command message + * @param cmd_opcode The wire protocol opcode for the command + * @param cb A callback that is invoked during events associated with the command. + * @param userdata Arbitrary data pointer associated with the command object + * @param timeout A timeout for the command. @see _acmd_reset_elapsed + * @return mongoc_async_cmd_t* A newly created asynchronous command object + */ mongoc_async_cmd_t * mongoc_async_cmd_new (mongoc_async_t *async, mongoc_stream_t *stream, bool is_setup_done, struct addrinfo *dns_result, - mongoc_async_cmd_initiate_t initiator, - int64_t initiate_delay_ms, + mongoc_async_cmd_connect_cb connect_callback, + mlib_duration connect_delay, mongoc_async_cmd_setup_t setup, void *setup_ctx, const char *dbname, const bson_t *cmd, const int32_t cmd_opcode, mongoc_async_cmd_cb_t cb, - void *cb_data, - int64_t timeout_msec); + void *userdata, + mlib_duration timeout); + +/** + * @brief Obtain a deadline timer that will expire when the given async command + * will time out. + * + * Note that the initiation time of the command can be changed, which will also + * adjust the point-in-time at which it expires. + */ +static inline mlib_timer +_acmd_deadline (const mongoc_async_cmd_t *self) +{ + return mlib_expires_at (mlib_later (self->_start_time, self->_timeout)); +} + +/** + * @brief Determine whether the given async command object has timed out + */ +static inline bool +_acmd_has_timed_out (const mongoc_async_cmd_t *self) +{ + return mlib_timer_is_expired (_acmd_deadline (self)); +} + +/** + * @brief Cancel an in-progress command. + * + * This doesn't immediately destroy any resources or perform I/O, it just marks + * the command to abort the next time it is polled. + */ +static inline void +_acmd_cancel (mongoc_async_cmd_t *self) +{ + // XXX: Should this check if ther command has already finished/failed? + self->state = MONGOC_ASYNC_CMD_ERROR_STATE; +} + +/** + * @brief Adjust the connect-delay timer for an async command by the given duration + * + * @param d A duration to be added/removed to the command's connect wait. + * + * This only effects commands that don't have an open stream and are pending a + * connect. If this causes the connect-delay timer to expire, then the command + * will attempt to connect the next time it is polled. + */ +static inline void +_acmd_adjust_connect_delay (mongoc_async_cmd_t *self, const mlib_duration d) +{ + mlib_time_adjust (&self->initiate_delay_timer.expires_at, d); +} + +/** + * @brief Reset the elapsed time for the command, changing when it will timeout + * + * XXX: This is a HACK to fix CDRIVER-1571. The problem is that the provided deferred + * connect (_stream_setup and/or _stream_connect) callbacks can perform blocking + * I/O that delays everyone in the async pool, which can cause other commands + * to exceed their timeout because one operation is blocking the entire pool. + * + * This function has the side effect that a command can exceed its allotted timeout + * because this function is called multiple times, so only a single individual I/O + * operation can actually timeout, rather than the entire composed operation. + * + * The proper fix is to force `_stream_setup` and `_stream_connect` to be + * non-blocking, and the reference start time for the command can remain fixed. + */ +static inline void +_acmd_reset_elapsed (mongoc_async_cmd_t *self) +{ + self->_start_time = mlib_now (); +} + +/** + * @brief Obtain the amount of time that the command has been running + */ +static inline mlib_duration +_acmd_elapsed (mongoc_async_cmd_t const *self) +{ + return mlib_elapsed_since (self->_start_time); +} + +/** + * @brief Obtain the userdata pointer associated with the given async command + * object + * + * @param T The type to read from the pointer + * @param Command Pointer to a command object + */ +#define _acmd_userdata(T, Command) ((T *) ((Command)->data)) void mongoc_async_cmd_destroy (mongoc_async_cmd_t *acmd); +/** + * @brief Pump the asynchronous command object state machine. + * + * If this function completes the command, it will destroy the async command + * object and return `false`. Otherwise, it will return `true`. + */ bool mongoc_async_cmd_run (mongoc_async_cmd_t *acmd); #ifdef MONGOC_ENABLE_SSL +/** + * @brief Stream setup callback. Initializes TLS on the stream before the command runner tries to use it. + * + * @param ctx The userdata for the TLS setup is the hostname string for the peer. + * + * Refer to `mongoc_async_cmd_stream_setup_cb` for signature details + */ int -mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, int32_t timeout_msec, bson_error_t *error); +mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, mlib_timer deadline, bson_error_t *error); #endif BSON_END_DECLS diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index f32cdc2e3e..1ab036571a 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -30,7 +30,10 @@ #include +#include #include +#include +#include #ifdef MONGOC_ENABLE_SSL #include @@ -38,20 +41,20 @@ typedef mongoc_async_cmd_result_t (*_mongoc_async_cmd_phase_t) (mongoc_async_cmd_t *cmd); -mongoc_async_cmd_result_t +static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_initiate (mongoc_async_cmd_t *cmd); -mongoc_async_cmd_result_t -_mongoc_async_cmd_phase_setup (mongoc_async_cmd_t *cmd); -mongoc_async_cmd_result_t +static mongoc_async_cmd_result_t +_mongoc_async_cmd_phase_stream_setup (mongoc_async_cmd_t *cmd); +static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_send (mongoc_async_cmd_t *cmd); -mongoc_async_cmd_result_t +static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_recv_len (mongoc_async_cmd_t *cmd); -mongoc_async_cmd_result_t +static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_recv_rpc (mongoc_async_cmd_t *cmd); static const _mongoc_async_cmd_phase_t gMongocCMDPhases[] = { _mongoc_async_cmd_phase_initiate, - _mongoc_async_cmd_phase_setup, + _mongoc_async_cmd_phase_stream_setup, _mongoc_async_cmd_phase_send, _mongoc_async_cmd_phase_recv_len, _mongoc_async_cmd_phase_recv_rpc, @@ -61,7 +64,7 @@ static const _mongoc_async_cmd_phase_t gMongocCMDPhases[] = { #ifdef MONGOC_ENABLE_SSL int -mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, int32_t timeout_msec, bson_error_t *error) +mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, mlib_timer deadline, bson_error_t *error) { mongoc_stream_t *tls_stream; const char *host = (const char *) ctx; @@ -72,11 +75,13 @@ mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, int tls_stream = mongoc_stream_get_base_stream (tls_stream)) { } + mlib_duration_rep_t remain_ms = mlib_milliseconds_count (mlib_timer_remaining (deadline)); + #if defined(MONGOC_ENABLE_SSL_OPENSSL) || defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) /* pass 0 for the timeout to begin / continue non-blocking handshake */ - timeout_msec = 0; + remain_ms = 0; #endif - if (mongoc_stream_tls_handshake (tls_stream, host, timeout_msec, &retry_events, error)) { + if (mongoc_stream_tls_handshake (tls_stream, host, remain_ms, &retry_events, error)) { return 1; } @@ -91,39 +96,38 @@ mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, int bool mongoc_async_cmd_run (mongoc_async_cmd_t *acmd) { - mongoc_async_cmd_result_t result; - int64_t duration_usec; - _mongoc_async_cmd_phase_t phase_callback; - - BSON_ASSERT (acmd); + BSON_ASSERT_PARAM (acmd); /* if we have successfully connected to the node, call the callback. */ if (acmd->state == MONGOC_ASYNC_CMD_SEND) { - acmd->cb (acmd, MONGOC_ASYNC_CMD_CONNECTED, NULL, 0); + acmd->cb (acmd, MONGOC_ASYNC_CMD_CONNECTED, NULL, _acmd_elapsed (acmd)); } - phase_callback = gMongocCMDPhases[acmd->state]; - if (phase_callback) { - result = phase_callback (acmd); - } else { - result = MONGOC_ASYNC_CMD_ERROR; - } + _mongoc_async_cmd_phase_t const phase_callback = gMongocCMDPhases[acmd->state]; + mongoc_async_cmd_result_t const result = phase_callback // + ? phase_callback (acmd) + : MONGOC_ASYNC_CMD_ERROR; - if (result == MONGOC_ASYNC_CMD_IN_PROGRESS) { + switch (result) { + case MONGOC_ASYNC_CMD_IN_PROGRESS: + // No callback on progress events. Just return true to tell the caller + // that there's more work to do. return true; + case MONGOC_ASYNC_CMD_CONNECTED: + mlib_check (false, because, "We should not reach this state"); + abort (); + case MONGOC_ASYNC_CMD_SUCCESS: + case MONGOC_ASYNC_CMD_ERROR: + case MONGOC_ASYNC_CMD_TIMEOUT: + acmd->cb (acmd, result, &acmd->reply, _acmd_elapsed (acmd)); + // No more work on this command. Destroy the object and tell the caller + // it's been removed + mongoc_async_cmd_destroy (acmd); + return false; + default: + mlib_check (false, because, "Invalid async command state"); + abort (); } - - duration_usec = bson_get_monotonic_time () - acmd->cmd_started; - - if (result == MONGOC_ASYNC_CMD_SUCCESS) { - acmd->cb (acmd, result, &acmd->reply, duration_usec); - } else { - /* we're in ERROR, TIMEOUT, or CANCELED */ - acmd->cb (acmd, result, NULL, duration_usec); - } - - mongoc_async_cmd_destroy (acmd); - return false; } static void @@ -165,10 +169,13 @@ void _mongoc_async_cmd_state_start (mongoc_async_cmd_t *acmd, bool is_setup_done) { if (!acmd->stream) { + // No stream yet associated, so we need to initiate a new connection acmd->state = MONGOC_ASYNC_CMD_INITIATE; } else if (acmd->setup && !is_setup_done) { + // We have a stream, and a setup callback, so call that setup callback next acmd->state = MONGOC_ASYNC_CMD_SETUP; } else { + // We have a stream, and no setup required. We're ready to send immediately. acmd->state = MONGOC_ASYNC_CMD_SEND; } @@ -180,32 +187,34 @@ mongoc_async_cmd_new (mongoc_async_t *async, mongoc_stream_t *stream, bool is_setup_done, struct addrinfo *dns_result, - mongoc_async_cmd_initiate_t initiator, - int64_t initiate_delay_ms, + mongoc_async_cmd_connect_cb initiator, + mlib_duration initiate_delay, mongoc_async_cmd_setup_t setup, void *setup_ctx, const char *dbname, const bson_t *cmd, const int32_t cmd_opcode, /* OP_QUERY or OP_MSG */ mongoc_async_cmd_cb_t cb, - void *cb_data, - int64_t timeout_msec) + void *userdata, + mlib_duration timeout) { BSON_ASSERT_PARAM (cmd); BSON_ASSERT_PARAM (dbname); mongoc_async_cmd_t *const acmd = BSON_ALIGNED_ALLOC0 (mongoc_async_cmd_t); + acmd->initiate_delay_timer = mlib_expires_after (initiate_delay); acmd->async = async; acmd->dns_result = dns_result; - acmd->timeout_msec = timeout_msec; + acmd->_timeout = timeout; acmd->stream = stream; acmd->initiator = initiator; - acmd->initiate_delay_ms = initiate_delay_ms; acmd->setup = setup; acmd->setup_ctx = setup_ctx; acmd->cb = cb; - acmd->data = cb_data; - acmd->connect_started = bson_get_monotonic_time (); + acmd->data = userdata; + acmd->_start_time = mlib_now (); + acmd->state = MONGOC_ASYNC_CMD_INITIATE; + acmd->reply = (bson_t) BSON_INITIALIZER; bson_copy_to (cmd, &acmd->cmd); if (MONGOC_OP_CODE_MSG == cmd_opcode) { @@ -237,10 +246,7 @@ mongoc_async_cmd_destroy (mongoc_async_cmd_t *acmd) acmd->async->ncmds--; bson_destroy (&acmd->cmd); - - if (acmd->reply_needs_cleanup) { - bson_destroy (&acmd->reply); - } + bson_destroy (&acmd->reply); bson_free (acmd->iovec); _mongoc_buffer_destroy (&acmd->buffer); @@ -257,23 +263,22 @@ _mongoc_async_cmd_phase_initiate (mongoc_async_cmd_t *acmd) if (!acmd->stream) { return MONGOC_ASYNC_CMD_ERROR; } - /* reset the connect started time after connection starts. */ - acmd->connect_started = bson_get_monotonic_time (); + + _acmd_reset_elapsed (acmd); if (acmd->setup) { + // There is a setup callback that we need to call acmd->state = MONGOC_ASYNC_CMD_SETUP; } else { + // There is no setup callback, so we can send data immediately acmd->state = MONGOC_ASYNC_CMD_SEND; } return MONGOC_ASYNC_CMD_IN_PROGRESS; } -mongoc_async_cmd_result_t -_mongoc_async_cmd_phase_setup (mongoc_async_cmd_t *acmd) +static mongoc_async_cmd_result_t +_mongoc_async_cmd_phase_stream_setup (mongoc_async_cmd_t *acmd) { - int retval; - - BSON_ASSERT (acmd->timeout_msec < INT32_MAX); - retval = acmd->setup (acmd->stream, &acmd->events, acmd->setup_ctx, (int32_t) acmd->timeout_msec, &acmd->error); + int const retval = acmd->setup (acmd->stream, &acmd->events, acmd->setup_ctx, _acmd_deadline (acmd), &acmd->error); switch (retval) { case -1: return MONGOC_ASYNC_CMD_ERROR; @@ -358,7 +363,7 @@ _mongoc_async_cmd_phase_send (mongoc_async_cmd_t *acmd) acmd->bytes_to_read = 4; acmd->events = POLLIN; - acmd->cmd_started = bson_get_monotonic_time (); + _acmd_reset_elapsed (acmd); return MONGOC_ASYNC_CMD_IN_PROGRESS; } @@ -455,8 +460,6 @@ _mongoc_async_cmd_phase_recv_rpc (mongoc_async_cmd_t *acmd) return MONGOC_ASYNC_CMD_ERROR; } - acmd->reply_needs_cleanup = true; - return MONGOC_ASYNC_CMD_SUCCESS; } diff --git a/src/libmongoc/src/mongoc/mongoc-async-private.h b/src/libmongoc/src/mongoc/mongoc-async-private.h index 3a225f16f2..c1cbe40717 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-private.h @@ -14,15 +14,19 @@ * limitations under the License. */ -#include #ifndef MONGOC_ASYNC_PRIVATE_H #define MONGOC_ASYNC_PRIVATE_H +#include + #include #include +#include +#include + BSON_BEGIN_DECLS struct _mongoc_async_cmd; @@ -33,25 +37,6 @@ typedef struct _mongoc_async { uint32_t request_id; } mongoc_async_t; -typedef enum { - MONGOC_ASYNC_CMD_CONNECTED, - MONGOC_ASYNC_CMD_IN_PROGRESS, - MONGOC_ASYNC_CMD_SUCCESS, - MONGOC_ASYNC_CMD_ERROR, - MONGOC_ASYNC_CMD_TIMEOUT, -} mongoc_async_cmd_result_t; - -typedef void (*mongoc_async_cmd_cb_t) (struct _mongoc_async_cmd *acmd, - mongoc_async_cmd_result_t result, - const bson_t *bson, - int64_t duration_usec); - -typedef mongoc_stream_t *(*mongoc_async_cmd_initiate_t) (struct _mongoc_async_cmd *); - -typedef int (*mongoc_async_cmd_setup_t) ( - mongoc_stream_t *stream, int *events, void *ctx, int32_t timeout_msec, bson_error_t *error); - - mongoc_async_t * mongoc_async_new (void); diff --git a/src/libmongoc/src/mongoc/mongoc-async.c b/src/libmongoc/src/mongoc/mongoc-async.c index f520112d6b..238202696f 100644 --- a/src/libmongoc/src/mongoc/mongoc-async.c +++ b/src/libmongoc/src/mongoc/mongoc-async.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ #include #include +#include mongoc_async_t * @@ -57,20 +59,13 @@ mongoc_async_run (mongoc_async_t *async) mongoc_async_cmd_t *acmd, *tmp; mongoc_async_cmd_t **acmds_polled = NULL; mongoc_stream_poll_t *poller = NULL; - int nstreams, i; ssize_t nactive = 0; - int64_t now; - int64_t expire_at; - int64_t poll_timeout_msec; - size_t poll_size; + size_t poll_size = 0; - now = bson_get_monotonic_time (); - poll_size = 0; - - /* CDRIVER-1571 reset start times in case a stream initiator was slow */ DL_FOREACH (async->cmds, acmd) { - acmd->connect_started = now; + // XXX: See _acmd_reset_elapsed doc comment to explain this hack + _acmd_reset_elapsed (acmd); } while (async->ncmds) { @@ -81,25 +76,32 @@ mongoc_async_run (mongoc_async_t *async) poll_size = async->ncmds; } - expire_at = INT64_MAX; - nstreams = 0; + // Number of streams in the poller object + unsigned nstreams = 0; + + // The timer to wake up the poll() + mlib_timer poll_timer = mlib_expires_after (mlib_duration_max ()); /* check if any cmds are ready to be initiated. */ DL_FOREACH_SAFE (async->cmds, acmd, tmp) { if (acmd->state == MONGOC_ASYNC_CMD_INITIATE) { + // Command is waiting to be initiated. + // Timer for when the command should be initiated: + // Should not yet have an associated stream BSON_ASSERT (!acmd->stream); - if (now >= acmd->initiate_delay_ms * 1000 + acmd->connect_started) { + if (mlib_timer_is_expired (acmd->initiate_delay_timer)) { /* time to initiate. */ if (mongoc_async_cmd_run (acmd)) { + // We should now have an associated stream BSON_ASSERT (acmd->stream); } else { /* this command was removed. */ continue; } } else { - /* don't poll longer than the earliest cmd ready to init. */ - expire_at = BSON_MIN (expire_at, acmd->connect_started + acmd->initiate_delay_ms); + // Wake up poll() when the initiation timeout is hit + poll_timer = mlib_soonest_timer (poll_timer, acmd->initiate_delay_timer); } } @@ -108,7 +110,8 @@ mongoc_async_run (mongoc_async_t *async) poller[nstreams].stream = acmd->stream; poller[nstreams].events = acmd->events; poller[nstreams].revents = 0; - expire_at = BSON_MIN (expire_at, acmd->connect_started + acmd->timeout_msec * 1000); + // Wake up poll() if the object's overall timeout is hit + poll_timer = mlib_soonest_timer (poll_timer, _acmd_deadline (acmd)); ++nstreams; } } @@ -118,21 +121,18 @@ mongoc_async_run (mongoc_async_t *async) break; } - poll_timeout_msec = BSON_MAX (0, (expire_at - now) / 1000); - BSON_ASSERT (poll_timeout_msec < INT32_MAX); - if (nstreams > 0) { /* we need at least one stream to poll. */ - nactive = mongoc_stream_poll (poller, nstreams, (int32_t) poll_timeout_msec); + nactive = _mongoc_stream_poll_internal (poller, nstreams, poll_timer); } else { /* currently this does not get hit. we always have at least one command * initialized with a stream. */ - mlib_sleep_for (poll_timeout_msec, ms); + mlib_sleep_until (poll_timer.expires_at); } if (nactive > 0) { - for (i = 0; i < nstreams; i++) { - mongoc_async_cmd_t *iter = acmds_polled[i]; + mlib_foreach_urange (i, nstreams) { + mongoc_async_cmd_t *const iter = acmds_polled[i]; if (poller[i].revents & (POLLERR | POLLHUP)) { int hup = poller[i].revents & POLLHUP; if (iter->state == MONGOC_ASYNC_CMD_SEND) { @@ -164,25 +164,23 @@ mongoc_async_run (mongoc_async_t *async) DL_FOREACH_SAFE (async->cmds, acmd, tmp) { /* check if an initiated cmd has passed the connection timeout. */ - if (acmd->state != MONGOC_ASYNC_CMD_INITIATE && now > acmd->connect_started + acmd->timeout_msec * 1000) { + if (acmd->state != MONGOC_ASYNC_CMD_INITIATE && _acmd_has_timed_out (acmd)) { _mongoc_set_error (&acmd->error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, acmd->state == MONGOC_ASYNC_CMD_SEND ? "connection timeout" : "socket timeout"); - acmd->cb (acmd, MONGOC_ASYNC_CMD_TIMEOUT, NULL, (now - acmd->connect_started) / 1000); + acmd->cb (acmd, MONGOC_ASYNC_CMD_TIMEOUT, NULL, _acmd_elapsed (acmd)); /* Remove acmd from the async->cmds doubly-linked list */ mongoc_async_cmd_destroy (acmd); } else if (acmd->state == MONGOC_ASYNC_CMD_CANCELED_STATE) { - acmd->cb (acmd, MONGOC_ASYNC_CMD_ERROR, NULL, (now - acmd->connect_started) / 1000); + acmd->cb (acmd, MONGOC_ASYNC_CMD_ERROR, NULL, _acmd_elapsed (acmd)); /* Remove acmd from the async->cmds doubly-linked list */ mongoc_async_cmd_destroy (acmd); } } - - now = bson_get_monotonic_time (); } bson_free (poller); diff --git a/src/libmongoc/src/mongoc/mongoc-stream-private.h b/src/libmongoc/src/mongoc/mongoc-stream-private.h index f39e360388..3a2b9e92ae 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-private.h +++ b/src/libmongoc/src/mongoc/mongoc-stream-private.h @@ -22,6 +22,8 @@ #include #include +#include + BSON_BEGIN_DECLS @@ -44,6 +46,16 @@ _mongoc_stream_writev_full ( mongoc_stream_t * mongoc_stream_get_root_stream (mongoc_stream_t *stream); +/** + * @brief Poll the given set of streams + * + * @param streams Pointer to an array of stream polling parameters + * @param nstreams The number of streams in the array + * @param until A timer that will wake up `poll()` from blocking + */ +ssize_t +_mongoc_stream_poll_internal (mongoc_stream_poll_t *streams, size_t nstreams, mlib_timer until); + BSON_END_DECLS diff --git a/src/libmongoc/src/mongoc/mongoc-stream.c b/src/libmongoc/src/mongoc/mongoc-stream.c index 576445e362..3de1d3d7bf 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream.c +++ b/src/libmongoc/src/mongoc/mongoc-stream.c @@ -33,6 +33,8 @@ #include #include +#include +#include #undef MONGOC_LOG_DOMAIN @@ -326,7 +328,13 @@ mongoc_stream_get_tls_stream (mongoc_stream_t *stream) /* IN */ } ssize_t -mongoc_stream_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t timeout) +mongoc_stream_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t timeout_ms) +{ + return _mongoc_stream_poll_internal (streams, nstreams, mlib_expires_after (mlib_duration (timeout_ms, ms))); +} + +ssize_t +_mongoc_stream_poll_internal (mongoc_stream_poll_t *streams, size_t nstreams, mlib_timer until) { mongoc_stream_poll_t *poller = (mongoc_stream_poll_t *) bson_malloc (sizeof (*poller) * nstreams); @@ -353,7 +361,12 @@ mongoc_stream_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t time goto CLEANUP; } - rval = poller[0].stream->poll (poller, nstreams, timeout); + int32_t time_remain_ms = 0; + if (mlib_narrow (&time_remain_ms, mlib_milliseconds_count (mlib_timer_remaining (until)))) { + // Too many ms, just use the max + time_remain_ms = INT32_MAX; + } + rval = poller[0].stream->poll (poller, nstreams, time_remain_ms); if (rval > 0) { for (size_t i = 0u; i < nstreams; i++) { diff --git a/src/libmongoc/src/mongoc/mongoc-stream.h b/src/libmongoc/src/mongoc/mongoc-stream.h index 4bfe7cef98..669a6f3928 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream.h +++ b/src/libmongoc/src/mongoc/mongoc-stream.h @@ -83,8 +83,17 @@ MONGOC_EXPORT (bool) mongoc_stream_timed_out (mongoc_stream_t *stream); MONGOC_EXPORT (bool) mongoc_stream_should_retry (mongoc_stream_t *stream); + +/** + * @brief Poll a set of streams + * + * @param streams Pointer to an array of streams to be polled + * @param nstreams The number of streams in the array pointed-to by `streams` + * @param timeout_ms The maximum number of milliseconds to poll + * + */ MONGOC_EXPORT (ssize_t) -mongoc_stream_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t timeout); +mongoc_stream_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t timeout_ms); BSON_END_DECLS diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c index 3007e223a7..05eb496631 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -26,6 +27,8 @@ #include +#include + #ifdef MONGOC_ENABLE_SSL #include #endif @@ -78,7 +81,7 @@ static void _async_handler (mongoc_async_cmd_t *acmd, mongoc_async_cmd_result_t async_status, const bson_t *hello_response, - int64_t duration_usec); + mlib_duration duration); static void _mongoc_topology_scanner_monitor_heartbeat_started (const mongoc_topology_scanner_t *ts, @@ -101,6 +104,21 @@ _mongoc_topology_scanner_monitor_heartbeat_failed (const mongoc_topology_scanner static void _delete_retired_nodes (mongoc_topology_scanner_t *ts); +// Get the scanner node associated with an async command +static mongoc_topology_scanner_node_t * +_scanner_node_of (mongoc_async_cmd_t const *a) +{ + return _acmd_userdata (mongoc_topology_scanner_node_t, a); +} + +// Test whether two async commands are associated with the same topology scanner node, +// and aren't the same command object +static bool +_is_sibling_command (mongoc_async_cmd_t const *l, mongoc_async_cmd_t *r) +{ + return l != r && _scanner_node_of (l) == _scanner_node_of (r); +} + /* cancel any pending async commands for a specific node excluding acmd. * If acmd is NULL, cancel all async commands on the node. */ static void @@ -110,9 +128,23 @@ _cancel_commands_excluding (mongoc_topology_scanner_node_t *node, mongoc_async_c static int _count_acmds (mongoc_topology_scanner_node_t *node); -/* if acmd fails, schedule the sibling commands sooner. */ + +/** + * @brief Cause all sibling commands to initiate sooner + */ static void -_jumpstart_other_acmds (mongoc_topology_scanner_node_t *node, mongoc_async_cmd_t *acmd); +_jumpstart_other_acmds (mongoc_async_cmd_t const *const self) +{ + mongoc_async_cmd_t *other; + DL_FOREACH (self->async->cmds, other) + { + // Only consider commands on the same node + if (_is_sibling_command (self, other)) { + // Decrease the delay by the happy eyeballs duration. + _acmd_adjust_connect_delay (other, mlib_duration (-HAPPY_EYEBALLS_DELAY_MS, ms)); + } + } +} static void _add_hello (mongoc_topology_scanner_t *ts) @@ -397,7 +429,7 @@ _begin_hello_cmd (mongoc_topology_scanner_node_t *node, is_setup_done, dns_result, _mongoc_topology_scanner_tcp_initiate, - initiate_delay_ms, + mlib_duration (initiate_delay_ms, ms), ts->setup, node->host.host, "admin", @@ -405,7 +437,7 @@ _begin_hello_cmd (mongoc_topology_scanner_node_t *node, cmd_opcode, &_async_handler, node, - ts->connect_timeout_msec); + mlib_duration (ts->connect_timeout_msec, ms)); bson_destroy (&cmd); } @@ -651,7 +683,7 @@ mongoc_topology_scanner_has_node_for_host (mongoc_topology_scanner_t *ts, mongoc static void _async_connected (mongoc_async_cmd_t *acmd) { - mongoc_topology_scanner_node_t *node = (mongoc_topology_scanner_node_t *) acmd->data; + mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); /* this cmd connected successfully, cancel other cmds on this node. */ _cancel_commands_excluding (node, acmd); node->successful_dns_result = acmd->dns_result; @@ -660,9 +692,8 @@ _async_connected (mongoc_async_cmd_t *acmd) static void _async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, int64_t duration_usec) { - void *data = acmd->data; - mongoc_topology_scanner_node_t *node = (mongoc_topology_scanner_node_t *) data; - mongoc_stream_t *stream = acmd->stream; + mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); + mongoc_stream_t *const stream = acmd->stream; mongoc_topology_scanner_t *ts = node->ts; if (node->retired) { @@ -706,8 +737,7 @@ _async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, int64_t static void _async_error_or_timeout (mongoc_async_cmd_t *acmd, int64_t duration_usec, const char *default_err_msg) { - void *data = acmd->data; - mongoc_topology_scanner_node_t *node = (mongoc_topology_scanner_node_t *) data; + mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); mongoc_stream_t *stream = acmd->stream; mongoc_topology_scanner_t *ts = node->ts; bson_error_t *error = &acmd->error; @@ -759,7 +789,7 @@ _async_error_or_timeout (mongoc_async_cmd_t *acmd, int64_t duration_usec, const } else { /* there are still more commands left for this node or it succeeded * with another stream. skip the topology scanner callback. */ - _jumpstart_other_acmds (node, acmd); + _jumpstart_other_acmds (acmd); } } @@ -776,10 +806,9 @@ static void _async_handler (mongoc_async_cmd_t *acmd, mongoc_async_cmd_result_t async_status, const bson_t *hello_response, - int64_t duration_usec) + mlib_duration duration) { - BSON_ASSERT (acmd->data); - + const int64_t duration_usec = mlib_microseconds_count (duration); switch (async_status) { case MONGOC_ASYNC_CMD_CONNECTED: _async_connected (acmd); @@ -836,7 +865,7 @@ _mongoc_topology_scanner_node_setup_stream_for_tls (mongoc_topology_scanner_node mongoc_stream_t * _mongoc_topology_scanner_tcp_initiate (mongoc_async_cmd_t *acmd) { - mongoc_topology_scanner_node_t *node = (mongoc_topology_scanner_node_t *) acmd->data; + mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); struct addrinfo *res = acmd->dns_result; mongoc_socket_t *sock = NULL; @@ -1410,8 +1439,8 @@ _cancel_commands_excluding (mongoc_topology_scanner_node_t *node, mongoc_async_c mongoc_async_cmd_t *iter; DL_FOREACH (node->ts->async->cmds, iter) { - if ((mongoc_topology_scanner_node_t *) iter->data == node && iter != acmd) { - iter->state = MONGOC_ASYNC_CMD_CANCELED_STATE; + if (acmd && _is_sibling_command (iter, acmd)) { + _acmd_cancel (iter); } } } @@ -1423,26 +1452,13 @@ _count_acmds (mongoc_topology_scanner_node_t *node) int count = 0; DL_FOREACH (node->ts->async->cmds, iter) { - if ((mongoc_topology_scanner_node_t *) iter->data == node) { + if (_scanner_node_of (iter) == node) { ++count; } } return count; } -static void -_jumpstart_other_acmds (mongoc_topology_scanner_node_t *node, mongoc_async_cmd_t *acmd) -{ - mongoc_async_cmd_t *iter; - DL_FOREACH (node->ts->async->cmds, iter) - { - if ((mongoc_topology_scanner_node_t *) iter->data == node && iter != acmd && - acmd->initiate_delay_ms < iter->initiate_delay_ms) { - iter->initiate_delay_ms = BSON_MAX (iter->initiate_delay_ms - HAPPY_EYEBALLS_DELAY_MS, 0); - } - } -} - void _mongoc_topology_scanner_set_server_api (mongoc_topology_scanner_t *ts, const mongoc_server_api_t *api) { diff --git a/src/libmongoc/tests/test-mongoc-async.c b/src/libmongoc/tests/test-mongoc-async.c index 39ee66562e..4de6f74913 100644 --- a/src/libmongoc/tests/test-mongoc-async.c +++ b/src/libmongoc/tests/test-mongoc-async.c @@ -6,6 +6,8 @@ #include +#include + #include #include #include @@ -47,9 +49,9 @@ static void test_hello_helper (mongoc_async_cmd_t *acmd, mongoc_async_cmd_result_t result, const bson_t *bson, - int64_t duration_usec) + mlib_duration duration_usec) { - struct result *r = (struct result *) acmd->data; + struct result *r = _acmd_userdata (struct result, acmd); bson_iter_t iter; bson_error_t *error = &acmd->error; @@ -139,8 +141,8 @@ test_hello_impl (bool with_ssl) sock_streams[i], false, NULL /* dns result, n/a. */, - NULL, /* initiator. */ - 0, /* initiate delay. */ + NULL, /* initiator. */ + mlib_duration (), /* No initiate delay. */ setup, setup_ctx, "admin", @@ -148,7 +150,7 @@ test_hello_impl (bool with_ssl) MONGOC_OP_CODE_QUERY, /* used by legacy hello */ &test_hello_helper, (void *) &results[i], - TIMEOUT); + mlib_duration (TIMEOUT, ms)); } future = future_async_run (async); @@ -310,19 +312,19 @@ static void test_hello_delay_callback (mongoc_async_cmd_t *acmd, mongoc_async_cmd_result_t result, const bson_t *bson, - int64_t duration_usec) + mlib_duration duration_usec) { BSON_UNUSED (result); BSON_UNUSED (bson); BSON_UNUSED (duration_usec); - ((stream_with_result_t *) acmd->data)->finished = true; + _acmd_userdata (stream_with_result_t, acmd)->finished = true; } static mongoc_stream_t * test_hello_delay_initializer (mongoc_async_cmd_t *acmd) { - return ((stream_with_result_t *) acmd->data)->stream; + return _acmd_userdata (stream_with_result_t, acmd)->stream; } static void @@ -346,15 +348,15 @@ test_hello_delay (void) false, /* is setup done. */ NULL, /* dns result. */ test_hello_delay_initializer, - 100, /* delay 100ms. */ - NULL, /* setup function. */ - NULL, /* setup ctx. */ + mlib_duration (100, ms), /* delay 100ms. */ + NULL, /* setup function. */ + NULL, /* setup ctx. */ "admin", &hello_cmd, MONGOC_OP_CODE_QUERY, /* used by legacy hello */ &test_hello_delay_callback, &stream_with_result, - TIMEOUT); + mlib_duration (TIMEOUT, ms)); mongoc_async_run (async); From f72cd5ae82d1247446f5171360ec8c920db7079b Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 22:10:44 -0600 Subject: [PATCH 28/66] Rename a lot of private command object attributes --- .../src/mongoc/mongoc-async-cmd-private.h | 40 ++++++------ src/libmongoc/src/mongoc/mongoc-async-cmd.c | 65 ++++++++++--------- src/libmongoc/src/mongoc/mongoc-async.c | 14 ++-- .../mongoc/mongoc-topology-scanner-private.h | 2 +- src/libmongoc/tests/test-happy-eyeballs.c | 4 +- src/libmongoc/tests/test-mongoc-async.c | 2 +- .../tests/test-mongoc-topology-scanner.c | 4 +- 7 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index e177c6d198..13e51bcf42 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -38,9 +38,9 @@ BSON_BEGIN_DECLS typedef enum { // The command has no stream and needs to connect to a peer - MONGOC_ASYNC_CMD_INITIATE, + MONGOC_ASYNC_CMD_PENDING_CONNECT, // The command has connected and has a stream, but needs to run stream setup - MONGOC_ASYNC_CMD_SETUP, + MONGOC_ASYNC_CMD_STREAM_SETUP, // The command has data to send to the peer MONGOC_ASYNC_CMD_SEND, // The command is ready to receive the response length header @@ -50,7 +50,7 @@ typedef enum { // The command is in an invalid error state MONGOC_ASYNC_CMD_ERROR_STATE, // The command has been cancelled. - MONGOC_ASYNC_CMD_CANCELED_STATE, + MONGOC_ASYNC_CMD_CANCELLED_STATE, } mongoc_async_cmd_state_t; /** @@ -76,10 +76,10 @@ typedef enum { * @param duration The elapsed duration that the command object has been running. * This will be zero when the CONNECTED state is invoked. */ -typedef void (*mongoc_async_cmd_cb_t) (struct _mongoc_async_cmd *acmd, - mongoc_async_cmd_result_t result, - const bson_t *bson, - mlib_duration duration); +typedef void (*mongoc_async_cmd_event_cb) (struct _mongoc_async_cmd *acmd, + mongoc_async_cmd_result_t result, + const bson_t *bson, + mlib_duration duration); /** * @brief Callback that is used to open a new stream for a command object. @@ -103,7 +103,7 @@ typedef mongoc_stream_t *(*mongoc_async_cmd_connect_cb) (struct _mongoc_async_cm * @return int The function should return -1 on failure, 1 if the stream * immediately has data to send, or 0 for generic success. */ -typedef int (*mongoc_async_cmd_setup_t) ( +typedef int (*mongoc_async_cmd_stream_setup_cb) ( mongoc_stream_t *stream, int *events, void *ctx, mlib_timer timeout, bson_error_t *error); @@ -130,27 +130,27 @@ typedef struct _mongoc_async_cmd { * @brief User-provided callback that will be used to lazily create the I/O stream * for the command. */ - mongoc_async_cmd_connect_cb initiator; + mongoc_async_cmd_connect_cb _stream_connect; /** * @brief User-provided callback function to do setup on the command's stream * after the stream has been created automatically. */ - mongoc_async_cmd_setup_t setup; + mongoc_async_cmd_stream_setup_cb _stream_setup; // Arbitrary userdata pointer passed to the stream setup function - void *setup_ctx; + void *_stream_seutp_userdata; /** * @brief User-provided command event callback. Invoked with the final result * and once when the command completes. */ - mongoc_async_cmd_cb_t cb; + mongoc_async_cmd_event_cb _event_callback; // Arbitrary userdata passed when the object was created - void *data; + void *_userdata; /** * @brief Timer to when the command should attempt to lazily initiate a new * connection with the _stream_connect callback. This does not apply if the * command was given a stream upon construction. */ - mlib_timer initiate_delay_timer; + mlib_timer _connect_delay_timer; /** * @brief The "start" reference point-in-time for the command object * @@ -166,7 +166,7 @@ typedef struct _mongoc_async_cmd { /** * @brief The BSON document of the command to be executed on the server. */ - bson_t cmd; + bson_t _command; mongoc_buffer_t buffer; mongoc_iovec_t *iovec; size_t niovec; @@ -178,7 +178,7 @@ typedef struct _mongoc_async_cmd { * * Initialized with BSON_INITIALIZER, so safe to pass/destroy upon construction. */ - bson_t reply; + bson_t _response_data; char *ns; /** * @brief The DNS address info that was associated with the command when @@ -219,12 +219,12 @@ mongoc_async_cmd_new (mongoc_async_t *async, struct addrinfo *dns_result, mongoc_async_cmd_connect_cb connect_callback, mlib_duration connect_delay, - mongoc_async_cmd_setup_t setup, + mongoc_async_cmd_stream_setup_cb setup, void *setup_ctx, const char *dbname, const bson_t *cmd, const int32_t cmd_opcode, - mongoc_async_cmd_cb_t cb, + mongoc_async_cmd_event_cb cb, void *userdata, mlib_duration timeout); @@ -275,7 +275,7 @@ _acmd_cancel (mongoc_async_cmd_t *self) static inline void _acmd_adjust_connect_delay (mongoc_async_cmd_t *self, const mlib_duration d) { - mlib_time_adjust (&self->initiate_delay_timer.expires_at, d); + mlib_time_adjust (&self->_connect_delay_timer.expires_at, d); } /** @@ -315,7 +315,7 @@ _acmd_elapsed (mongoc_async_cmd_t const *self) * @param T The type to read from the pointer * @param Command Pointer to a command object */ -#define _acmd_userdata(T, Command) ((T *) ((Command)->data)) +#define _acmd_userdata(T, Command) ((T *) ((Command)->_userdata)) void mongoc_async_cmd_destroy (mongoc_async_cmd_t *acmd); diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index 1ab036571a..f3f11927b5 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -42,7 +42,7 @@ typedef mongoc_async_cmd_result_t (*_mongoc_async_cmd_phase_t) (mongoc_async_cmd_t *cmd); static mongoc_async_cmd_result_t -_mongoc_async_cmd_phase_initiate (mongoc_async_cmd_t *cmd); +_mongoc_async_cmd_phase_connect (mongoc_async_cmd_t *cmd); static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_stream_setup (mongoc_async_cmd_t *cmd); static mongoc_async_cmd_result_t @@ -53,7 +53,7 @@ static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_recv_rpc (mongoc_async_cmd_t *cmd); static const _mongoc_async_cmd_phase_t gMongocCMDPhases[] = { - _mongoc_async_cmd_phase_initiate, + _mongoc_async_cmd_phase_connect, _mongoc_async_cmd_phase_stream_setup, _mongoc_async_cmd_phase_send, _mongoc_async_cmd_phase_recv_len, @@ -100,7 +100,7 @@ mongoc_async_cmd_run (mongoc_async_cmd_t *acmd) /* if we have successfully connected to the node, call the callback. */ if (acmd->state == MONGOC_ASYNC_CMD_SEND) { - acmd->cb (acmd, MONGOC_ASYNC_CMD_CONNECTED, NULL, _acmd_elapsed (acmd)); + acmd->_event_callback (acmd, MONGOC_ASYNC_CMD_CONNECTED, NULL, _acmd_elapsed (acmd)); } _mongoc_async_cmd_phase_t const phase_callback = gMongocCMDPhases[acmd->state]; @@ -119,7 +119,7 @@ mongoc_async_cmd_run (mongoc_async_cmd_t *acmd) case MONGOC_ASYNC_CMD_SUCCESS: case MONGOC_ASYNC_CMD_ERROR: case MONGOC_ASYNC_CMD_TIMEOUT: - acmd->cb (acmd, result, &acmd->reply, _acmd_elapsed (acmd)); + acmd->_event_callback (acmd, result, &acmd->_response_data, _acmd_elapsed (acmd)); // No more work on this command. Destroy the object and tell the caller // it's been removed mongoc_async_cmd_destroy (acmd); @@ -148,12 +148,12 @@ _mongoc_async_cmd_init_send (const int32_t cmd_opcode, mongoc_async_cmd_t *acmd, message_length += mcd_rpc_op_query_set_full_collection_name (acmd->rpc, acmd->ns); message_length += mcd_rpc_op_query_set_number_to_skip (acmd->rpc, 0); message_length += mcd_rpc_op_query_set_number_to_return (acmd->rpc, -1); - message_length += mcd_rpc_op_query_set_query (acmd->rpc, bson_get_data (&acmd->cmd)); + message_length += mcd_rpc_op_query_set_query (acmd->rpc, bson_get_data (&acmd->_command)); } else { mcd_rpc_op_msg_set_sections_count (acmd->rpc, 1u); message_length += mcd_rpc_op_msg_set_flag_bits (acmd->rpc, MONGOC_OP_MSG_FLAG_NONE); message_length += mcd_rpc_op_msg_section_set_kind (acmd->rpc, 0u, 0); - message_length += mcd_rpc_op_msg_section_set_body (acmd->rpc, 0u, bson_get_data (&acmd->cmd)); + message_length += mcd_rpc_op_msg_section_set_body (acmd->rpc, 0u, bson_get_data (&acmd->_command)); } mcd_rpc_message_set_length (acmd->rpc, message_length); @@ -170,10 +170,10 @@ _mongoc_async_cmd_state_start (mongoc_async_cmd_t *acmd, bool is_setup_done) { if (!acmd->stream) { // No stream yet associated, so we need to initiate a new connection - acmd->state = MONGOC_ASYNC_CMD_INITIATE; - } else if (acmd->setup && !is_setup_done) { + acmd->state = MONGOC_ASYNC_CMD_PENDING_CONNECT; + } else if (acmd->_stream_setup && !is_setup_done) { // We have a stream, and a setup callback, so call that setup callback next - acmd->state = MONGOC_ASYNC_CMD_SETUP; + acmd->state = MONGOC_ASYNC_CMD_STREAM_SETUP; } else { // We have a stream, and no setup required. We're ready to send immediately. acmd->state = MONGOC_ASYNC_CMD_SEND; @@ -187,14 +187,14 @@ mongoc_async_cmd_new (mongoc_async_t *async, mongoc_stream_t *stream, bool is_setup_done, struct addrinfo *dns_result, - mongoc_async_cmd_connect_cb initiator, - mlib_duration initiate_delay, - mongoc_async_cmd_setup_t setup, - void *setup_ctx, + mongoc_async_cmd_connect_cb connect_cb, + mlib_duration connect_delay, + mongoc_async_cmd_stream_setup_cb stream_setup, + void *setup_userdata, const char *dbname, const bson_t *cmd, const int32_t cmd_opcode, /* OP_QUERY or OP_MSG */ - mongoc_async_cmd_cb_t cb, + mongoc_async_cmd_event_cb event_cb, void *userdata, mlib_duration timeout) { @@ -202,24 +202,24 @@ mongoc_async_cmd_new (mongoc_async_t *async, BSON_ASSERT_PARAM (dbname); mongoc_async_cmd_t *const acmd = BSON_ALIGNED_ALLOC0 (mongoc_async_cmd_t); - acmd->initiate_delay_timer = mlib_expires_after (initiate_delay); + acmd->_connect_delay_timer = mlib_expires_after (connect_delay); acmd->async = async; acmd->dns_result = dns_result; acmd->_timeout = timeout; acmd->stream = stream; - acmd->initiator = initiator; - acmd->setup = setup; - acmd->setup_ctx = setup_ctx; - acmd->cb = cb; - acmd->data = userdata; + acmd->_stream_connect = connect_cb; + acmd->_stream_setup = stream_setup; + acmd->_stream_seutp_userdata = setup_userdata; + acmd->_event_callback = event_cb; + acmd->_userdata = userdata; acmd->_start_time = mlib_now (); - acmd->state = MONGOC_ASYNC_CMD_INITIATE; - acmd->reply = (bson_t) BSON_INITIALIZER; - bson_copy_to (cmd, &acmd->cmd); + acmd->state = MONGOC_ASYNC_CMD_PENDING_CONNECT; + acmd->_response_data = (bson_t) BSON_INITIALIZER; + bson_copy_to (cmd, &acmd->_command); if (MONGOC_OP_CODE_MSG == cmd_opcode) { /* If we're sending an OP_MSG, we need to add the "db" field: */ - bson_append_utf8 (&acmd->cmd, "$db", 3, "admin", 5); + bson_append_utf8 (&acmd->_command, "$db", 3, "admin", 5); } acmd->rpc = mcd_rpc_message_new (); @@ -245,8 +245,8 @@ mongoc_async_cmd_destroy (mongoc_async_cmd_t *acmd) DL_DELETE (acmd->async->cmds, acmd); acmd->async->ncmds--; - bson_destroy (&acmd->cmd); - bson_destroy (&acmd->reply); + bson_destroy (&acmd->_command); + bson_destroy (&acmd->_response_data); bson_free (acmd->iovec); _mongoc_buffer_destroy (&acmd->buffer); @@ -257,17 +257,17 @@ mongoc_async_cmd_destroy (mongoc_async_cmd_t *acmd) } mongoc_async_cmd_result_t -_mongoc_async_cmd_phase_initiate (mongoc_async_cmd_t *acmd) +_mongoc_async_cmd_phase_connect (mongoc_async_cmd_t *acmd) { - acmd->stream = acmd->initiator (acmd); + acmd->stream = acmd->_stream_connect (acmd); if (!acmd->stream) { return MONGOC_ASYNC_CMD_ERROR; } _acmd_reset_elapsed (acmd); - if (acmd->setup) { + if (acmd->_stream_setup) { // There is a setup callback that we need to call - acmd->state = MONGOC_ASYNC_CMD_SETUP; + acmd->state = MONGOC_ASYNC_CMD_STREAM_SETUP; } else { // There is no setup callback, so we can send data immediately acmd->state = MONGOC_ASYNC_CMD_SEND; @@ -278,7 +278,8 @@ _mongoc_async_cmd_phase_initiate (mongoc_async_cmd_t *acmd) static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_stream_setup (mongoc_async_cmd_t *acmd) { - int const retval = acmd->setup (acmd->stream, &acmd->events, acmd->setup_ctx, _acmd_deadline (acmd), &acmd->error); + int const retval = acmd->_stream_setup ( + acmd->stream, &acmd->events, acmd->_stream_seutp_userdata, _acmd_deadline (acmd), &acmd->error); switch (retval) { case -1: return MONGOC_ASYNC_CMD_ERROR; @@ -454,7 +455,7 @@ _mongoc_async_cmd_phase_recv_rpc (mongoc_async_cmd_t *acmd) _mongoc_buffer_init (&acmd->buffer, decompressed_data, decompressed_data_len, NULL, NULL); } - if (!mcd_rpc_message_get_body (acmd->rpc, &acmd->reply)) { + if (!mcd_rpc_message_get_body (acmd->rpc, &acmd->_response_data)) { _mongoc_set_error ( &acmd->error, MONGOC_ERROR_PROTOCOL, MONGOC_ERROR_PROTOCOL_INVALID_REPLY, "Invalid reply from server"); return MONGOC_ASYNC_CMD_ERROR; diff --git a/src/libmongoc/src/mongoc/mongoc-async.c b/src/libmongoc/src/mongoc/mongoc-async.c index 238202696f..b552a7b8a4 100644 --- a/src/libmongoc/src/mongoc/mongoc-async.c +++ b/src/libmongoc/src/mongoc/mongoc-async.c @@ -85,12 +85,12 @@ mongoc_async_run (mongoc_async_t *async) /* check if any cmds are ready to be initiated. */ DL_FOREACH_SAFE (async->cmds, acmd, tmp) { - if (acmd->state == MONGOC_ASYNC_CMD_INITIATE) { + if (acmd->state == MONGOC_ASYNC_CMD_PENDING_CONNECT) { // Command is waiting to be initiated. // Timer for when the command should be initiated: // Should not yet have an associated stream BSON_ASSERT (!acmd->stream); - if (mlib_timer_is_expired (acmd->initiate_delay_timer)) { + if (mlib_timer_is_expired (acmd->_connect_delay_timer)) { /* time to initiate. */ if (mongoc_async_cmd_run (acmd)) { // We should now have an associated stream @@ -101,7 +101,7 @@ mongoc_async_run (mongoc_async_t *async) } } else { // Wake up poll() when the initiation timeout is hit - poll_timer = mlib_soonest_timer (poll_timer, acmd->initiate_delay_timer); + poll_timer = mlib_soonest_timer (poll_timer, acmd->_connect_delay_timer); } } @@ -164,18 +164,18 @@ mongoc_async_run (mongoc_async_t *async) DL_FOREACH_SAFE (async->cmds, acmd, tmp) { /* check if an initiated cmd has passed the connection timeout. */ - if (acmd->state != MONGOC_ASYNC_CMD_INITIATE && _acmd_has_timed_out (acmd)) { + if (acmd->state != MONGOC_ASYNC_CMD_PENDING_CONNECT && _acmd_has_timed_out (acmd)) { _mongoc_set_error (&acmd->error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, acmd->state == MONGOC_ASYNC_CMD_SEND ? "connection timeout" : "socket timeout"); - acmd->cb (acmd, MONGOC_ASYNC_CMD_TIMEOUT, NULL, _acmd_elapsed (acmd)); + acmd->_event_callback (acmd, MONGOC_ASYNC_CMD_TIMEOUT, NULL, _acmd_elapsed (acmd)); /* Remove acmd from the async->cmds doubly-linked list */ mongoc_async_cmd_destroy (acmd); - } else if (acmd->state == MONGOC_ASYNC_CMD_CANCELED_STATE) { - acmd->cb (acmd, MONGOC_ASYNC_CMD_ERROR, NULL, _acmd_elapsed (acmd)); + } else if (acmd->state == MONGOC_ASYNC_CMD_CANCELLED_STATE) { + acmd->_event_callback (acmd, MONGOC_ASYNC_CMD_ERROR, NULL, _acmd_elapsed (acmd)); /* Remove acmd from the async->cmds doubly-linked list */ mongoc_async_cmd_destroy (acmd); diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h b/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h index f8854cb23d..55c7f6a63c 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner-private.h @@ -124,7 +124,7 @@ typedef struct mongoc_topology_scanner { mongoc_topology_scanner_cb_t cb; void *cb_data; const mongoc_uri_t *uri; - mongoc_async_cmd_setup_t setup; + mongoc_async_cmd_stream_setup_cb setup; mongoc_stream_initiator_t initiator; void *initiator_context; bson_error_t error; diff --git a/src/libmongoc/tests/test-happy-eyeballs.c b/src/libmongoc/tests/test-happy-eyeballs.c index 50fdfd306a..424fd8a88a 100644 --- a/src/libmongoc/tests/test-happy-eyeballs.c +++ b/src/libmongoc/tests/test-happy-eyeballs.c @@ -152,7 +152,7 @@ _mock_poll (mongoc_stream_poll_t *streams, size_t nstreams, int32_t timeout) } static mongoc_stream_t * -_mock_initiator (mongoc_async_cmd_t *acmd) +_mock_connect (mongoc_async_cmd_t *acmd) { mongoc_stream_t *stream = _mongoc_topology_scanner_tcp_initiate (acmd); /* override poll */ @@ -312,7 +312,7 @@ _testcase_run (he_testcase_t *testcase) DL_FOREACH (ts->async->cmds, iter) { - iter->initiator = _mock_initiator; + iter->_stream_connect = _mock_connect; } mongoc_topology_scanner_work (ts); diff --git a/src/libmongoc/tests/test-mongoc-async.c b/src/libmongoc/tests/test-mongoc-async.c index 4de6f74913..86f7815c65 100644 --- a/src/libmongoc/tests/test-mongoc-async.c +++ b/src/libmongoc/tests/test-mongoc-async.c @@ -80,7 +80,7 @@ test_hello_impl (bool with_ssl) mock_server_t *servers[NSERVERS]; mongoc_async_t *async; mongoc_stream_t *sock_streams[NSERVERS]; - mongoc_async_cmd_setup_t setup = NULL; + mongoc_async_cmd_stream_setup_cb setup = NULL; void *setup_ctx = NULL; uint16_t ports[NSERVERS]; struct result results[NSERVERS]; diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index 94d280c5b9..cf56dcdb4b 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -568,7 +568,7 @@ _retired_fails_to_initiate_cb ( } static mongoc_stream_t * -null_initiator (mongoc_async_cmd_t *acmd) +null_connect (mongoc_async_cmd_t *acmd) { BSON_UNUSED (acmd); @@ -609,7 +609,7 @@ test_topology_retired_fails_to_initiate (void) * a failed mongoc_socket_new or mongoc_stream_connect. */ DL_FOREACH (scanner->async->cmds, acmd) { - scanner->async->cmds->initiator = null_initiator; + scanner->async->cmds->_stream_connect = null_connect; } mongoc_topology_scanner_work (scanner); From bde84066c3683683c204a8d33ad679b9bad8ab9e Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 22:10:58 -0600 Subject: [PATCH 29/66] Rename accidental duplicate test case --- src/libmongoc/tests/test-mongoc-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/tests/test-mongoc-client.c b/src/libmongoc/tests/test-mongoc-client.c index 746f32e387..f984f6ad56 100644 --- a/src/libmongoc/tests/test-mongoc-client.c +++ b/src/libmongoc/tests/test-mongoc-client.c @@ -3849,7 +3849,7 @@ void test_client_install (TestSuite *suite) { TestSuite_AddLive (suite, "/Client/ipv6/single", test_mongoc_client_ipv6_single); - TestSuite_AddLive (suite, "/Client/ipv6/single", test_mongoc_client_ipv6_pooled); + TestSuite_AddLive (suite, "/Client/ipv6/pooled", test_mongoc_client_ipv6_pooled); TestSuite_AddFull ( suite, "/Client/authenticate", test_mongoc_client_authenticate, NULL, NULL, test_framework_skip_if_no_auth); From a23c8eb8b92874b21e5e38a3f1e7ade016f2743d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 22:21:22 -0600 Subject: [PATCH 30/66] [fixup] Missing updates in conditional sections --- src/libmongoc/tests/test-mongoc-async.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-async.c b/src/libmongoc/tests/test-mongoc-async.c index 86f7815c65..e6a3ce9aeb 100644 --- a/src/libmongoc/tests/test-mongoc-async.c +++ b/src/libmongoc/tests/test-mongoc-async.c @@ -281,8 +281,8 @@ test_large_hello (void *ctx) sock_stream, false, /* is setup done. */ NULL /* dns result, n/a. */, - NULL, /* initiator. */ - 0, /* initiate delay. */ + NULL, /* initiator. */ + mlib_duration (), /* initiate delay. */ #ifdef MONGOC_ENABLE_SSL test_framework_get_ssl () ? mongoc_async_cmd_tls_setup : NULL, #else From 5c76085d78f78d97c664acfe7b72ab3096566839 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 22:22:48 -0600 Subject: [PATCH 31/66] [fixup] Time member rename in Windows --- src/common/src/mlib/time_point.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 59506f326d..b6c68f6e8c 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -137,7 +137,7 @@ mlib_now (void) mlib_noexcept // Number of microseconds beyond the last whole second: const int64_t subsecond_us = ((ticks % ticks_per_second) * one_million) / ticks_per_second; mlib_time_point ret; - ret._time_since_monotonic_start = mlib_duration ((whole_seconds_1m, us), plus, (subsecond_us, us)); + ret.time_since_monotonic_start = mlib_duration ((whole_seconds_1m, us), plus, (subsecond_us, us)); return ret; #else #error We do not know how to get the current time on this platform From b7628ece0fece725a2b7e2d4f9729337b658de06 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 22:26:42 -0600 Subject: [PATCH 32/66] [fixup] More conditional compilation misses --- src/libmongoc/tests/test-mongoc-async.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-async.c b/src/libmongoc/tests/test-mongoc-async.c index e6a3ce9aeb..4f6c774365 100644 --- a/src/libmongoc/tests/test-mongoc-async.c +++ b/src/libmongoc/tests/test-mongoc-async.c @@ -215,9 +215,9 @@ static void test_large_hello_helper (mongoc_async_cmd_t *acmd, mongoc_async_cmd_result_t result, const bson_t *bson, - int64_t duration_usec) + mlib_duration deadline) { - BSON_UNUSED (duration_usec); + BSON_UNUSED (deadline); bson_iter_t iter; bson_error_t *error = &acmd->error; @@ -294,7 +294,7 @@ test_large_hello (void *ctx) MONGOC_OP_CODE_QUERY, /* used by legacy hello */ &test_large_hello_helper, NULL, - TIMEOUT); + mlib_duration (TIMEOUT, ms)); mongoc_async_run (async); mongoc_async_destroy (async); From 55ed326d233520bb0e7c4bb70d5e4c26b69624ac Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Tue, 22 Jul 2025 22:54:00 -0600 Subject: [PATCH 33/66] Fix dead-write warning and simplify some macro usage --- src/libmongoc/src/mongoc/mongoc-async-cmd.c | 14 +++++++------- src/libmongoc/src/mongoc/mongoc-config.h.in | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index f3f11927b5..3bc64a8764 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -70,17 +70,17 @@ mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, mli const char *host = (const char *) ctx; int retry_events = 0; - for (tls_stream = stream; tls_stream->type != MONGOC_STREAM_TLS; tls_stream = mongoc_stream_get_base_stream (tls_stream)) { } - mlib_duration_rep_t remain_ms = mlib_milliseconds_count (mlib_timer_remaining (deadline)); - -#if defined(MONGOC_ENABLE_SSL_OPENSSL) || defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) - /* pass 0 for the timeout to begin / continue non-blocking handshake */ - remain_ms = 0; -#endif + // Try to do a non-blocking operation, if our backend allows it + const mlib_duration_rep_t remain_ms = // + (MONGOC_SECURE_CHANNEL_ENABLED () || MONGOC_OPENSSL_ENABLED ()) + // Pass 0 for the timeout to begin / continue a non-blocking handshake + ? 0 + // Otherwise, use the deadline + : mlib_milliseconds_count (mlib_timer_remaining (deadline)); if (mongoc_stream_tls_handshake (tls_stream, host, remain_ms, &retry_events, error)) { return 1; } diff --git a/src/libmongoc/src/mongoc/mongoc-config.h.in b/src/libmongoc/src/mongoc/mongoc-config.h.in index de8d2a5975..fffb392f6f 100644 --- a/src/libmongoc/src/mongoc/mongoc-config.h.in +++ b/src/libmongoc/src/mongoc/mongoc-config.h.in @@ -48,8 +48,8 @@ * compiled with Native SSL support on Windows */ #define MONGOC_ENABLE_SSL_SECURE_CHANNEL @MONGOC_ENABLE_SSL_SECURE_CHANNEL@ - -#if MONGOC_ENABLE_SSL_SECURE_CHANNEL != 1 +#define MONGOC_SECURE_CHANNEL_ENABLED() @MONGOC_ENABLE_SSL_SECURE_CHANNEL@ +#if MONGOC_SECURE_CHANNEL_ENABLED() != 1 # undef MONGOC_ENABLE_SSL_SECURE_CHANNEL #endif @@ -65,8 +65,8 @@ #endif /* - * MONGOC_HAVE_BCRYPT_PBKDF2 is set from configure to determine if - * our Bcrypt Windows library supports PBKDF2 + * MONGOC_HAVE_BCRYPT_PBKDF2 is set from configure to determine if + * our Bcrypt Windows library supports PBKDF2 */ #define MONGOC_HAVE_BCRYPT_PBKDF2 @MONGOC_HAVE_BCRYPT_PBKDF2@ @@ -101,8 +101,8 @@ * compiled with OpenSSL support. */ #define MONGOC_ENABLE_SSL_OPENSSL @MONGOC_ENABLE_SSL_OPENSSL@ - -#if MONGOC_ENABLE_SSL_OPENSSL != 1 +#define MONGOC_OPENSSL_ENABLED() @MONGOC_ENABLE_SSL_OPENSSL@ +#if MONGOC_OPENSSL_ENABLED() != 1 # undef MONGOC_ENABLE_SSL_OPENSSL #endif From c40bffc7c4e925597f4955c52fee570a0b140a83 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 23 Jul 2025 10:58:51 -0600 Subject: [PATCH 34/66] Fix: Wrong state when setting cancellation --- src/libmongoc/src/mongoc/mongoc-async-cmd-private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index 13e51bcf42..61c43192b6 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -260,7 +260,7 @@ static inline void _acmd_cancel (mongoc_async_cmd_t *self) { // XXX: Should this check if ther command has already finished/failed? - self->state = MONGOC_ASYNC_CMD_ERROR_STATE; + self->state = MONGOC_ASYNC_CMD_CANCELLED_STATE; } /** From c623b52091a7771a1037d761e860bb89b74c9a2b Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 24 Jul 2025 15:47:15 -0600 Subject: [PATCH 35/66] Fix: sleep_for not using the checked useconds value --- src/common/src/mlib/time_point.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index b6c68f6e8c..0b083a63e1 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -263,13 +263,16 @@ mlib_sleep_for (const mlib_duration d) mlib_noexcept return 0; } #if mlib_have_posix_clocks() - // Convert the microseconds count to the value for the usleep function + // Convert the microseconds count to the value for the usleep function. We don't + // know the precise integer type that `usleep` expects, so do a checked-narrow + // to handle too-large values. useconds_t i = 0; - if (mlib_narrow (&i, mlib_microseconds_count (d))) { - // Too many microseconds. Sleep for the max. + if (mlib_narrow (&i, duration_usec)) { + // Too many microseconds. Sleep for the max. This will only be reached + // for positive durations because of the above check against `<= 0` i = mlib_maxof (useconds_t); } - int rc = usleep (mlib_microseconds_count (d)); + int rc = usleep (i); if (rc != 0) { return errno; } From 3e7bb3117e70688c87564acb2ce02026c664fc25 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 24 Jul 2025 16:15:23 -0600 Subject: [PATCH 36/66] Test case cleanup --- src/common/tests/test-mlib.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 2e101f7d3e..4ebe544beb 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -941,7 +941,6 @@ _test_duration (void) d = mlib_duration ((4, sec), min, (400, ms)); mlib_check (mlib_duration_cmp (d, ==, (400, ms))); - // Clamp to at most five minutes d = mlib_duration ( // At least 5 seconds: (d, max, (5, sec)), @@ -975,8 +974,6 @@ _test_duration (void) d = mlib_duration (-1729, us); mlib_check (mlib_milliseconds_count (d), eq, -1); - // d = mlib_duration (1, sec, plus, 729, ms); - // mlib_check (mlib_microseconds_count (d), eq, 1729000); d = mlib_duration ((-3, sec), plus, mlib_duration_min ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); d = mlib_duration ((4, sec), plus, mlib_duration_max ()); @@ -1061,11 +1058,11 @@ _test_sleep (void) // Sleeping for a negative duration returns immediately with success start = mlib_now (); mlib_check (mlib_sleep_for (-10, sec), eq, 0); - mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); + mlib_check (mlib_duration_cmp (mlib_elapsed_since (start), <, (100, ms))); // Sleeping until a point in the past returns immediately as well mlib_check (mlib_sleep_until (start), eq, 0); - mlib_check (mlib_milliseconds_count (mlib_time_difference (start, mlib_now ())) < 100); + mlib_check (mlib_duration_cmp (mlib_elapsed_since (start), <, (100, ms))); } static void @@ -1093,7 +1090,7 @@ _test_timer (void) mlib_check (cond); // Create a timer that expired in the past - tm = mlib_expires_at (mlib_later (mlib_now (), mlib_duration (-10, sec))); + tm = mlib_expires_at (mlib_later (mlib_now (), (-10, sec))); mlib_check (mlib_timer_is_expired (tm)); } From 161c4038584143a7c075bf20a3f22bc0bb6e400f Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 24 Jul 2025 16:33:27 -0600 Subject: [PATCH 37/66] Comment cleanup and spelling in async-cmd --- .../src/mongoc/mongoc-async-cmd-private.h | 14 ++++++++++---- src/libmongoc/src/mongoc/mongoc-async-cmd.c | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index 61c43192b6..f70a808170 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -137,10 +137,10 @@ typedef struct _mongoc_async_cmd { */ mongoc_async_cmd_stream_setup_cb _stream_setup; // Arbitrary userdata pointer passed to the stream setup function - void *_stream_seutp_userdata; + void *_stream_setup_userdata; /** * @brief User-provided command event callback. Invoked with the final result - * and once when the command completes. + * and once when the command connects. */ mongoc_async_cmd_event_cb _event_callback; // Arbitrary userdata passed when the object was created @@ -156,12 +156,18 @@ typedef struct _mongoc_async_cmd { * * This is used to determine how long the command has been in progress, * including for when to consider the command to have timed-out. + * + * NOTE: This value can change! See: `_acmd_reset_elapsed` */ mlib_time_point _start_time; - // The timeout duration allotted to this command object + /** + * @brief The timeout duration allotted to the command object. + * + * We need to store it as a duration rather than a timer, because we need to + * reset the timeout at certain points (see: `_acmd_reset_elapsed`) + */ mlib_duration _timeout; - bson_error_t error; /** * @brief The BSON document of the command to be executed on the server. diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index 3bc64a8764..9617e09ef7 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -209,7 +209,7 @@ mongoc_async_cmd_new (mongoc_async_t *async, acmd->stream = stream; acmd->_stream_connect = connect_cb; acmd->_stream_setup = stream_setup; - acmd->_stream_seutp_userdata = setup_userdata; + acmd->_stream_setup_userdata = setup_userdata; acmd->_event_callback = event_cb; acmd->_userdata = userdata; acmd->_start_time = mlib_now (); @@ -279,7 +279,7 @@ static mongoc_async_cmd_result_t _mongoc_async_cmd_phase_stream_setup (mongoc_async_cmd_t *acmd) { int const retval = acmd->_stream_setup ( - acmd->stream, &acmd->events, acmd->_stream_seutp_userdata, _acmd_deadline (acmd), &acmd->error); + acmd->stream, &acmd->events, acmd->_stream_setup_userdata, _acmd_deadline (acmd), &acmd->error); switch (retval) { case -1: return MONGOC_ASYNC_CMD_ERROR; From 52e9a14e099b768b0fecdef8f201c4a63916d48d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 24 Jul 2025 17:21:23 -0600 Subject: [PATCH 38/66] Remove unneeded inclusions following a goofy merge --- .../src/mongoc/mongoc-client-private.h | 2 -- src/libmongoc/src/mongoc/mongoc-client.c | 1 - src/libmongoc/src/mongoc/mongoc-crypt.c | 2 -- src/libmongoc/tests/json-test-operations.c | 6 ----- src/libmongoc/tests/test-happy-eyeballs.c | 4 ---- .../tests/test-mongoc-background-monitoring.c | 3 --- src/libmongoc/tests/test-mongoc-client-pool.c | 4 ---- .../tests/test-mongoc-client-session.c | 8 ------- .../test-mongoc-client-side-encryption.c | 4 ---- src/libmongoc/tests/test-mongoc-cluster.c | 8 ------- src/libmongoc/tests/test-mongoc-counters.c | 7 ------ src/libmongoc/tests/test-mongoc-interrupt.c | 6 ----- .../tests/test-mongoc-max-staleness.c | 9 -------- .../tests/test-mongoc-sample-commands.c | 23 +++++++++---------- src/libmongoc/tests/test-mongoc-sdam.c | 4 ---- src/libmongoc/tests/test-mongoc-socket.c | 3 --- .../tests/test-mongoc-topology-reconcile.c | 8 ------- .../tests/test-mongoc-topology-scanner.c | 9 -------- src/libmongoc/tests/test-mongoc-topology.c | 7 ------ .../tests/test-mongoc-with-transaction.c | 5 ---- 20 files changed, 11 insertions(+), 112 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-client-private.h b/src/libmongoc/src/mongoc/mongoc-client-private.h index 06a0cc7fbf..783211a545 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-private.h +++ b/src/libmongoc/src/mongoc/mongoc-client-private.h @@ -16,8 +16,6 @@ #include -#include - #ifndef MONGOC_CLIENT_PRIVATE_H #define MONGOC_CLIENT_PRIVATE_H diff --git a/src/libmongoc/src/mongoc/mongoc-client.c b/src/libmongoc/src/mongoc/mongoc-client.c index 12cc7d15e6..ee81a2dc2c 100644 --- a/src/libmongoc/src/mongoc/mongoc-client.c +++ b/src/libmongoc/src/mongoc/mongoc-client.c @@ -18,7 +18,6 @@ #include -#include #ifdef MONGOC_HAVE_DNSAPI /* for DnsQuery_UTF8 */ #include diff --git a/src/libmongoc/src/mongoc/mongoc-crypt.c b/src/libmongoc/src/mongoc/mongoc-crypt.c index 81bb3ae494..fb4fa192e7 100644 --- a/src/libmongoc/src/mongoc/mongoc-crypt.c +++ b/src/libmongoc/src/mongoc/mongoc-crypt.c @@ -35,9 +35,7 @@ #include #include -#include #include -#include #include #include diff --git a/src/libmongoc/tests/json-test-operations.c b/src/libmongoc/tests/json-test-operations.c index 386d6f3f80..2470b55a0e 100644 --- a/src/libmongoc/tests/json-test-operations.c +++ b/src/libmongoc/tests/json-test-operations.c @@ -17,12 +17,6 @@ #include -#include "./TestSuite.h" -#include "./json-test-operations.h" -#include "./json-test.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include #include diff --git a/src/libmongoc/tests/test-happy-eyeballs.c b/src/libmongoc/tests/test-happy-eyeballs.c index 424fd8a88a..b2d6022042 100644 --- a/src/libmongoc/tests/test-happy-eyeballs.c +++ b/src/libmongoc/tests/test-happy-eyeballs.c @@ -1,7 +1,3 @@ -#include "./TestSuite.h" -#include "./mock_server/mock-server.h" -#include "./test-libmongoc.h" - #include #include #include diff --git a/src/libmongoc/tests/test-mongoc-background-monitoring.c b/src/libmongoc/tests/test-mongoc-background-monitoring.c index 170ad526e4..6bfedbff0c 100644 --- a/src/libmongoc/tests/test-mongoc-background-monitoring.c +++ b/src/libmongoc/tests/test-mongoc-background-monitoring.c @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "./mock_server/mock-server.h" - #include #include #include @@ -27,7 +25,6 @@ #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-client-pool.c b/src/libmongoc/tests/test-mongoc-client-pool.c index 6c040f3682..d713d1973a 100644 --- a/src/libmongoc/tests/test-mongoc-client-pool.c +++ b/src/libmongoc/tests/test-mongoc-client-pool.c @@ -1,6 +1,3 @@ -#include "./TestSuite.h" -#include "./test-libmongoc.h" - #include // BEGIN_IGNORE_DEPRECATIONS #include #include @@ -8,7 +5,6 @@ #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-client-session.c b/src/libmongoc/tests/test-mongoc-client-session.c index adb8dd76bc..87d6ca22d7 100644 --- a/src/libmongoc/tests/test-mongoc-client-session.c +++ b/src/libmongoc/tests/test-mongoc-client-session.c @@ -1,10 +1,3 @@ -#include "./TestSuite.h" -#include "./json-test.h" -#include "./mock_server/future-functions.h" -#include "./mock_server/mock-server.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include // BEGIN_IGNORE_DEPRECATIONS #include #include @@ -14,7 +7,6 @@ #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-client-side-encryption.c b/src/libmongoc/tests/test-mongoc-client-side-encryption.c index 6d092ff182..54443ddb1d 100644 --- a/src/libmongoc/tests/test-mongoc-client-side-encryption.c +++ b/src/libmongoc/tests/test-mongoc-client-side-encryption.c @@ -14,15 +14,11 @@ * limitations under the License. */ -#include "./json-test.h" -#include "./test-libmongoc.h" - #include #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-cluster.c b/src/libmongoc/tests/test-mongoc-cluster.c index 42b4ec3416..81c6f4d0d0 100644 --- a/src/libmongoc/tests/test-mongoc-cluster.c +++ b/src/libmongoc/tests/test-mongoc-cluster.c @@ -1,10 +1,3 @@ -#include "./TestSuite.h" -#include "./mock_server/future-functions.h" -#include "./mock_server/future.h" -#include "./mock_server/mock-server.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include #include @@ -14,7 +7,6 @@ #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-counters.c b/src/libmongoc/tests/test-mongoc-counters.c index 1c4c8629ce..fdff5e1bdd 100644 --- a/src/libmongoc/tests/test-mongoc-counters.c +++ b/src/libmongoc/tests/test-mongoc-counters.c @@ -14,18 +14,11 @@ * limitations under the License. */ -#include "./TestSuite.h" -#include "./mock_server/future-functions.h" -#include "./mock_server/mock-server.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-interrupt.c b/src/libmongoc/tests/test-mongoc-interrupt.c index 243767a87a..a06b66f7ff 100644 --- a/src/libmongoc/tests/test-mongoc-interrupt.c +++ b/src/libmongoc/tests/test-mongoc-interrupt.c @@ -14,18 +14,12 @@ * limitations under the License. */ -#include "./TestSuite.h" -#include "./mock_server/future.h" -#include "./mock_server/mock-server.h" -#include "./test-libmongoc.h" - #include #include #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-max-staleness.c b/src/libmongoc/tests/test-mongoc-max-staleness.c index 4799da9637..1a2f6877e1 100644 --- a/src/libmongoc/tests/test-mongoc-max-staleness.c +++ b/src/libmongoc/tests/test-mongoc-max-staleness.c @@ -1,17 +1,8 @@ -#include "./TestSuite.h" -#include "./json-test.h" -#include "./mock_server/future-functions.h" -#include "./mock_server/mock-server.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include #include -#include - #include #include #include diff --git a/src/libmongoc/tests/test-mongoc-sample-commands.c b/src/libmongoc/tests/test-mongoc-sample-commands.c index 45150a8c50..b56063c114 100644 --- a/src/libmongoc/tests/test-mongoc-sample-commands.c +++ b/src/libmongoc/tests/test-mongoc-sample-commands.c @@ -23,22 +23,21 @@ * These are the C examples for that page. */ -/* clang-format off */ -#include -#include -#include -#include +#include "./TestSuite.h" +#include "./test-conveniences.h" +#include "./test-libmongoc.h" + #include +#include +#include + +#include -#include -#include -#include -#include "./TestSuite.h" -#include #include -#include "./test-libmongoc.h" -#include "./test-conveniences.h" +#include + +/* clang-format off */ typedef void (*sample_command_fn_t) (mongoc_database_t *db); typedef void (*sample_txn_command_fn_t) (mongoc_client_t *client); diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index 64042cba66..b67d3bff6a 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -1,6 +1,3 @@ -#include "./json-test.h" -#include "./test-libmongoc.h" - #include #include #include @@ -10,7 +7,6 @@ #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-socket.c b/src/libmongoc/tests/test-mongoc-socket.c index a17900fde3..a65e2ae7d5 100644 --- a/src/libmongoc/tests/test-mongoc-socket.c +++ b/src/libmongoc/tests/test-mongoc-socket.c @@ -1,5 +1,3 @@ -#include "./test-libmongoc.h" - #include #include #include @@ -8,7 +6,6 @@ #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-topology-reconcile.c b/src/libmongoc/tests/test-mongoc-topology-reconcile.c index 3619d9b4a5..39ec9ebcf2 100644 --- a/src/libmongoc/tests/test-mongoc-topology-reconcile.c +++ b/src/libmongoc/tests/test-mongoc-topology-reconcile.c @@ -1,10 +1,3 @@ -#include "./TestSuite.h" -#include "./mock_server/future-functions.h" -#include "./mock_server/future.h" -#include "./mock_server/mock-server.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include #include @@ -13,7 +6,6 @@ #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-topology-scanner.c b/src/libmongoc/tests/test-mongoc-topology-scanner.c index cf56dcdb4b..143aecb5d5 100644 --- a/src/libmongoc/tests/test-mongoc-topology-scanner.c +++ b/src/libmongoc/tests/test-mongoc-topology-scanner.c @@ -1,11 +1,3 @@ -#include "./TestSuite.h" -#include "./mock_server/future-functions.h" -#include "./mock_server/future.h" -#include "./mock_server/mock-rs.h" -#include "./mock_server/mock-server.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include #include @@ -16,7 +8,6 @@ #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-topology.c b/src/libmongoc/tests/test-mongoc-topology.c index fb829773f6..0fc0331b82 100644 --- a/src/libmongoc/tests/test-mongoc-topology.c +++ b/src/libmongoc/tests/test-mongoc-topology.c @@ -1,9 +1,3 @@ -#include "./mock_server/future-functions.h" -#include "./mock_server/future.h" -#include "./mock_server/mock-server.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include #include @@ -14,7 +8,6 @@ #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-with-transaction.c b/src/libmongoc/tests/test-mongoc-with-transaction.c index 94a5712c28..30c91fd6ac 100644 --- a/src/libmongoc/tests/test-mongoc-with-transaction.c +++ b/src/libmongoc/tests/test-mongoc-with-transaction.c @@ -1,12 +1,7 @@ -#include "./json-test.h" -#include "./test-conveniences.h" -#include "./test-libmongoc.h" - #include #include -#include #include #include From 4eba25c92df50e02c3cd8f296e763efbe6ce79be Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 24 Jul 2025 17:24:52 -0600 Subject: [PATCH 39/66] Drop more unneeded headers --- src/libmongoc/tests/json-test-operations.c | 1 - src/libmongoc/tests/mock_server/mock-server.c | 1 - src/libmongoc/tests/test-happy-eyeballs.c | 1 - src/libmongoc/tests/test-mongoc-client.c | 1 - src/libmongoc/tests/test-mongoc-dns.c | 1 - src/libmongoc/tests/test-mongoc-speculative-auth.c | 1 - src/libmongoc/tests/test-mongoc-stream-tls-error.c | 1 - src/libmongoc/tests/unified/operation.c | 1 - 8 files changed, 8 deletions(-) diff --git a/src/libmongoc/tests/json-test-operations.c b/src/libmongoc/tests/json-test-operations.c index 2470b55a0e..843ca54599 100644 --- a/src/libmongoc/tests/json-test-operations.c +++ b/src/libmongoc/tests/json-test-operations.c @@ -32,7 +32,6 @@ #include -#include #include #include diff --git a/src/libmongoc/tests/mock_server/mock-server.c b/src/libmongoc/tests/mock_server/mock-server.c index c149a8fe68..ba015f894a 100644 --- a/src/libmongoc/tests/mock_server/mock-server.c +++ b/src/libmongoc/tests/mock_server/mock-server.c @@ -33,7 +33,6 @@ #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-happy-eyeballs.c b/src/libmongoc/tests/test-happy-eyeballs.c index b2d6022042..979059a96a 100644 --- a/src/libmongoc/tests/test-happy-eyeballs.c +++ b/src/libmongoc/tests/test-happy-eyeballs.c @@ -8,7 +8,6 @@ #include #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-client.c b/src/libmongoc/tests/test-mongoc-client.c index f984f6ad56..1477e03c93 100644 --- a/src/libmongoc/tests/test-mongoc-client.c +++ b/src/libmongoc/tests/test-mongoc-client.c @@ -9,7 +9,6 @@ #include -#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-dns.c b/src/libmongoc/tests/test-mongoc-dns.c index c99f8b3f8d..3fafb8d837 100644 --- a/src/libmongoc/tests/test-mongoc-dns.c +++ b/src/libmongoc/tests/test-mongoc-dns.c @@ -7,7 +7,6 @@ #include #include -#include #include #ifdef MONGOC_ENABLE_SSL diff --git a/src/libmongoc/tests/test-mongoc-speculative-auth.c b/src/libmongoc/tests/test-mongoc-speculative-auth.c index 051c45eac0..5079b55781 100644 --- a/src/libmongoc/tests/test-mongoc-speculative-auth.c +++ b/src/libmongoc/tests/test-mongoc-speculative-auth.c @@ -16,7 +16,6 @@ #include -#include #include #ifdef _POSIX_VERSION #include diff --git a/src/libmongoc/tests/test-mongoc-stream-tls-error.c b/src/libmongoc/tests/test-mongoc-stream-tls-error.c index ed1aef78a8..d4f57d2345 100644 --- a/src/libmongoc/tests/test-mongoc-stream-tls-error.c +++ b/src/libmongoc/tests/test-mongoc-stream-tls-error.c @@ -4,7 +4,6 @@ #include #include -#include #include #ifdef MONGOC_ENABLE_SSL_OPENSSL diff --git a/src/libmongoc/tests/unified/operation.c b/src/libmongoc/tests/unified/operation.c index a4da39873e..21cbb30507 100644 --- a/src/libmongoc/tests/unified/operation.c +++ b/src/libmongoc/tests/unified/operation.c @@ -27,7 +27,6 @@ #include #include -#include #include #include From b6aea1e0e0ca305b2b7991936bd761ef643976d1 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 30 Jul 2025 17:04:27 -0600 Subject: [PATCH 40/66] Fix copyright statements --- src/common/src/mlib/duration.h | 2 +- src/common/src/mlib/platform.h | 2 +- src/common/src/mlib/time_point.h | 2 +- src/common/src/mlib/timer.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index a94abbcc7c..dc4473b8fc 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -12,7 +12,7 @@ * The `mlib_duration` is a trivial object that represents a duration of time. * The internal representation should not be inspected outside of this file. * - * @copyright Copyright (c) 2025 + * @copyright Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/common/src/mlib/platform.h b/src/common/src/mlib/platform.h index 0e44253ca0..da7b7d653d 100644 --- a/src/common/src/mlib/platform.h +++ b/src/common/src/mlib/platform.h @@ -6,7 +6,7 @@ * This file will conditionally include the general system headers available * for the current host platform. * - * @copyright Copyright (c) 2025 + * @copyright Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 0b083a63e1..4b3edd3d5b 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -7,7 +7,7 @@ * itself is relative to a monotonic clock for the program, so it should not be * transmitted or persisted outside of the execution of a program that uses it. * - * @copyright Copyright (c) 2025 + * @copyright Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/common/src/mlib/timer.h b/src/common/src/mlib/timer.h index c7e7edca7b..f0e5f65c91 100644 --- a/src/common/src/mlib/timer.h +++ b/src/common/src/mlib/timer.h @@ -6,7 +6,7 @@ * This file contains APIs for creating fixed-deadline timer object that represent * a stable expiration point. * - * @copyright Copyright (c) 2025 + * @copyright Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 41c978e0ab226c6713bbef0f863c9304a4aa4a64 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 30 Jul 2025 17:05:03 -0600 Subject: [PATCH 41/66] const correctness --- src/libmongoc/src/mongoc/mongoc-topology-scanner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c index 05eb496631..efdeb2d59d 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c @@ -114,7 +114,7 @@ _scanner_node_of (mongoc_async_cmd_t const *a) // Test whether two async commands are associated with the same topology scanner node, // and aren't the same command object static bool -_is_sibling_command (mongoc_async_cmd_t const *l, mongoc_async_cmd_t *r) +_is_sibling_command (mongoc_async_cmd_t const *l, mongoc_async_cmd_t const *r) { return l != r && _scanner_node_of (l) == _scanner_node_of (r); } From 36a1cda6807ec221fb7feb34440905ec20d8d7c6 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 30 Jul 2025 17:06:05 -0600 Subject: [PATCH 42/66] Missing headers for sleep() --- src/libmongoc/tests/test-mongoc-max-staleness.c | 2 ++ src/libmongoc/tests/test-mongoc-primary-stepdown.c | 2 ++ src/libmongoc/tests/test-mongoc-sdam-monitoring.c | 1 + src/libmongoc/tests/test-mongoc-sdam.c | 1 + 4 files changed, 6 insertions(+) diff --git a/src/libmongoc/tests/test-mongoc-max-staleness.c b/src/libmongoc/tests/test-mongoc-max-staleness.c index 1a2f6877e1..25ca2d166e 100644 --- a/src/libmongoc/tests/test-mongoc-max-staleness.c +++ b/src/libmongoc/tests/test-mongoc-max-staleness.c @@ -3,6 +3,8 @@ #include +#include + #include #include #include diff --git a/src/libmongoc/tests/test-mongoc-primary-stepdown.c b/src/libmongoc/tests/test-mongoc-primary-stepdown.c index 60c730f587..b6a17fe873 100644 --- a/src/libmongoc/tests/test-mongoc-primary-stepdown.c +++ b/src/libmongoc/tests/test-mongoc-primary-stepdown.c @@ -9,6 +9,8 @@ #include +#include + #include #include #include diff --git a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c index b9df4a8e57..4f07143759 100644 --- a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c +++ b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c @@ -4,6 +4,7 @@ #include #include +#include #include #include diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index b67d3bff6a..14cc7d5510 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -8,6 +8,7 @@ #include #include +#include #include #include From d362230cdb83cb7b7990ee2d2a45fb2db6e39838 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 30 Jul 2025 17:06:45 -0600 Subject: [PATCH 43/66] Rewrite some test macros as duration types --- src/libmongoc/tests/test-mongoc-sdam.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index 14cc7d5510..f607c6562d 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -754,9 +754,9 @@ heartbeat_succeeded (const mongoc_apm_server_heartbeat_succeeded_t *event) #endif } -#define RTT_TEST_TIMEOUT_SEC 60 -#define RTT_TEST_INITIAL_SLEEP_SEC 2 -#define RTT_TEST_TICK_MS 10 +#define RTT_TEST_TIMEOUT mlib_duration (1, min) +#define RTT_TEST_INITIAL_SLEEP mlib_duration (2, sec) +#define RTT_TEST_TICK mlib_duration (10, ms) static void test_prose_rtt (void *unused) @@ -774,7 +774,6 @@ test_prose_rtt (void *unused) prose_test_ctx_t ctx; bson_t cmd; bool ret; - int64_t start_us; bool satisfied; int64_t rtt = 0; @@ -799,7 +798,7 @@ test_prose_rtt (void *unused) /* Sleep for RTT_TEST_INITIAL_SLEEP_SEC seconds to allow multiple heartbeats * to succeed. */ - mlib_sleep_for (RTT_TEST_INITIAL_SLEEP_SEC, sec); + mlib_sleep_for (RTT_TEST_INITIAL_SLEEP); /* Set a failpoint to make hello commands take longer. */ bson_init (&cmd); @@ -826,8 +825,8 @@ test_prose_rtt (void *unused) /* Wait for the server's RTT to exceed 250ms. If this does not happen for * RTT_TEST_TIMEOUT_SEC seconds, consider it a failure. */ satisfied = false; - start_us = bson_get_monotonic_time (); - while (!satisfied && bson_get_monotonic_time () < start_us + RTT_TEST_TIMEOUT_SEC * 1000 * 1000) { + mlib_timer deadline = mlib_expires_after (RTT_TEST_TIMEOUT); + while (!satisfied && !mlib_timer_is_expired (deadline)) { mongoc_server_description_t *sd; sd = mongoc_client_select_server (client, true, NULL /* read prefs */, &error); @@ -837,11 +836,13 @@ test_prose_rtt (void *unused) satisfied = true; } mongoc_server_description_destroy (sd); - mlib_sleep_for (RTT_TEST_TICK_MS, ms); + mlib_sleep_for (RTT_TEST_TICK); } if (!satisfied) { - test_error ("After %d seconds, the latest observed RTT was only %" PRId64, RTT_TEST_TIMEOUT_SEC, rtt); + test_error ("After %d seconds, the latest observed RTT was only %" PRId64, + (int) mlib_seconds_count (RTT_TEST_TIMEOUT), + rtt); } /* Disable the failpoint. */ From 3e29b8fcc114572e895bd0354b2064a7ca185ff9 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 30 Jul 2025 17:25:46 -0600 Subject: [PATCH 44/66] Tweak spelling of seconds suffix, add hour suffix --- src/common/src/mlib/duration.h | 15 +++-- src/common/tests/test-mlib.c | 61 +++++++++++-------- src/libmongoc/tests/test-happy-eyeballs.c | 2 +- src/libmongoc/tests/test-mongoc-client.c | 2 +- src/libmongoc/tests/test-mongoc-cluster.c | 2 +- .../tests/test-mongoc-max-staleness.c | 6 +- .../tests/test-mongoc-primary-stepdown.c | 2 +- src/libmongoc/tests/test-mongoc-sdam.c | 2 +- .../tests/test-mongoc-topology-reconcile.c | 2 +- src/libmongoc/tests/test-mongoc-topology.c | 6 +- 10 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index dc4473b8fc..bb48c018b7 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -261,8 +261,9 @@ _mlibDurationMaxBetween (mlib_duration lhs, mlib_duration rhs) * - `ns` (nanoseconds) * - `us` (microseconds) * - `ms` (milliseconds) - * - `sec` (seconds) + * - `s` (seconds) * - `min` (minutes) + * - `h` (hours) * * Other unit suffixes will generate a compile-time error */ @@ -306,7 +307,7 @@ _mlibCreateDurationFromUnitCount_ms (const mlib_upsized_integer n) mlib_noexcept } static inline mlib_duration -_mlibCreateDurationFromUnitCount_sec (const mlib_upsized_integer n) +_mlibCreateDurationFromUnitCount_s (const mlib_upsized_integer n) { return mlib_duration (_mlibCreateDurationFromUnitCount_us (n), mul, 1000 * 1000); } @@ -317,6 +318,12 @@ _mlibCreateDurationFromUnitCount_min (const mlib_upsized_integer n) return mlib_duration (_mlibCreateDurationFromUnitCount_us (n), mul, 60 * 1000 * 1000); } +static inline mlib_duration +_mlibCreateDurationFromUnitCount_h (const mlib_upsized_integer n) +{ + return mlib_duration (_mlibCreateDurationFromUnitCount_min (n), mul, 60); +} + /** * @brief Compare two durations * @@ -355,7 +362,7 @@ mlib_duration_cmp (const mlib_duration a, const mlib_duration b) mlib_noexcept static inline mlib_duration mlib_duration_from_timespec (const struct timespec ts) mlib_noexcept { - return mlib_duration ((ts.tv_sec, sec), plus, (ts.tv_nsec, ns)); + return mlib_duration ((ts.tv_sec, s), plus, (ts.tv_nsec, ns)); } /** @@ -370,7 +377,7 @@ mlib_duration_to_timespec (const mlib_duration d) mlib_noexcept // Number of full seconds in the duration const mlib_duration_rep_t n_full_seconds = mlib_seconds_count (d); // Duration with full seconds removed - const mlib_duration usec_part = mlib_duration (d, minus, (n_full_seconds, sec)); + const mlib_duration usec_part = mlib_duration (d, minus, (n_full_seconds, s)); // Number of microseconds in the duration, minus all full seconds const mlib_duration_rep_t n_remaining_microseconds = mlib_microseconds_count (usec_part); // Compute the number of nanoseconds: diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 4ebe544beb..747eae342b 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -926,39 +926,46 @@ _test_duration (void) mlib_check (mlib_duration_cmp (d, ==, (10, ms))); d = mlib_duration (10, us); mlib_check (mlib_duration_cmp (d, ==, (10, us))); - d = mlib_duration (10, sec); - mlib_check (mlib_duration_cmp (d, ==, (10, sec))); + d = mlib_duration (10, s); + mlib_check (mlib_duration_cmp (d, ==, (10, s))); - d = mlib_duration ((10, sec), mul, 3); - mlib_check (mlib_duration_cmp (d, ==, (30, sec))); + d = mlib_duration ((10, s), mul, 3); + mlib_check (mlib_duration_cmp (d, ==, (30, s))); - d = mlib_duration ((10, sec), plus, (40, ms)); + d = mlib_duration ((10, s), plus, (40, ms)); mlib_check (mlib_duration_cmp (d, ==, (10040, ms))); - d = mlib_duration ((10, sec), div, 20); + d = mlib_duration ((10, s), div, 20); mlib_check (mlib_duration_cmp (d, ==, (500, ms))); - d = mlib_duration ((4, sec), min, (400, ms)); + d = mlib_duration ((4, s), min, (400, ms)); mlib_check (mlib_duration_cmp (d, ==, (400, ms))); + d = mlib_duration (10, min); + mlib_check (mlib_duration_cmp (d, ==, (600, s))); + + d = mlib_duration (4, h); + mlib_check (mlib_duration_cmp (d, ==, (240, min))); + + d = mlib_duration ((10, s), div, 20); d = mlib_duration ( // At least 5 seconds: - (d, max, (5, sec)), + (d, max, (5, s)), // At most 90 seconds: min, - (90, sec)); - mlib_check (mlib_duration_cmp (d, ==, (5, sec))); + (90, s)); + mlib_check (mlib_duration_cmp (d, ==, (5, s))); // Comparison - mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (4, sec)) == 0); - mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (5, sec)) < 0); - mlib_check (mlib_duration_cmp (mlib_duration (4, sec), mlib_duration (-5, sec)) > 0); - mlib_check (mlib_duration_cmp ((4, sec), ==, (4, sec))); - mlib_check (mlib_duration_cmp ((4, sec), <, (5, sec))); - mlib_check (mlib_duration_cmp ((4, sec), >, (-5, sec))); + mlib_check (mlib_duration_cmp (mlib_duration (4, s), mlib_duration (4, s)) == 0); + mlib_check (mlib_duration_cmp (mlib_duration (4, s), mlib_duration (5, s)) < 0); + mlib_check (mlib_duration_cmp (mlib_duration (4, s), mlib_duration (-5, s)) > 0); + mlib_check (mlib_duration_cmp ((4, s), ==, (4, s))); + mlib_check (mlib_duration_cmp ((4, s), <, (5, s))); + mlib_check (mlib_duration_cmp ((4, s), >, (-5, s))); // Overflow saturates: - d = mlib_duration (mlib_maxof (mlib_duration_rep_t), sec); + d = mlib_duration (mlib_maxof (mlib_duration_rep_t), s); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration (d, mul, 16); @@ -974,12 +981,12 @@ _test_duration (void) d = mlib_duration (-1729, us); mlib_check (mlib_milliseconds_count (d), eq, -1); - d = mlib_duration ((-3, sec), plus, mlib_duration_min ()); + d = mlib_duration ((-3, s), plus, mlib_duration_min ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_min ())); - d = mlib_duration ((4, sec), plus, mlib_duration_max ()); + d = mlib_duration ((4, s), plus, mlib_duration_max ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); - d = mlib_duration ((4, sec), minus, (2271, ms)); + d = mlib_duration ((4, s), minus, (2271, ms)); mlib_check (mlib_milliseconds_count (d), eq, 1729); // Overflow saturates: d = mlib_duration ((-4, ms), minus, mlib_duration_max ()); @@ -987,8 +994,8 @@ _test_duration (void) d = mlib_duration ((4, ms), minus, mlib_duration_min ()); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); - d = mlib_duration ((4, sec), mul, 5); - mlib_check (mlib_duration_cmp (d, ==, (20, sec))); + d = mlib_duration ((4, s), mul, 5); + mlib_check (mlib_duration_cmp (d, ==, (20, s))); d = mlib_duration (mlib_duration_max (), mul, 2); mlib_check (mlib_duration_cmp (d, ==, mlib_duration_max ())); d = mlib_duration (mlib_duration_max (), mul, -2); @@ -1012,7 +1019,7 @@ _test_duration (void) ts.tv_sec = 4; ts.tv_nsec = 0; d = mlib_duration_from_timespec (ts); - mlib_check (mlib_duration_cmp (d, ==, (4, sec))); + mlib_check (mlib_duration_cmp (d, ==, (4, s))); // ts.tv_sec = -3; ts.tv_nsec = -4000; @@ -1030,7 +1037,7 @@ _test_time_point (void) mlib_time_point t = mlib_now (); // Offset the time point - mlib_time_point later = mlib_later (t, (1, sec)); + mlib_time_point later = mlib_later (t, (1, s)); mlib_check (mlib_time_cmp (t, <, later)); // Difference between two time points is a duration: @@ -1057,7 +1064,7 @@ _test_sleep (void) // Sleeping for a negative duration returns immediately with success start = mlib_now (); - mlib_check (mlib_sleep_for (-10, sec), eq, 0); + mlib_check (mlib_sleep_for (-10, s), eq, 0); mlib_check (mlib_duration_cmp (mlib_elapsed_since (start), <, (100, ms))); // Sleeping until a point in the past returns immediately as well @@ -1083,14 +1090,14 @@ _test_timer (void) // Try with a not-yet-expired timer cond = false; - tm = mlib_expires_after (10, sec); + tm = mlib_expires_after (10, s); mlib_check (!mlib_timer_is_expired (tm)); mlib_check (!mlib_timer_is_expired (tm, &cond)); // cond was set to `true`, even though we are not yet expired mlib_check (cond); // Create a timer that expired in the past - tm = mlib_expires_at (mlib_later (mlib_now (), (-10, sec))); + tm = mlib_expires_at (mlib_later (mlib_now (), (-10, s))); mlib_check (mlib_timer_is_expired (tm)); } diff --git a/src/libmongoc/tests/test-happy-eyeballs.c b/src/libmongoc/tests/test-happy-eyeballs.c index 979059a96a..2965126a72 100644 --- a/src/libmongoc/tests/test-happy-eyeballs.c +++ b/src/libmongoc/tests/test-happy-eyeballs.c @@ -393,7 +393,7 @@ test_happy_eyeballs_dns_cache (void) mongoc_topology_scanner_node_disconnect (testcase.state.ts->nodes, false); /* wait for DNS cache to expire. */ - mlib_sleep_for (2, sec); + mlib_sleep_for (2, s); /* after running once, the topology scanner should have cached the DNS * result for IPv6. It should complete immediately. */ diff --git a/src/libmongoc/tests/test-mongoc-client.c b/src/libmongoc/tests/test-mongoc-client.c index 1477e03c93..d8cc10fa93 100644 --- a/src/libmongoc/tests/test-mongoc-client.c +++ b/src/libmongoc/tests/test-mongoc-client.c @@ -2991,7 +2991,7 @@ _test_client_sends_handshake (bool pooled) mlib_sleep_for ( // (MONGOC_TOPOLOGY_COOLDOWN_MS, ms), plus, - (1, sec)); + (1, s)); future = _force_hello_with_ping (client, heartbeat_ms); } diff --git a/src/libmongoc/tests/test-mongoc-cluster.c b/src/libmongoc/tests/test-mongoc-cluster.c index 81c6f4d0d0..a9d0d4d0d4 100644 --- a/src/libmongoc/tests/test-mongoc-cluster.c +++ b/src/libmongoc/tests/test-mongoc-cluster.c @@ -502,7 +502,7 @@ _test_cluster_time (bool pooled, command_fn_t command) client = mongoc_client_pool_pop (pool); /* CDRIVER-3596 - prevent client discovery of the pool interfering with * the test operations. */ - mlib_sleep_for (5, sec); + mlib_sleep_for (5, s); } else { client = test_framework_new_default_client (); mongoc_client_set_apm_callbacks (client, callbacks, &cluster_time_test); diff --git a/src/libmongoc/tests/test-mongoc-max-staleness.c b/src/libmongoc/tests/test-mongoc-max-staleness.c index 25ca2d166e..5ef140e817 100644 --- a/src/libmongoc/tests/test-mongoc-max-staleness.c +++ b/src/libmongoc/tests/test-mongoc-max-staleness.c @@ -225,15 +225,15 @@ _test_last_write_date (bool pooled) r = mongoc_collection_insert_one (collection, tmp_bson ("{}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); s0 = mongoc_topology_select (client->topology, MONGOC_SS_WRITE, TEST_SS_LOG_CONTEXT, NULL, NULL, &error); ASSERT_OR_PRINT (s0, error); - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); r = mongoc_collection_insert_one (collection, tmp_bson ("{}"), NULL, NULL, &error); ASSERT_OR_PRINT (r, error); - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); s1 = mongoc_topology_select (client->topology, MONGOC_SS_WRITE, TEST_SS_LOG_CONTEXT, NULL, NULL, &error); ASSERT_OR_PRINT (s1, error); ASSERT_CMPINT64 (s1->last_write_date_ms, !=, (int64_t) -1); diff --git a/src/libmongoc/tests/test-mongoc-primary-stepdown.c b/src/libmongoc/tests/test-mongoc-primary-stepdown.c index b6a17fe873..8d8534e248 100644 --- a/src/libmongoc/tests/test-mongoc-primary-stepdown.c +++ b/src/libmongoc/tests/test-mongoc-primary-stepdown.c @@ -126,7 +126,7 @@ _run_test_single_or_pooled (_test_fn_t test, bool use_pooled) _setup_test_with_client (client); /* Wait one second to be assured that the RTT connection has been * established as well. */ - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); test (client); mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index f607c6562d..ce91a41e20 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -755,7 +755,7 @@ heartbeat_succeeded (const mongoc_apm_server_heartbeat_succeeded_t *event) } #define RTT_TEST_TIMEOUT mlib_duration (1, min) -#define RTT_TEST_INITIAL_SLEEP mlib_duration (2, sec) +#define RTT_TEST_INITIAL_SLEEP mlib_duration (2, s) #define RTT_TEST_TICK mlib_duration (10, ms) static void diff --git a/src/libmongoc/tests/test-mongoc-topology-reconcile.c b/src/libmongoc/tests/test-mongoc-topology-reconcile.c index 39ec9ebcf2..72a7cf51da 100644 --- a/src/libmongoc/tests/test-mongoc-topology-reconcile.c +++ b/src/libmongoc/tests/test-mongoc-topology-reconcile.c @@ -250,7 +250,7 @@ _test_topology_reconcile_sharded (bool pooled) request_destroy (request); /* make sure the mongos response is processed first */ - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); /* replica set secondary - topology removes it */ request = mock_server_receives_any_hello (secondary); diff --git a/src/libmongoc/tests/test-mongoc-topology.c b/src/libmongoc/tests/test-mongoc-topology.c index 0fc0331b82..9e9573ef6d 100644 --- a/src/libmongoc/tests/test-mongoc-topology.c +++ b/src/libmongoc/tests/test-mongoc-topology.c @@ -716,7 +716,7 @@ test_cooldown_standalone (void) MONGOC_ERROR_SERVER_SELECTION_FAILURE, "No servers yet eligible for rescan"); - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); /* third selection doesn't try to call hello: we're still in cooldown */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -834,7 +834,7 @@ test_cooldown_rs (void) BSON_ASSERT (!future_get_mongoc_server_description_ptr (future)); future_destroy (future); - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); /* second selection doesn't try hello on server 1: it's in cooldown */ future = future_topology_select (client->topology, MONGOC_SS_READ, TEST_SS_LOG_CONTEXT, primary_pref, NULL, &error); @@ -1235,7 +1235,7 @@ test_rtt (void *ctx) future = future_client_command_simple (client, "db", tmp_bson ("{'ping': 1}"), NULL, NULL, &error); request = mock_server_receives_any_hello (server); - mlib_sleep_for (1, sec); + mlib_sleep_for (1, s); reply_to_request ( request, MONGOC_REPLY_NONE, From 07c256811af80ee0858fcda8a344c9f1407499cf Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 30 Jul 2025 17:28:15 -0600 Subject: [PATCH 45/66] Drive-by timer change --- src/libmongoc/tests/test-mongoc-sdam-monitoring.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c index 4f07143759..0fcb15654d 100644 --- a/src/libmongoc/tests/test-mongoc-sdam-monitoring.c +++ b/src/libmongoc/tests/test-mongoc-sdam-monitoring.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -1060,7 +1061,7 @@ smm_new (const char *mode) static bool smm_wait (smm_t *t, size_t count) { - int64_t started = bson_get_monotonic_time (); + const mlib_timer deadline = mlib_expires_after (10, s); while (true) { bson_mutex_lock (&t->lock); if (t->events_len >= count) { @@ -1069,9 +1070,8 @@ smm_wait (smm_t *t, size_t count) } bson_mutex_unlock (&t->lock); - int64_t now = bson_get_monotonic_time (); // Timeout - if (now - started > 10 * 1000 * 1000) { + if (mlib_timer_is_expired (deadline)) { break; } mlib_sleep_for (500, ms); From 1232d4680d7491dab58cdb81886c90681c72fe9e Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 31 Jul 2025 08:59:46 -0600 Subject: [PATCH 46/66] Add a function to get the cock ID for mlib_now() --- src/common/src/mlib/time_point.h | 33 ++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 4b3edd3d5b..1b818dbe83 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -40,6 +40,7 @@ #endif #include +#include #include mlib_extern_c_begin (); @@ -94,6 +95,28 @@ mlib_latest (mlib_time_point l, mlib_time_point r) return l; } +/** + * @brief Obtain the integer clock ID that is used by `mlib_now()` to obtain + * the time. This value only has meaning on POSIX systems. On Win32, returns + * INT_MIN. + * + * @return int The integer clock ID, corresponding to a value of `CLOCK_...` + * object macro. + */ +static inline int +mlib_now_clockid (void) mlib_noexcept +{ +#ifdef CLOCK_MONOTONIC_RAW + // Linux had a bad definition of CLOCK_MONOTONIC, which would jump based on NTP adjustments. + // They replaced it with CLOCK_MONOTONIC_RAW, which is stable and cannot be adjusted. + return CLOCK_MONOTONIC_RAW; +#elif defined(CLOCK_MONOTONIC) + return CLOCK_MONOTONIC; +#else + return INT_MIN; +#endif +} + /** * @brief Obtain a point-in-time corresponding to the current time */ @@ -103,14 +126,8 @@ mlib_now (void) mlib_noexcept #if mlib_have_posix_clocks() // Use POSIX clock_gettime struct timespec ts; -// Use the POSIX monotonic clock -#ifdef CLOCK_MONOTONIC_RAW - // Linux had a bad definition of CLOCK_MONOTONIC, which would jump based on NTP adjustments. - // They replaced it with CLOCK_MONOTONIC_RAW, which is stable and cannot be adjusted. - int rc = clock_gettime (CLOCK_MONOTONIC_RAW, &ts); -#else - int rc = clock_gettime (CLOCK_MONOTONIC, &ts); -#endif + // Use the POSIX monotonic clock + int rc = clock_gettime (mlib_now_clockid (), &ts); // The above call must never fail: mlib_check (rc, eq, 0); // Encode the time point: From 9f3b1922a027cd3cc418e18aff4e0aec54362ed8 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 6 Aug 2025 10:36:13 -0600 Subject: [PATCH 47/66] Tweaks following PR reviews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use upsized integers for duration arithmetic. - Rename `mlib_later` → `mlib_time_add` - Drop the `mlib_time_adjust` function. - Rename `mlib_soonest` → `mlib_earliest`. - Various spelling and comment tweaks. --- src/common/src/mlib/duration.h | 30 ++++++++++++------- src/common/src/mlib/time_point.h | 25 +++------------- src/common/src/mlib/timer.h | 6 ++-- src/common/tests/test-mlib.c | 4 +-- .../src/mongoc/mongoc-async-cmd-private.h | 11 ++++--- src/libmongoc/src/mongoc/mongoc-async-cmd.c | 4 +-- src/libmongoc/src/mongoc/mongoc-async.c | 2 +- 7 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index bb48c018b7..08390588b8 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -116,7 +116,7 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept * - `mlib_duration(, , )` * Manipulates a duration according to ``. * - * In the above, `` may be a parenthsized `mlib_duration` argument list or a + * In the above, `` may be a parenthesized `mlib_duration` argument list or a * duration object; `` must be an integral expression and `` is a * unit suffix identifer (see: `mlib_duration_with_unit`) to create a duration * of `` instances of ``, and `` is one of: @@ -163,13 +163,15 @@ _mlibDurationCopy (mlib_duration d) } // Duration scalar multiply -#define _mlibDurationInfixOperator_mul(LHS, Fac) _mlibDurationMultiply (_mlibDurationArgument (LHS), (Fac)) +#define _mlibDurationInfixOperator_mul(LHS, Fac) \ + _mlibDurationMultiply (_mlibDurationArgument (LHS), mlib_upsize_integer (Fac)) static inline mlib_duration -_mlibDurationMultiply (const mlib_duration dur, int fac) mlib_noexcept +_mlibDurationMultiply (const mlib_duration dur, mlib_upsized_integer fac) mlib_noexcept { mlib_duration ret = {0}; - if (mlib_mul (&ret._rep, dur._rep, fac)) { - if ((dur._rep < 0) != (fac < 0)) { + uintmax_t bits; + if ((mlib_mul) (&bits, true, true, (uintmax_t) dur._rep, fac.is_signed, fac.bits.as_unsigned)) { + if ((dur._rep < 0) != (fac.is_signed && fac.bits.as_signed < 0)) { // Different signs: Neg × Pos = Neg ret = mlib_duration_min (); } else { @@ -177,21 +179,29 @@ _mlibDurationMultiply (const mlib_duration dur, int fac) mlib_noexcept // Neg × Neg = Pos ret = mlib_duration_max (); } + } else { + ret._rep = (intmax_t) bits; } return ret; } // Duration scalar divide -#define _mlibDurationInfixOperator_div(LHS, Div) _mlibDurationDivide (_mlibDurationArgument (LHS), (Div)) +#define _mlibDurationInfixOperator_div(LHS, Div) \ + _mlibDurationDivide (_mlibDurationArgument (LHS), mlib_upsize_integer (Div)) static inline mlib_duration -_mlibDurationDivide (mlib_duration a, int div) mlib_noexcept +_mlibDurationDivide (mlib_duration a, mlib_upsized_integer div) mlib_noexcept { - mlib_check (div, neq, 0); - if (div == -1 && a._rep == mlib_minof (mlib_duration_rep_t)) { + mlib_check (div.bits.as_unsigned, neq, 0); + if ((div.is_signed && div.bits.as_signed == -1) // + && a._rep == mlib_minof (mlib_duration_rep_t)) { // MIN / -1 is UB, but the saturating result is the max a = mlib_duration_max (); } else { - a._rep /= div; + if (div.is_signed) { + a._rep /= div.bits.as_signed; + } else { + a._rep /= div.bits.as_unsigned; + } } return a; } diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 1b818dbe83..bcb3ccbdbf 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -32,6 +32,7 @@ // Check for POSIX clock functions functions #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_DEFAULT_SOURCE) && !defined(_WIN32)) #include +#undef mlib_have_posix_clocks #define mlib_have_posix_clocks() 1 #endif @@ -79,7 +80,7 @@ typedef struct mlib_time_point { * @brief Given two time points, selects the time point that occurs earliest */ static inline mlib_time_point -mlib_soonest (mlib_time_point l, mlib_time_point r) +mlib_earliest (mlib_time_point l, mlib_time_point r) { l.time_since_monotonic_start = mlib_duration (l.time_since_monotonic_start, min, r.time_since_monotonic_start); return l; @@ -172,32 +173,14 @@ mlib_now (void) mlib_noexcept * is a point-in-time *before* 'from'. */ static inline mlib_time_point -mlib_later (mlib_time_point from, mlib_duration delta) mlib_noexcept +mlib_time_add (mlib_time_point from, mlib_duration delta) mlib_noexcept { mlib_time_point ret; ret.time_since_monotonic_start = mlib_duration (from.time_since_monotonic_start, plus, delta); return ret; } -#define mlib_later(From, Delta) mlib_later (From, mlib_duration_arg (Delta)) - -/** - * @brief Adjust a time-point in-place - * - * @param time Pointer to the valid time object to be updated - * @param delta The duration to add/remove from the point-in-time - * @return mlib_time_point Returns the previous value of the time point - */ -static inline mlib_time_point -mlib_time_adjust (mlib_time_point *time, mlib_duration delta) mlib_noexcept -{ - mlib_check (time, because, "Pointer-to-time is required"); - mlib_time_point prev = *time; - *time = mlib_later (*time, delta); - return prev; -} - -#define mlib_time_adjust(Time, Duration) mlib_time_adjust (Time, mlib_duration_arg (Duration)) +#define mlib_time_add(From, Delta) mlib_time_add (From, mlib_duration_arg (Delta)) /** * @brief Obtain the duration between two points in time. diff --git a/src/common/src/mlib/timer.h b/src/common/src/mlib/timer.h index f0e5f65c91..b93620855b 100644 --- a/src/common/src/mlib/timer.h +++ b/src/common/src/mlib/timer.h @@ -64,7 +64,7 @@ mlib_expires_at (const mlib_time_point t) mlib_noexcept static inline mlib_timer mlib_expires_after (const mlib_duration dur) mlib_noexcept { - const mlib_time_point later = mlib_later (mlib_now (), dur); + const mlib_time_point later = mlib_time_add (mlib_now (), dur); return mlib_expires_at (later); } @@ -76,7 +76,7 @@ mlib_expires_after (const mlib_duration dur) mlib_noexcept static inline mlib_timer mlib_soonest_timer (mlib_timer l, mlib_timer r) mlib_noexcept { - l.expires_at = mlib_soonest (l.expires_at, r.expires_at); + l.expires_at = mlib_earliest (l.expires_at, r.expires_at); return l; } @@ -118,7 +118,7 @@ mlib_timer_remaining (const mlib_timer timer) mlib_noexcept * * ``` * void do_thing() { - * bool once = 0; + * bool once = false; * while (!mlib_timer_is_expired(timer, &once)) { * try_thing(timer); * } diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 747eae342b..86bb9f57c3 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -1037,7 +1037,7 @@ _test_time_point (void) mlib_time_point t = mlib_now (); // Offset the time point - mlib_time_point later = mlib_later (t, (1, s)); + mlib_time_point later = mlib_time_add (t, (1, s)); mlib_check (mlib_time_cmp (t, <, later)); // Difference between two time points is a duration: @@ -1097,7 +1097,7 @@ _test_timer (void) mlib_check (cond); // Create a timer that expired in the past - tm = mlib_expires_at (mlib_later (mlib_now (), (-10, s))); + tm = mlib_expires_at (mlib_time_add (mlib_now (), (-10, s))); mlib_check (mlib_timer_is_expired (tm)); } diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index f70a808170..71f8f4787d 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -139,8 +139,8 @@ typedef struct _mongoc_async_cmd { // Arbitrary userdata pointer passed to the stream setup function void *_stream_setup_userdata; /** - * @brief User-provided command event callback. Invoked with the final result - * and once when the command connects. + * @brief User-provided command event callback. Invoked after a new + * connection is established, and again when the command completes. */ mongoc_async_cmd_event_cb _event_callback; // Arbitrary userdata passed when the object was created @@ -244,7 +244,8 @@ mongoc_async_cmd_new (mongoc_async_t *async, static inline mlib_timer _acmd_deadline (const mongoc_async_cmd_t *self) { - return mlib_expires_at (mlib_later (self->_start_time, self->_timeout)); + BSON_ASSERT_PARAM (self); + return mlib_expires_at (mlib_time_add (self->_start_time, self->_timeout)); } /** @@ -265,6 +266,8 @@ _acmd_has_timed_out (const mongoc_async_cmd_t *self) static inline void _acmd_cancel (mongoc_async_cmd_t *self) { + BSON_ASSERT_PARAM (self); + // XXX: Should this check if ther command has already finished/failed? self->state = MONGOC_ASYNC_CMD_CANCELLED_STATE; } @@ -281,7 +284,7 @@ _acmd_cancel (mongoc_async_cmd_t *self) static inline void _acmd_adjust_connect_delay (mongoc_async_cmd_t *self, const mlib_duration d) { - mlib_time_adjust (&self->_connect_delay_timer.expires_at, d); + self->_connect_delay_timer.expires_at = mlib_time_add (self->_connect_delay_timer.expires_at, d); } /** diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index 9617e09ef7..eaeffe080a 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -202,7 +202,8 @@ mongoc_async_cmd_new (mongoc_async_t *async, BSON_ASSERT_PARAM (dbname); mongoc_async_cmd_t *const acmd = BSON_ALIGNED_ALLOC0 (mongoc_async_cmd_t); - acmd->_connect_delay_timer = mlib_expires_after (connect_delay); + acmd->_start_time = mlib_now (); + acmd->_connect_delay_timer = mlib_expires_at (mlib_time_add (acmd->_start_time, connect_delay)); acmd->async = async; acmd->dns_result = dns_result; acmd->_timeout = timeout; @@ -212,7 +213,6 @@ mongoc_async_cmd_new (mongoc_async_t *async, acmd->_stream_setup_userdata = setup_userdata; acmd->_event_callback = event_cb; acmd->_userdata = userdata; - acmd->_start_time = mlib_now (); acmd->state = MONGOC_ASYNC_CMD_PENDING_CONNECT; acmd->_response_data = (bson_t) BSON_INITIALIZER; bson_copy_to (cmd, &acmd->_command); diff --git a/src/libmongoc/src/mongoc/mongoc-async.c b/src/libmongoc/src/mongoc/mongoc-async.c index b552a7b8a4..7aaefdbabe 100644 --- a/src/libmongoc/src/mongoc/mongoc-async.c +++ b/src/libmongoc/src/mongoc/mongoc-async.c @@ -64,7 +64,7 @@ mongoc_async_run (mongoc_async_t *async) DL_FOREACH (async->cmds, acmd) { - // XXX: See _acmd_reset_elapsed doc comment to explain this hack + // CDRIVER-1571: See _acmd_reset_elapsed doc comment to explain this hack _acmd_reset_elapsed (acmd); } From 6aa01b7efe8e189b3517481ab720dbd96ef982a5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 6 Aug 2025 10:40:28 -0600 Subject: [PATCH 48/66] `MLIB_DEFERRED` macro for simpler macro magic --- src/common/src/mlib/config.h | 30 ++++++++++++++++++++++++------ src/common/src/mlib/duration.h | 15 ++++++--------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index 3ad80b1ec1..f34266445d 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -97,10 +97,29 @@ /** * @brief Expands to `1` if the given macro argument is a parenthesized group of - * tokens, otherwize `0` + * tokens, otherwise `0` */ #define MLIB_IS_PARENTHESIZED(X) \ - _mlibHasComma(_mlibCommaIfParens X MLIB_NOTHING("Inhibit immediate expansion: " #X)) + _mlibHasComma(_mlibCommaIfParens X) + +/** + * @brief Pass a function-like macro name, inhibiting its expansion until the + * next pass: + * + * #define func_macro(x) x + * + * MLIB_DEFERRED(func_macro)(foo) // Expands to "func_macro(foo)", not "foo" + */ +#define MLIB_DEFERRED(MacroName) \ + /* Expand to the macro name: */ \ + MacroName \ + /*- + * Place a separator between the function macro name and whatever comes next + * in the file. Presumably, the next token will be the parens to invoke "MacroName", + * but this separator inhibits its expansion unless something else comes + * along to do another expansion pass + */ \ + MLIB_NOTHING("[separator]") /** * A helper for isEmpty(): If given (0, 0, 0, 1), expands as: @@ -138,9 +157,8 @@ * is not expanded and is discarded. */ #define MLIB_IF_ELSE(...) MLIB_PASTE (_mlibIfElseBranch_, MLIB_BOOLEAN (__VA_ARGS__)) -#define _mlibIfElseBranch_1(...) __VA_ARGS__ _mlibNoExpandNothing -#define _mlibIfElseBranch_0(...) MLIB_NOTHING (#__VA_ARGS__) MLIB_JUST -#define _mlibNoExpandNothing(...) MLIB_NOTHING (#__VA_ARGS__) +#define _mlibIfElseBranch_1(...) __VA_ARGS__ MLIB_NOTHING +#define _mlibIfElseBranch_0(...) MLIB_JUST /** * @brief Expands to an integer literal corresponding to the number of macro @@ -155,7 +173,7 @@ * @brief Expand to a call expression `Prefix##_argc_N(...)`, where `N` is the * number of macro arguments. */ -#define MLIB_ARGC_PICK(Prefix, ...) MLIB_EVAL_4 (MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__)) +#define MLIB_ARGC_PICK(Prefix, ...) MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__) #define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3 (Prefix, _argc_, MLIB_ARG_COUNT (__VA_ARGS__)) #ifdef __cplusplus diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 08390588b8..6fb1e01b54 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -129,19 +129,17 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept * arithmetic, and never wrap or trap. */ #define mlib_duration(...) MLIB_EVAL_16 (_mlibDurationMagic (__VA_ARGS__)) -#define _mlibDurationMagic(...) \ - MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__) \ - MLIB_NOTHING ("force the argument list to expand first") \ +#define _mlibDurationMagic(...) \ + MLIB_DEFERRED (MLIB_ARGC_PASTE (_mlib_duration, __VA_ARGS__)) \ (__VA_ARGS__) // Wraps a `` argument, and expands to the magic only if it is parenthesized #define _mlibDurationArgument(X) \ /* If given a parenthesized expression, act as an invocation of `mlib_duration() */ \ MLIB_IF_ELSE (MLIB_IS_PARENTHESIZED (X)) \ - MLIB_NOTHING ("force the 'then' branch to expand first") \ - /* then: */ (_mlibDurationMagic MLIB_NOTHING () X) /* else: */ (X) + /* then: */ (_mlibDurationMagic X) /* else: */ (X) // Wrap a macro argument that should support the duration DSL -#define mlib_duration_arg(X) MLIB_EVAL_8 (_mlibDurationArgument (X)) +#define mlib_duration_arg(X) MLIB_EVAL_16 (_mlibDurationArgument (X)) // Zero arguments, just return a zero duration: #define _mlib_duration_argc_0() (mlib_init (mlib_duration){0}) @@ -150,9 +148,8 @@ mlib_seconds_count (const mlib_duration dur) mlib_noexcept // Two arguments, the second arg is a unit suffix: #define _mlib_duration_argc_2(Count, Unit) mlib_duration_with_unit (Count, Unit) // Three arguments, an infix operation: -#define _mlib_duration_argc_3(Duration, Operator, Operand) \ - MLIB_PASTE (_mlibDurationInfixOperator_, Operator) \ - MLIB_NOTHING ("force the arguments to expand first") \ +#define _mlib_duration_argc_3(Duration, Operator, Operand) \ + MLIB_DEFERRED (MLIB_PASTE (_mlibDurationInfixOperator_, Operator)) \ (Duration, Operand) // By-value copy a duration From d7031fedc9c6a7536d09f439e803951dc88991e6 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 6 Aug 2025 10:59:15 -0600 Subject: [PATCH 49/66] Rewrtie client_pool_pop() to use our timer type --- src/common/src/mlib/timer.h | 15 +++++++++++++++ src/libmongoc/src/mongoc/mongoc-async.c | 2 +- src/libmongoc/src/mongoc/mongoc-client-pool.c | 19 +++++++++++-------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/common/src/mlib/timer.h b/src/common/src/mlib/timer.h index b93620855b..febbd37ae4 100644 --- a/src/common/src/mlib/timer.h +++ b/src/common/src/mlib/timer.h @@ -70,6 +70,21 @@ mlib_expires_after (const mlib_duration dur) mlib_noexcept #define mlib_expires_after(...) mlib_expires_after (mlib_duration (__VA_ARGS__)) +/** + * @brief Obtain a timer that will "never" expire + * + * In actuality, the timer expires at a time so far in the future that no computer + * program could ever hope to continue running to that point, and by the time + * that point is reached it will be some other civilization's problem. + */ +static inline mlib_timer +mlib_expires_never (void) mlib_noexcept +{ + mlib_timer t; + t.expires_at.time_since_monotonic_start = mlib_duration_max (); + return t; +} + /** * @brief Between two timers, return the timer that will expire the soonest */ diff --git a/src/libmongoc/src/mongoc/mongoc-async.c b/src/libmongoc/src/mongoc/mongoc-async.c index 7aaefdbabe..52edebe8f3 100644 --- a/src/libmongoc/src/mongoc/mongoc-async.c +++ b/src/libmongoc/src/mongoc/mongoc-async.c @@ -80,7 +80,7 @@ mongoc_async_run (mongoc_async_t *async) unsigned nstreams = 0; // The timer to wake up the poll() - mlib_timer poll_timer = mlib_expires_after (mlib_duration_max ()); + mlib_timer poll_timer = mlib_expires_never (); /* check if any cmds are ready to be initiated. */ DL_FOREACH_SAFE (async->cmds, acmd, tmp) diff --git a/src/libmongoc/src/mongoc/mongoc-client-pool.c b/src/libmongoc/src/mongoc/mongoc-client-pool.c index 75ff559a68..a0f6dfc12a 100644 --- a/src/libmongoc/src/mongoc/mongoc-client-pool.c +++ b/src/libmongoc/src/mongoc/mongoc-client-pool.c @@ -33,6 +33,10 @@ #include +#include +#include +#include + #ifdef MONGOC_ENABLE_SSL #include #endif @@ -312,18 +316,17 @@ mongoc_client_t * mongoc_client_pool_pop (mongoc_client_pool_t *pool) { mongoc_client_t *client; - int32_t wait_queue_timeout_ms; - int64_t expire_at_ms = -1; - int64_t now_ms; int r; ENTRY; BSON_ASSERT_PARAM (pool); - wait_queue_timeout_ms = mongoc_uri_get_option_as_int32 (pool->uri, MONGOC_URI_WAITQUEUETIMEOUTMS, -1); + mlib_timer expires_at = mlib_expires_never (); + + const int32_t wait_queue_timeout_ms = mongoc_uri_get_option_as_int32 (pool->uri, MONGOC_URI_WAITQUEUETIMEOUTMS, -1); if (wait_queue_timeout_ms > 0) { - expire_at_ms = (bson_get_monotonic_time () / 1000) + wait_queue_timeout_ms; + expires_at = mlib_expires_after (wait_queue_timeout_ms, ms); } bson_mutex_lock (&pool->mutex); @@ -336,9 +339,9 @@ mongoc_client_pool_pop (mongoc_client_pool_t *pool) pool->size++; } else { if (wait_queue_timeout_ms > 0) { - now_ms = bson_get_monotonic_time () / 1000; - if (now_ms < expire_at_ms) { - r = mongoc_cond_timedwait (&pool->cond, &pool->mutex, expire_at_ms - now_ms); + if (!mlib_timer_is_expired (expires_at)) { + const mlib_duration remain = mlib_timer_remaining (expires_at); + r = mongoc_cond_timedwait (&pool->cond, &pool->mutex, mlib_milliseconds_count (remain)); if (mongo_cond_ret_is_timedout (r)) { GOTO (done); } From 0e4718a852e498820f73b27178a2e87b515ed3b5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 6 Aug 2025 12:04:10 -0600 Subject: [PATCH 50/66] Remove inclusion --- src/common/src/mlib/platform.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/common/src/mlib/platform.h b/src/common/src/mlib/platform.h index da7b7d653d..36f502608e 100644 --- a/src/common/src/mlib/platform.h +++ b/src/common/src/mlib/platform.h @@ -49,13 +49,6 @@ #include #endif -// Feature detection -#ifdef __has_include - #if __has_include() - #include - #endif -#endif - // clang-format on #endif // MLIB_PLATFORM_H_INCLUDED From a7336f427a0dd04cb12e2c3a90a376fbc1fa86c3 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 7 Aug 2025 12:35:46 -0600 Subject: [PATCH 51/66] Make cancellation requests conditional on non-error states --- src/libmongoc/src/mongoc/mongoc-async-cmd-private.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index 71f8f4787d..dc51a863df 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -268,8 +268,11 @@ _acmd_cancel (mongoc_async_cmd_t *self) { BSON_ASSERT_PARAM (self); - // XXX: Should this check if ther command has already finished/failed? - self->state = MONGOC_ASYNC_CMD_CANCELLED_STATE; + // Don't attempt to cancel a comman in the error state, as it will already have + // a waiting completion. + if (self->state != MONGOC_ASYNC_CMD_ERROR_STATE) { + self->state = MONGOC_ASYNC_CMD_CANCELLED_STATE; + } } /** From 190086a70117b3895800e68e307ec13bf3c23dcd Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 8 Aug 2025 14:33:57 -0600 Subject: [PATCH 52/66] Minor spelling and name changes --- src/common/src/mlib/time_point.h | 27 +++++++++---------- src/common/src/mlib/timer.h | 4 +-- .../src/mongoc/mongoc-async-cmd-private.h | 2 +- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index bcb3ccbdbf..f2e6b472f5 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -101,7 +101,7 @@ mlib_latest (mlib_time_point l, mlib_time_point r) * the time. This value only has meaning on POSIX systems. On Win32, returns * INT_MIN. * - * @return int The integer clock ID, corresponding to a value of `CLOCK_...` + * @return int The integer clock ID, corresponding to the value of a `CLOCK_...` * object macro. */ static inline int @@ -127,7 +127,6 @@ mlib_now (void) mlib_noexcept #if mlib_have_posix_clocks() // Use POSIX clock_gettime struct timespec ts; - // Use the POSIX monotonic clock int rc = clock_gettime (mlib_now_clockid (), &ts); // The above call must never fail: mlib_check (rc, eq, 0); @@ -135,7 +134,7 @@ mlib_now (void) mlib_noexcept mlib_time_point ret; ret.time_since_monotonic_start = mlib_duration_from_timespec (ts); return ret; -#elif defined(_WIN32) +#elif mlib_is_win32() // Win32 APIs for the high-performance monotonic counter. These APIs never fail after Windows XP LARGE_INTEGER freq; QueryPerformanceFrequency (&freq); @@ -150,12 +149,10 @@ mlib_now (void) mlib_noexcept const int64_t one_million = 1000000; // Number of whole seconds that have elapsed: const int64_t whole_seconds = ticks / ticks_per_second; - // Times one million: - const int64_t whole_seconds_1m = whole_seconds * one_million; // Number of microseconds beyond the last whole second: const int64_t subsecond_us = ((ticks % ticks_per_second) * one_million) / ticks_per_second; mlib_time_point ret; - ret.time_since_monotonic_start = mlib_duration ((whole_seconds_1m, us), plus, (subsecond_us, us)); + ret.time_since_monotonic_start = mlib_duration ((whole_seconds, s), plus, (subsecond_us, us)); return ret; #else #error We do not know how to get the current time on this platform @@ -185,22 +182,22 @@ mlib_time_add (mlib_time_point from, mlib_duration delta) mlib_noexcept /** * @brief Obtain the duration between two points in time. * - * @param then The target time - * @param from The base time + * @param stop The target time + * @param start The base time * @return mlib_duration The amount of time you would need to wait starting - * at 'from' for the time to become 'then' (the result may be a negative + * at 'start' for the time to become 'stop' (the result may be a negative * duration). * - * Intuition: If "then" is "in the future" relative to "from", you will + * Intuition: If "stop" is "in the future" relative to "start", you will * receive a positive duration, indicating an amount of time to wait - * beginning at 'from' to reach 'then'. If "then" is actually *before* - * "from", you will receive a paradoxical *negative* duration, indicating - * the amount of time needed to time-travel backwards to reach "then." + * beginning at 'start' to reach 'stop'. If "stop" is actually *before* + * "start", you will receive a paradoxical *negative* duration, indicating + * the amount of time needed to time-travel backwards to reach "stop." */ static inline mlib_duration -mlib_time_difference (mlib_time_point then, mlib_time_point from) +mlib_time_difference (mlib_time_point stop, mlib_time_point start) { - return mlib_duration (then.time_since_monotonic_start, minus, from.time_since_monotonic_start); + return mlib_duration (stop.time_since_monotonic_start, minus, start.time_since_monotonic_start); } /** diff --git a/src/common/src/mlib/timer.h b/src/common/src/mlib/timer.h index febbd37ae4..03cc754978 100644 --- a/src/common/src/mlib/timer.h +++ b/src/common/src/mlib/timer.h @@ -3,8 +3,8 @@ * @brief Timer types and functions * @date 2025-04-18 * - * This file contains APIs for creating fixed-deadline timer object that represent - * a stable expiration point. + * This file contains APIs for creating fixed-deadline timer objects that represent + * stable expiration points. * * @copyright Copyright 2009-present MongoDB, Inc. * diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index dc51a863df..27793fa367 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -39,7 +39,7 @@ BSON_BEGIN_DECLS typedef enum { // The command has no stream and needs to connect to a peer MONGOC_ASYNC_CMD_PENDING_CONNECT, - // The command has connected and has a stream, but needs to run stream setup + // The command has connected and has a stream, but needs to run stream setup (e.g. TLS handshake) MONGOC_ASYNC_CMD_STREAM_SETUP, // The command has data to send to the peer MONGOC_ASYNC_CMD_SEND, From 43e349d76449ee1da29887e1932f0b274b74ae50 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 8 Aug 2025 14:34:18 -0600 Subject: [PATCH 53/66] Change topology scanner to use typed times --- .../src/mongoc/mongoc-topology-scanner.c | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c index efdeb2d59d..79887b2ea4 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c @@ -28,6 +28,7 @@ #include #include +#include #ifdef MONGOC_ENABLE_SSL #include @@ -65,17 +66,17 @@ #define MONGOC_LOG_DOMAIN "topology_scanner" #define DNS_CACHE_TIMEOUT_MS 10 * 60 * 1000 -#define HAPPY_EYEBALLS_DELAY_MS 250 +#define HAPPY_EYEBALLS_DELAY mlib_duration (250, ms) /* forward declarations */ static void _async_connected (mongoc_async_cmd_t *acmd); static void -_async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, int64_t duration_usec); +_async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, mlib_duration elapsed); static void -_async_error_or_timeout (mongoc_async_cmd_t *acmd, int64_t duration_usec, const char *default_err_msg); +_async_error_or_timeout (mongoc_async_cmd_t *acmd, mlib_duration elapsed, const char *default_err_msg); static void _async_handler (mongoc_async_cmd_t *acmd, @@ -91,13 +92,13 @@ static void _mongoc_topology_scanner_monitor_heartbeat_succeeded (const mongoc_topology_scanner_t *ts, const mongoc_host_list_t *host, const bson_t *reply, - int64_t duration_usec); + mlib_duration elapsed); static void _mongoc_topology_scanner_monitor_heartbeat_failed (const mongoc_topology_scanner_t *ts, const mongoc_host_list_t *host, const bson_error_t *error, - int64_t duration_usec); + mlib_duration elapsed); /* reset "retired" nodes that failed or were removed in the previous scan */ @@ -141,7 +142,7 @@ _jumpstart_other_acmds (mongoc_async_cmd_t const *const self) // Only consider commands on the same node if (_is_sibling_command (self, other)) { // Decrease the delay by the happy eyeballs duration. - _acmd_adjust_connect_delay (other, mlib_duration (-HAPPY_EYEBALLS_DELAY_MS, ms)); + _acmd_adjust_connect_delay (other, mlib_duration ((0, us), minus, HAPPY_EYEBALLS_DELAY)); } } } @@ -391,7 +392,7 @@ _begin_hello_cmd (mongoc_topology_scanner_node_t *node, mongoc_stream_t *stream, bool is_setup_done, struct addrinfo *dns_result, - int64_t initiate_delay_ms, + mlib_duration initiate_delay, bool use_handshake) { mongoc_topology_scanner_t *ts = node->ts; @@ -429,7 +430,7 @@ _begin_hello_cmd (mongoc_topology_scanner_node_t *node, is_setup_done, dns_result, _mongoc_topology_scanner_tcp_initiate, - mlib_duration (initiate_delay_ms, ms), + initiate_delay, ts->setup, node->host.host, "admin", @@ -690,7 +691,7 @@ _async_connected (mongoc_async_cmd_t *acmd) } static void -_async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, int64_t duration_usec) +_async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, mlib_duration elapsed) { mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); mongoc_stream_t *const stream = acmd->stream; @@ -706,7 +707,7 @@ _async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, int64_t node->last_used = bson_get_monotonic_time (); node->last_failed = -1; - _mongoc_topology_scanner_monitor_heartbeat_succeeded (ts, &node->host, hello_response, duration_usec); + _mongoc_topology_scanner_monitor_heartbeat_succeeded (ts, &node->host, hello_response, elapsed); /* set our successful stream. */ BSON_ASSERT (!node->stream); @@ -717,7 +718,7 @@ _async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, int64_t /* Store a server description associated with the handshake. */ mongoc_server_description_init (&sd, node->host.host_and_port, node->id); - mongoc_server_description_handle_hello (&sd, hello_response, duration_usec / 1000, &acmd->error); + mongoc_server_description_handle_hello (&sd, hello_response, mlib_milliseconds_count (elapsed), &acmd->error); node->handshake_sd = mongoc_server_description_new_copy (&sd); mongoc_server_description_cleanup (&sd); } @@ -731,11 +732,11 @@ _async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, int64_t } /* mongoc_topology_scanner_cb_t takes rtt_msec, not usec */ - ts->cb (node->id, hello_response, duration_usec / 1000, ts->cb_data, &acmd->error); + ts->cb (node->id, hello_response, mlib_milliseconds_count (elapsed), ts->cb_data, &acmd->error); } static void -_async_error_or_timeout (mongoc_async_cmd_t *acmd, int64_t duration_usec, const char *default_err_msg) +_async_error_or_timeout (mongoc_async_cmd_t *acmd, mlib_duration elapsed, const char *default_err_msg) { mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); mongoc_stream_t *stream = acmd->stream; @@ -778,11 +779,11 @@ _async_error_or_timeout (mongoc_async_cmd_t *acmd, int64_t duration_usec, const message, node->host.host_and_port); - _mongoc_topology_scanner_monitor_heartbeat_failed (ts, &node->host, &node->last_error, duration_usec); + _mongoc_topology_scanner_monitor_heartbeat_failed (ts, &node->host, &node->last_error, elapsed); /* call the topology scanner callback. cannot connect to this node. * callback takes rtt_msec, not usec. */ - ts->cb (node->id, NULL, duration_usec / 1000, ts->cb_data, error); + ts->cb (node->id, NULL, mlib_milliseconds_count (elapsed), ts->cb_data, error); mongoc_server_description_destroy (node->handshake_sd); node->handshake_sd = NULL; @@ -808,19 +809,18 @@ _async_handler (mongoc_async_cmd_t *acmd, const bson_t *hello_response, mlib_duration duration) { - const int64_t duration_usec = mlib_microseconds_count (duration); switch (async_status) { case MONGOC_ASYNC_CMD_CONNECTED: _async_connected (acmd); return; case MONGOC_ASYNC_CMD_SUCCESS: - _async_success (acmd, hello_response, duration_usec); + _async_success (acmd, hello_response, duration); return; case MONGOC_ASYNC_CMD_TIMEOUT: - _async_error_or_timeout (acmd, duration_usec, "connection timeout"); + _async_error_or_timeout (acmd, duration, "connection timeout"); return; case MONGOC_ASYNC_CMD_ERROR: - _async_error_or_timeout (acmd, duration_usec, "connection error"); + _async_error_or_timeout (acmd, duration, "connection error"); return; case MONGOC_ASYNC_CMD_IN_PROGRESS: default: @@ -900,7 +900,7 @@ mongoc_topology_scanner_node_setup_tcp (mongoc_topology_scanner_node_t *node, bs char portstr[8]; mongoc_host_list_t *host; int s; - int64_t delay = 0; + mlib_duration delay = mlib_duration (); int64_t now = bson_get_monotonic_time (); ENTRY; @@ -943,14 +943,14 @@ mongoc_topology_scanner_node_setup_tcp (mongoc_topology_scanner_node_t *node, bs NULL /* stream */, false /* is_setup_done */, node->successful_dns_result, - 0 /* initiate_delay_ms */, + mlib_duration () /* initiate_delay */, true /* use_handshake */); } else { LL_FOREACH2 (node->dns_results, iter, ai_next) { _begin_hello_cmd (node, NULL /* stream */, false /* is_setup_done */, iter, delay, true /* use_handshake */); /* each subsequent DNS result will have an additional 250ms delay. */ - delay += HAPPY_EYEBALLS_DELAY_MS; + delay = mlib_duration (delay, plus, HAPPY_EYEBALLS_DELAY); } } @@ -1011,8 +1011,12 @@ mongoc_topology_scanner_node_connect_unix (mongoc_topology_scanner_node_t *node, stream = _mongoc_topology_scanner_node_setup_stream_for_tls (node, mongoc_stream_socket_new (sock)); if (stream) { - _begin_hello_cmd ( - node, stream, false /* is_setup_done */, NULL /* dns result */, 0 /* delay */, true /* use_handshake */); + _begin_hello_cmd (node, + stream, + false /* is_setup_done */, + NULL /* dns result */, + mlib_duration () /* no delay */, + true /* use_handshake */); RETURN (true); } _mongoc_set_error (error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, "Failed to create TLS stream"); @@ -1039,10 +1043,9 @@ mongoc_topology_scanner_node_setup (mongoc_topology_scanner_node_t *node, bson_e { bool success = false; mongoc_stream_t *stream; - int64_t start; _mongoc_topology_scanner_monitor_heartbeat_started (node->ts, &node->host); - start = bson_get_monotonic_time (); + const mlib_time_point start_time = mlib_now (); /* if there is already a working stream, push it back to be re-scanned. */ if (node->stream) { @@ -1050,7 +1053,7 @@ mongoc_topology_scanner_node_setup (mongoc_topology_scanner_node_t *node, bson_e node->stream, true /* is_setup_done */, NULL /* dns_result */, - 0 /* initiate_delay_ms */, + mlib_duration () /* initiate_delay */, false /* use_handshake */); node->stream = NULL; return; @@ -1079,7 +1082,7 @@ mongoc_topology_scanner_node_setup (mongoc_topology_scanner_node_t *node, bson_e stream, false /* is_setup_done */, NULL /* dns_result */, - 0 /* initiate_delay_ms */, + mlib_duration () /* initiate_delay */, true /* use_handshake */); } } else { @@ -1091,8 +1094,7 @@ mongoc_topology_scanner_node_setup (mongoc_topology_scanner_node_t *node, bson_e } if (!success) { - _mongoc_topology_scanner_monitor_heartbeat_failed ( - node->ts, &node->host, error, (bson_get_monotonic_time () - start) / 1000); + _mongoc_topology_scanner_monitor_heartbeat_failed (node->ts, &node->host, error, mlib_elapsed_since (start_time)); node->ts->setup_err_cb (node->id, node->ts->cb_data, error); return; @@ -1347,7 +1349,7 @@ static void _mongoc_topology_scanner_monitor_heartbeat_succeeded (const mongoc_topology_scanner_t *ts, const mongoc_host_list_t *host, const bson_t *reply, - int64_t duration_usec) + mlib_duration elapsed) { /* This redaction is more lenient than the general command redaction in the Command Logging and Monitoring spec and * the cmd*() structured log items. In those general command logs, sensitive replies are omitted entirely. In this @@ -1367,7 +1369,7 @@ _mongoc_topology_scanner_monitor_heartbeat_succeeded (const mongoc_topology_scan utf8 ("serverHost", host->host), int32 ("serverPort", host->port), boolean ("awaited", false), - monotonic_time_duration (duration_usec), + monotonic_time_duration (mlib_microseconds_count (elapsed)), bson_as_json ("reply", &hello_redacted)); if (ts->log_and_monitor->apm_callbacks.server_heartbeat_succeeded) { @@ -1375,7 +1377,7 @@ _mongoc_topology_scanner_monitor_heartbeat_succeeded (const mongoc_topology_scan event.host = host; event.context = ts->log_and_monitor->apm_context; event.reply = reply; - event.duration_usec = duration_usec; + event.duration_usec = mlib_microseconds_count (elapsed); event.awaited = false; ts->log_and_monitor->apm_callbacks.server_heartbeat_succeeded (&event); } @@ -1388,7 +1390,7 @@ static void _mongoc_topology_scanner_monitor_heartbeat_failed (const mongoc_topology_scanner_t *ts, const mongoc_host_list_t *host, const bson_error_t *error, - int64_t duration_usec) + mlib_duration elapsed) { mongoc_structured_log (ts->log_and_monitor->structured_log, MONGOC_STRUCTURED_LOG_LEVEL_DEBUG, @@ -1398,7 +1400,7 @@ _mongoc_topology_scanner_monitor_heartbeat_failed (const mongoc_topology_scanner utf8 ("serverHost", host->host), int32 ("serverPort", host->port), boolean ("awaited", false), - monotonic_time_duration (duration_usec), + monotonic_time_duration (mlib_microseconds_count (elapsed)), error ("failure", error)); if (ts->log_and_monitor->apm_callbacks.server_heartbeat_failed) { @@ -1406,7 +1408,7 @@ _mongoc_topology_scanner_monitor_heartbeat_failed (const mongoc_topology_scanner event.host = host; event.context = ts->log_and_monitor->apm_context; event.error = error; - event.duration_usec = duration_usec; + event.duration_usec = mlib_microseconds_count (elapsed); event.awaited = false; ts->log_and_monitor->apm_callbacks.server_heartbeat_failed (&event); } From 856e088489aa7d7a21ad3aee5372226891205dcd Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 09:18:54 -0600 Subject: [PATCH 54/66] Revert changes for new preproc config macros --- src/common/src/mlib/time_point.h | 5 +---- src/libmongoc/src/mongoc/mongoc-async-cmd.c | 10 +++++++++- src/libmongoc/src/mongoc/mongoc-config.h.in | 6 ++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index f2e6b472f5..04ff73d541 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -30,16 +30,13 @@ #include // Check for POSIX clock functions functions +#define mlib_have_posix_clocks() 0 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_DEFAULT_SOURCE) && !defined(_WIN32)) #include #undef mlib_have_posix_clocks #define mlib_have_posix_clocks() 1 #endif -#ifndef mlib_have_posix_clocks -#define mlib_have_posix_clocks() 0 -#endif - #include #include #include diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index eaeffe080a..64a9878897 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -74,9 +74,17 @@ mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, mli tls_stream = mongoc_stream_get_base_stream (tls_stream)) { } + const bool use_non_blocking = +#if defined(MONGOC_ENABLE_SSL_OPENSSL) || defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) + true +#else + false +#endif + ; + // Try to do a non-blocking operation, if our backend allows it const mlib_duration_rep_t remain_ms = // - (MONGOC_SECURE_CHANNEL_ENABLED () || MONGOC_OPENSSL_ENABLED ()) + use_non_blocking // Pass 0 for the timeout to begin / continue a non-blocking handshake ? 0 // Otherwise, use the deadline diff --git a/src/libmongoc/src/mongoc/mongoc-config.h.in b/src/libmongoc/src/mongoc/mongoc-config.h.in index fffb392f6f..a250d12968 100644 --- a/src/libmongoc/src/mongoc/mongoc-config.h.in +++ b/src/libmongoc/src/mongoc/mongoc-config.h.in @@ -48,8 +48,7 @@ * compiled with Native SSL support on Windows */ #define MONGOC_ENABLE_SSL_SECURE_CHANNEL @MONGOC_ENABLE_SSL_SECURE_CHANNEL@ -#define MONGOC_SECURE_CHANNEL_ENABLED() @MONGOC_ENABLE_SSL_SECURE_CHANNEL@ -#if MONGOC_SECURE_CHANNEL_ENABLED() != 1 +#if MONGOC_ENABLE_SSL_SECURE_CHANNEL != 1 # undef MONGOC_ENABLE_SSL_SECURE_CHANNEL #endif @@ -101,8 +100,7 @@ * compiled with OpenSSL support. */ #define MONGOC_ENABLE_SSL_OPENSSL @MONGOC_ENABLE_SSL_OPENSSL@ -#define MONGOC_OPENSSL_ENABLED() @MONGOC_ENABLE_SSL_OPENSSL@ -#if MONGOC_OPENSSL_ENABLED() != 1 +#if MONGOC_ENABLE_SSL_OPENSSL != 1 # undef MONGOC_ENABLE_SSL_OPENSSL #endif From d405460bbf64f0059d43ef61334828b2958902e0 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 09:21:01 -0600 Subject: [PATCH 55/66] Several more non-null checks --- src/libmongoc/src/mongoc/mongoc-async-cmd-private.h | 3 +++ src/libmongoc/src/mongoc/mongoc-topology-scanner.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index 27793fa367..95a87741ee 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -16,6 +16,8 @@ #include +#include + #ifndef MONGOC_ASYNC_CMD_PRIVATE_H #define MONGOC_ASYNC_CMD_PRIVATE_H @@ -287,6 +289,7 @@ _acmd_cancel (mongoc_async_cmd_t *self) static inline void _acmd_adjust_connect_delay (mongoc_async_cmd_t *self, const mlib_duration d) { + BSON_ASSERT_PARAM (self); self->_connect_delay_timer.expires_at = mlib_time_add (self->_connect_delay_timer.expires_at, d); } diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c index 79887b2ea4..95df3df1b5 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c @@ -109,6 +109,7 @@ _delete_retired_nodes (mongoc_topology_scanner_t *ts); static mongoc_topology_scanner_node_t * _scanner_node_of (mongoc_async_cmd_t const *a) { + BSON_ASSERT_PARAM (a); return _acmd_userdata (mongoc_topology_scanner_node_t, a); } @@ -117,6 +118,8 @@ _scanner_node_of (mongoc_async_cmd_t const *a) static bool _is_sibling_command (mongoc_async_cmd_t const *l, mongoc_async_cmd_t const *r) { + BSON_ASSERT_PARAM (l); + BSON_ASSERT_PARAM (r); return l != r && _scanner_node_of (l) == _scanner_node_of (r); } @@ -136,6 +139,7 @@ _count_acmds (mongoc_topology_scanner_node_t *node); static void _jumpstart_other_acmds (mongoc_async_cmd_t const *const self) { + BSON_ASSERT_PARAM (self); mongoc_async_cmd_t *other; DL_FOREACH (self->async->cmds, other) { @@ -684,6 +688,7 @@ mongoc_topology_scanner_has_node_for_host (mongoc_topology_scanner_t *ts, mongoc static void _async_connected (mongoc_async_cmd_t *acmd) { + BSON_ASSERT_PARAM (acmd); mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); /* this cmd connected successfully, cancel other cmds on this node. */ _cancel_commands_excluding (node, acmd); @@ -693,6 +698,8 @@ _async_connected (mongoc_async_cmd_t *acmd) static void _async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, mlib_duration elapsed) { + BSON_ASSERT_PARAM (acmd); + BSON_ASSERT_PARAM (hello_response); mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); mongoc_stream_t *const stream = acmd->stream; mongoc_topology_scanner_t *ts = node->ts; @@ -738,6 +745,7 @@ _async_success (mongoc_async_cmd_t *acmd, const bson_t *hello_response, mlib_dur static void _async_error_or_timeout (mongoc_async_cmd_t *acmd, mlib_duration elapsed, const char *default_err_msg) { + BSON_ASSERT_PARAM (acmd); mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); mongoc_stream_t *stream = acmd->stream; mongoc_topology_scanner_t *ts = node->ts; @@ -865,6 +873,7 @@ _mongoc_topology_scanner_node_setup_stream_for_tls (mongoc_topology_scanner_node mongoc_stream_t * _mongoc_topology_scanner_tcp_initiate (mongoc_async_cmd_t *acmd) { + BSON_ASSERT_PARAM (acmd); mongoc_topology_scanner_node_t *const node = _scanner_node_of (acmd); struct addrinfo *res = acmd->dns_result; mongoc_socket_t *sock = NULL; From 590d51e9d36c4e3254d9a418310dc9959a15bf4f Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 09:26:34 -0600 Subject: [PATCH 56/66] MInor update to use a timer --- src/libmongoc/tests/test-mongoc-client.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/libmongoc/tests/test-mongoc-client.c b/src/libmongoc/tests/test-mongoc-client.c index d8cc10fa93..2b60d90b13 100644 --- a/src/libmongoc/tests/test-mongoc-client.c +++ b/src/libmongoc/tests/test-mongoc-client.c @@ -10,6 +10,7 @@ #include #include +#include #include #ifdef MONGOC_ENABLE_SSL @@ -2196,35 +2197,29 @@ test_mongoc_client_descriptions_single (void) static void test_mongoc_client_descriptions_pooled (void *unused) { - mongoc_client_t *client; - mongoc_client_pool_t *pool; - mongoc_server_description_t **sds; - size_t n, expected_n; - int64_t start; - BSON_UNUSED (unused); - expected_n = test_framework_server_count (); - n = 0; + const size_t expected_n = test_framework_server_count (); + size_t n = 0; /* * pooled */ - pool = test_framework_new_default_client_pool (); - client = mongoc_client_pool_pop (pool); + mongoc_client_pool_t *const pool = test_framework_new_default_client_pool (); + mongoc_client_t *const client = mongoc_client_pool_pop (pool); /* wait for background thread to discover all members */ - start = bson_get_monotonic_time (); + const mlib_timer deadline = mlib_expires_after (3, s); do { mlib_sleep_for (1, ms); /* Windows IPv4 tasks may take longer to connect since connection to the * first address returned by getaddrinfo may be IPv6, and failure to * connect may take a couple seconds. See CDRIVER-3639. */ - if (bson_get_monotonic_time () - start > 3 * 1000 * 1000) { + if (mlib_timer_is_expired (deadline)) { test_error ("still have %d descriptions, not expected %d, after 1 sec", (int) n, (int) expected_n); } - sds = mongoc_client_get_server_descriptions (client, &n); + mongoc_server_description_t **const sds = mongoc_client_get_server_descriptions (client, &n); mongoc_server_descriptions_destroy_all (sds, n); } while (n != expected_n); From 9b567208ca8a76add0024c30018f98fe2a7485e2 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 09:35:58 -0600 Subject: [PATCH 57/66] Header cleanup in topology-scanner.c --- .../src/mongoc/mongoc-topology-scanner.c | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c index 95df3df1b5..238aaff430 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c @@ -14,22 +14,36 @@ * limitations under the License. */ +#include +#include #include +#include +#include +#include #include #include +#include #include +#include +#include #include #include +#include +#include #include #include #include +#include #include +#include #include #include +#include + #ifdef MONGOC_ENABLE_SSL #include #endif @@ -45,23 +59,6 @@ #include #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - #undef MONGOC_LOG_DOMAIN #define MONGOC_LOG_DOMAIN "topology_scanner" @@ -146,7 +143,7 @@ _jumpstart_other_acmds (mongoc_async_cmd_t const *const self) // Only consider commands on the same node if (_is_sibling_command (self, other)) { // Decrease the delay by the happy eyeballs duration. - _acmd_adjust_connect_delay (other, mlib_duration ((0, us), minus, HAPPY_EYEBALLS_DELAY)); + _acmd_adjust_connect_delay (other, mlib_duration (HAPPY_EYEBALLS_DELAY, mul, -1)); } } } From 170b14f6f53b728c2dbc23a766b754048e63bf2d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 10:45:36 -0600 Subject: [PATCH 58/66] Fix header position --- src/libmongoc/src/mongoc/mongoc-async-cmd-private.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index 95a87741ee..28f189d47c 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -16,8 +16,6 @@ #include -#include - #ifndef MONGOC_ASYNC_CMD_PRIVATE_H #define MONGOC_ASYNC_CMD_PRIVATE_H @@ -31,6 +29,7 @@ #include #include +#include #include #include From f9c872e57b3a2ceb061ca8af803aacdafe3a3ddb Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 13:26:20 -0600 Subject: [PATCH 59/66] Workaround broken MSVC preproc --- src/common/src/mlib/config.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index f34266445d..30ecbefeef 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -172,8 +172,11 @@ /** * @brief Expand to a call expression `Prefix##_argc_N(...)`, where `N` is the * number of macro arguments. + * + * XXX: The `MLIB_JUST` forces an additional expansion pass that works around a + * bug in the old MSVC preprocessor, but is not required in a conforming preprocessor. */ -#define MLIB_ARGC_PICK(Prefix, ...) MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__) +#define MLIB_ARGC_PICK(Prefix, ...) MLIB_JUST (MLIB_ARGC_PASTE (Prefix, __VA_ARGS__) (__VA_ARGS__)) #define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3 (Prefix, _argc_, MLIB_ARG_COUNT (__VA_ARGS__)) #ifdef __cplusplus From e4b3ee6fb5865b7f22e6871c55fcbb66d573868e Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 13:26:35 -0600 Subject: [PATCH 60/66] Guard against an existing definition --- src/common/src/mlib/time_point.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/src/mlib/time_point.h b/src/common/src/mlib/time_point.h index 04ff73d541..e683eb0426 100644 --- a/src/common/src/mlib/time_point.h +++ b/src/common/src/mlib/time_point.h @@ -30,6 +30,7 @@ #include // Check for POSIX clock functions functions +#undef mlib_have_posix_clocks #define mlib_have_posix_clocks() 0 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_DEFAULT_SOURCE) && !defined(_WIN32)) #include From bf410f5da107b22bac6d0f6b15ce09ed3ff7068a Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 13:29:36 -0600 Subject: [PATCH 61/66] Address implicit narrowing --- src/libmongoc/src/mongoc/mongoc-async-cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index 64a9878897..3970461749 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -89,7 +89,7 @@ mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, mli ? 0 // Otherwise, use the deadline : mlib_milliseconds_count (mlib_timer_remaining (deadline)); - if (mongoc_stream_tls_handshake (tls_stream, host, remain_ms, &retry_events, error)) { + if (mongoc_stream_tls_handshake (tls_stream, host, mlib_assert_narrow (int32_t, remain_ms), &retry_events, error)) { return 1; } From 794a84ebd57727bacdafac9ec7bf7caadcd4b8da Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 13:57:00 -0600 Subject: [PATCH 62/66] Address conversion in division warning --- src/common/src/mlib/duration.h | 2 +- src/libmongoc/src/mongoc/mongoc-async-cmd-private.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index 6fb1e01b54..f3c5214b49 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -197,7 +197,7 @@ _mlibDurationDivide (mlib_duration a, mlib_upsized_integer div) mlib_noexcept if (div.is_signed) { a._rep /= div.bits.as_signed; } else { - a._rep /= div.bits.as_unsigned; + a._rep = (mlib_duration_rep_t) ((uintmax_t) a._rep / div.bits.as_unsigned); } } return a; diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h index 28f189d47c..19823310a1 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd-private.h @@ -269,8 +269,8 @@ _acmd_cancel (mongoc_async_cmd_t *self) { BSON_ASSERT_PARAM (self); - // Don't attempt to cancel a comman in the error state, as it will already have - // a waiting completion. + // Don't attempt to cancel a command in the error state, as it already has a + // completion state waiting to be delivered. if (self->state != MONGOC_ASYNC_CMD_ERROR_STATE) { self->state = MONGOC_ASYNC_CMD_CANCELLED_STATE; } From 20e1a6b259aa4cbe7834c9e061668275c3e4e542 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 13:59:08 -0600 Subject: [PATCH 63/66] Minor formatting --- src/libmongoc/src/mongoc/mongoc-async-cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-async-cmd.c b/src/libmongoc/src/mongoc/mongoc-async-cmd.c index 3970461749..3ebaa165a3 100644 --- a/src/libmongoc/src/mongoc/mongoc-async-cmd.c +++ b/src/libmongoc/src/mongoc/mongoc-async-cmd.c @@ -89,7 +89,7 @@ mongoc_async_cmd_tls_setup (mongoc_stream_t *stream, int *events, void *ctx, mli ? 0 // Otherwise, use the deadline : mlib_milliseconds_count (mlib_timer_remaining (deadline)); - if (mongoc_stream_tls_handshake (tls_stream, host, mlib_assert_narrow (int32_t, remain_ms), &retry_events, error)) { + if (mongoc_stream_tls_handshake (tls_stream, host, mlib_assert_narrow (int32_t, remain_ms), &retry_events, error)) { return 1; } From 93416b4188645dd138406be41530e28f38797639 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 22:00:59 -0600 Subject: [PATCH 64/66] Simpler multiply overflow handling --- src/common/src/mlib/duration.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/common/src/mlib/duration.h b/src/common/src/mlib/duration.h index f3c5214b49..07affe893b 100644 --- a/src/common/src/mlib/duration.h +++ b/src/common/src/mlib/duration.h @@ -166,8 +166,9 @@ static inline mlib_duration _mlibDurationMultiply (const mlib_duration dur, mlib_upsized_integer fac) mlib_noexcept { mlib_duration ret = {0}; - uintmax_t bits; - if ((mlib_mul) (&bits, true, true, (uintmax_t) dur._rep, fac.is_signed, fac.bits.as_unsigned)) { + const bool overflowed = fac.is_signed ? mlib_mul (&ret._rep, dur._rep, fac.bits.as_signed) + : mlib_mul (&ret._rep, dur._rep, fac.bits.as_unsigned); + if (overflowed) { if ((dur._rep < 0) != (fac.is_signed && fac.bits.as_signed < 0)) { // Different signs: Neg × Pos = Neg ret = mlib_duration_min (); @@ -176,8 +177,6 @@ _mlibDurationMultiply (const mlib_duration dur, mlib_upsized_integer fac) mlib_n // Neg × Neg = Pos ret = mlib_duration_max (); } - } else { - ret._rep = (intmax_t) bits; } return ret; } From e0ce0c9eb1b05b75c103cd7f44eed9694dfd043a Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 22:11:02 -0600 Subject: [PATCH 65/66] Add NOMINMAX globally to all sources --- CMakeLists.txt | 4 ++++ src/common/src/mlib/platform.h | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58a6737495..351dbd714a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -383,6 +383,10 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin") list (APPEND CMAKE_REQUIRED_DEFINITIONS -D_DARWIN_C_SOURCE) endif () +# The windows.h `min` and `max` macros should never be defined. Suppress them +# globally rather than trying to do it in-source, which is error-prone. +add_compile_definitions ($<$:NOMINMAX>) + # Convenience targets to build all tests or all examples. add_custom_target(mongo_c_driver_tests) add_custom_target(mongo_c_driver_examples) diff --git a/src/common/src/mlib/platform.h b/src/common/src/mlib/platform.h index 36f502608e..bdc8bb3832 100644 --- a/src/common/src/mlib/platform.h +++ b/src/common/src/mlib/platform.h @@ -36,7 +36,6 @@ // Request a new-enough version of the Win32 API (required for MinGW) #define _WIN32_WINNT 0x601 #endif - #define NOMINMAX // Winsock must be included before windows.h #include #include From cf2ddaf7dcf3e5cb10574119b75e73fc488e959d Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 14 Aug 2025 22:38:17 -0600 Subject: [PATCH 66/66] Undo NOMINMAX, just disable it locally, since NOMINMAX doesn't work on C --- CMakeLists.txt | 4 ---- src/common/tests/test-mlib.c | 2 ++ src/libmongoc/tests/test-mongoc-sdam.c | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 351dbd714a..58a6737495 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -383,10 +383,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin") list (APPEND CMAKE_REQUIRED_DEFINITIONS -D_DARWIN_C_SOURCE) endif () -# The windows.h `min` and `max` macros should never be defined. Suppress them -# globally rather than trying to do it in-source, which is error-prone. -add_compile_definitions ($<$:NOMINMAX>) - # Convenience targets to build all tests or all examples. add_custom_target(mongo_c_driver_tests) add_custom_target(mongo_c_driver_examples) diff --git a/src/common/tests/test-mlib.c b/src/common/tests/test-mlib.c index 86bb9f57c3..8e02133a67 100644 --- a/src/common/tests/test-mlib.c +++ b/src/common/tests/test-mlib.c @@ -14,6 +14,8 @@ #include +#undef min // Used as a time unit suffix + mlib_diagnostic_push (); // We don't set any diagnostics, we just want to make sure it compiles // Not relevant, we just want to test that it compiles: diff --git a/src/libmongoc/tests/test-mongoc-sdam.c b/src/libmongoc/tests/test-mongoc-sdam.c index ce91a41e20..d4bd99f3c8 100644 --- a/src/libmongoc/tests/test-mongoc-sdam.c +++ b/src/libmongoc/tests/test-mongoc-sdam.c @@ -17,6 +17,7 @@ #include #endif +#undef min // Used as a time unit suffix static void _topology_has_description (const mongoc_topology_description_t *topology, bson_t *server, const char *address)