diff mbox series

[ovs-dev,1/6] timeval: Make timewarp available for internal callers.

Message ID 20240110192939.15220-2-frode.nordahl@canonical.com
State Changes Requested
Delegated to: Ilya Maximets
Headers show
Series Introduce cooperative multitasking to improve OVSDB RAFT cluster operation. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation fail test: fail

Commit Message

Frode Nordahl Jan. 10, 2024, 7:29 p.m. UTC
At present the timeval timewarp functionality is tightly coupled
with the unixctl interface for external operation by test suite.

It is however desirable to make use of the timewarp functionality
directly in unit tests.

Split unixctl callback interface and the actual timewarp
functionality into separate functions.

This will be used in a patch that implements unit tests for the
cooperative multitasking module.

Signed-off-by: Frode Nordahl <frode.nordahl@canonical.com>
---
 lib/timeval.c | 56 ++++++++++++++++++++++++++++++++++++++++-----------
 lib/timeval.h |  4 ++++
 2 files changed, 48 insertions(+), 12 deletions(-)

Comments

Ilya Maximets Jan. 11, 2024, 9:49 p.m. UTC | #1
On 1/10/24 20:29, Frode Nordahl wrote:
> At present the timeval timewarp functionality is tightly coupled
> with the unixctl interface for external operation by test suite.
> 
> It is however desirable to make use of the timewarp functionality
> directly in unit tests.
> 
> Split unixctl callback interface and the actual timewarp
> functionality into separate functions.
> 
> This will be used in a patch that implements unit tests for the
> cooperative multitasking module.
> 
> Signed-off-by: Frode Nordahl <frode.nordahl@canonical.com>
> ---
>  lib/timeval.c | 56 ++++++++++++++++++++++++++++++++++++++++-----------
>  lib/timeval.h |  4 ++++
>  2 files changed, 48 insertions(+), 12 deletions(-)

Hi, Frode.  Thanks for the patch!

I agree that idea is very useful for unit testing.  Though, I'm not
sure we need to replicate the exact unixctl interface internally.

The unixctl interface is designed around a server that calls
poll_block() and we also really want to allow other threads to do
some work within long warps, so we sleep and we have a stepping
interface that adjusts time in small chunks.

I'm not sure if internal interface needs most of that, and I'm also
not sure if it is actually easy to take advantage of such functionality
from within the process.  What I'm trying to say is that the program
that is using this interface is managing the threads, and it likely
knows when exectly it needs the time to be warped without relying on
that to be done by the poll_block().

So, I think, a much simpler API might be better here.  We can have
timeval_stop(void) that stops the timer, i.e. directs it into slow path,
and timeval_warp(long long int) that directly advances the time, i.e.
just grabs the mutex, converts the argument into timeval and calls
timeval_add().

In a worst case, if someone will actually need to advance time
gradually in the code, they can always just call timeval_warp()
before they call poll_block().

The multi-threading support can be simplified down to adding
seq_change(timewarp_seq); into timeval_warp() and adding one more
function timewarp_wait() that waits on it.  It seems reasonable to
expect multi-threaded test applications to call that before
poll_block().  But it is not even necessary to have for tests
introduced in this patch set, so may be delayed until such
fucntionality is actually needed.

What do you think?

Best regards, Ilya Maximets.
Frode Nordahl Jan. 12, 2024, 7:35 a.m. UTC | #2
On Thu, Jan 11, 2024 at 10:49 PM Ilya Maximets <i.maximets@ovn.org> wrote:
>
> On 1/10/24 20:29, Frode Nordahl wrote:
> > At present the timeval timewarp functionality is tightly coupled
> > with the unixctl interface for external operation by test suite.
> >
> > It is however desirable to make use of the timewarp functionality
> > directly in unit tests.
> >
> > Split unixctl callback interface and the actual timewarp
> > functionality into separate functions.
> >
> > This will be used in a patch that implements unit tests for the
> > cooperative multitasking module.
> >
> > Signed-off-by: Frode Nordahl <frode.nordahl@canonical.com>
> > ---
> >  lib/timeval.c | 56 ++++++++++++++++++++++++++++++++++++++++-----------
> >  lib/timeval.h |  4 ++++
> >  2 files changed, 48 insertions(+), 12 deletions(-)
>
> Hi, Frode.  Thanks for the patch!

Thank you for taking the time to review, much appreciated!

> I agree that idea is very useful for unit testing.  Though, I'm not
> sure we need to replicate the exact unixctl interface internally.
>
> The unixctl interface is designed around a server that calls
> poll_block() and we also really want to allow other threads to do
> some work within long warps, so we sleep and we have a stepping
> interface that adjusts time in small chunks.
>
> I'm not sure if internal interface needs most of that, and I'm also
> not sure if it is actually easy to take advantage of such functionality
> from within the process.  What I'm trying to say is that the program
> that is using this interface is managing the threads, and it likely
> knows when exectly it needs the time to be warped without relying on
> that to be done by the poll_block().
>
> So, I think, a much simpler API might be better here.  We can have
> timeval_stop(void) that stops the timer, i.e. directs it into slow path,
> and timeval_warp(long long int) that directly advances the time, i.e.
> just grabs the mutex, converts the argument into timeval and calls
> timeval_add().

I agree, in v1 I was mostly concerned about surfacing the feature for
the test with as little change and as much reuse as possible.

Adding a new function that advances the time directly does not
duplicate too much and allows us to do away with the ugly pointer
hack.

> In a worst case, if someone will actually need to advance time
> gradually in the code, they can always just call timeval_warp()
> before they call poll_block().
>
> The multi-threading support can be simplified down to adding
> seq_change(timewarp_seq); into timeval_warp() and adding one more
> function timewarp_wait() that waits on it.  It seems reasonable to
> expect multi-threaded test applications to call that before
> poll_block().  But it is not even necessary to have for tests
> introduced in this patch set, so may be delayed until such
> fucntionality is actually needed.
>
> What do you think?

No immediate need for multi threaded support for this, so it makes
sense to me to expose a simpler interface. Thanks!
diff mbox series

Patch

diff --git a/lib/timeval.c b/lib/timeval.c
index 193c7bab1..4d4951c42 100644
--- a/lib/timeval.c
+++ b/lib/timeval.c
@@ -533,6 +533,10 @@  nsec_to_timespec(long long int nsec, struct timespec *ts)
     ts->tv_nsec = nsec;
 }
 
+#define MONOTONIC_CLOCK_HAS_WARP_CONN(c) \
+    ( (struct clock *) c)->large_warp.conn && \
+    (uintptr_t) ( (struct clock *) c)->large_warp.conn != UINTPTR_MAX
+
 static void
 timewarp_work(void)
 {
@@ -540,6 +544,8 @@  timewarp_work(void)
     struct timespec warp;
 
     ovs_mutex_lock(&c->mutex);
+    /* note that we do not use MONOTONIC_CLOCK_HAS_WARP_CONN here as we want
+     * to perform work in case of an internal caller has requested warp. */
     if (!c->large_warp.conn) {
         ovs_mutex_unlock(&c->mutex);
         return;
@@ -560,7 +566,9 @@  timewarp_work(void)
     }
 
     if (!c->large_warp.total_warp) {
-        unixctl_command_reply(c->large_warp.conn, "warped");
+        if (MONOTONIC_CLOCK_HAS_WARP_CONN(c)) {
+            unixctl_command_reply(c->large_warp.conn, "warped");
+        }
         c->large_warp.conn = NULL;
     }
 
@@ -763,17 +771,22 @@  get_cpu_usage(void)
 
 /* "time/stop" stops the monotonic time returned by e.g. time_msec() from
  * advancing, except due to later calls to "time/warp". */
-static void
-timeval_stop_cb(struct unixctl_conn *conn,
-                 int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
-                 void *aux OVS_UNUSED)
+void
+timeval_stop(void)
 {
     ovs_mutex_lock(&monotonic_clock.mutex);
     atomic_store_relaxed(&monotonic_clock.slow_path, true);
     monotonic_clock.stopped = true;
     xclock_gettime(monotonic_clock.id, &monotonic_clock.cache);
     ovs_mutex_unlock(&monotonic_clock.mutex);
+}
 
+static void
+timeval_stop_cb(struct unixctl_conn *conn,
+                 int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
+                 void *aux OVS_UNUSED)
+{
+    timeval_stop();
     unixctl_command_reply(conn, NULL);
 }
 
@@ -787,6 +800,25 @@  timeval_stop_cb(struct unixctl_conn *conn,
  * time to run after the clock has been advanced by MSECS.
  *
  * Does not affect wall clock readings. */
+void
+timeval_warp(long long int total_warp, long long int msecs)
+{
+    ovs_mutex_lock(&monotonic_clock.mutex);
+    if (!MONOTONIC_CLOCK_HAS_WARP_CONN(&monotonic_clock)) {
+        /* we do not have a unixctl connection for internal callers, but the
+         * pointer needs to be set for timewarp_work() to know when to
+         * perform work. */
+        monotonic_clock.large_warp.conn = (struct unixctl_conn *) UINTPTR_MAX;
+    }
+    atomic_store_relaxed(&monotonic_clock.slow_path, true);
+    monotonic_clock.large_warp.total_warp = total_warp;
+    monotonic_clock.large_warp.warp = msecs;
+    monotonic_clock.large_warp.main_thread_id = ovsthread_id_self();
+    ovs_mutex_unlock(&monotonic_clock.mutex);
+
+    timewarp_work();
+}
+
 static void
 timeval_warp_cb(struct unixctl_conn *conn,
                 int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED)
@@ -799,25 +831,25 @@  timeval_warp_cb(struct unixctl_conn *conn,
     }
 
     ovs_mutex_lock(&monotonic_clock.mutex);
-    if (monotonic_clock.large_warp.conn) {
+    if (MONOTONIC_CLOCK_HAS_WARP_CONN(&monotonic_clock)) {
         ovs_mutex_unlock(&monotonic_clock.mutex);
         unixctl_command_reply_error(conn, "A previous warp in progress");
         return;
     }
-    atomic_store_relaxed(&monotonic_clock.slow_path, true);
     monotonic_clock.large_warp.conn = conn;
-    monotonic_clock.large_warp.total_warp = total_warp;
-    monotonic_clock.large_warp.warp = msecs;
-    monotonic_clock.large_warp.main_thread_id = ovsthread_id_self();
     ovs_mutex_unlock(&monotonic_clock.mutex);
 
-    timewarp_work();
+    timeval_warp(total_warp, msecs);
 }
 
+void
+timeval_timewarp_enable(void) {
+    timewarp_enabled = true;
+}
 void
 timeval_dummy_register(void)
 {
-    timewarp_enabled = true;
+    timeval_timewarp_enable();
     unixctl_command_register("time/stop", "", 0, 0, timeval_stop_cb, NULL);
     unixctl_command_register("time/warp", "[large_msecs] msecs", 1, 2,
                              timeval_warp_cb, NULL);
diff --git a/lib/timeval.h b/lib/timeval.h
index 502f703d4..ea7977e0c 100644
--- a/lib/timeval.h
+++ b/lib/timeval.h
@@ -81,6 +81,10 @@  long long int time_boot_msec(void);
 
 void timewarp_run(void);
 
+void timeval_timewarp_enable(void);
+void timeval_stop(void);
+void timeval_warp(long long int, long long int);
+
 #ifdef  __cplusplus
 }
 #endif