Networking Changes Pt 1

This is the first set of changes making up CL:
https://chromium-review.googlesource.com/c/openscreen/+/1654330

Note that EventWaiter contains both the new and old implementations in this
change, allowing for a smaller patch set.

Primary changes are:
1) Rework EventWaiter to only be platform-specific methods
2) Add NetworkLoop class to handle non-platform-specific parts of network calls
and handle callbacks
3) Add NetworkRunner to handle interactions between NetworkLoop and TaskRunner

Change-Id: Ied65cc5e5c6d1f1c013f1e2d0999f5c29ca7d9ac
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/1655955
Commit-Queue: Ryan Keane <rwkeane@google.com>
Reviewed-by: mark a. foltz <mfoltz@chromium.org>
Reviewed-by: Brandon Tolsch <btolsch@chromium.org>
Reviewed-by: Peter Thatcher <pthatcher@google.com>
diff --git a/platform/api/task_runner.h b/platform/api/task_runner.h
index 611b2b5..3f67ec4 100644
--- a/platform/api/task_runner.h
+++ b/platform/api/task_runner.h
@@ -7,6 +7,7 @@
 
 #include <future>
 
+#include "absl/types/optional.h"
 #include "platform/api/time.h"
 
 namespace openscreen {
@@ -14,13 +15,11 @@
 
 // A thread-safe API surface that allows for posting tasks. The underlying
 // implementation may be single or multi-threaded, and all complication should
-// be handled by either the implementation class or the TaskRunnerFactory
-// method. It is the expectation of this API that the underlying impl gives
-// the following guarantees:
+// be handled by the implementation class. It is the expectation of this API
+// that the underlying impl gives the following guarantees:
 // (1) Tasks shall not overlap in time/CPU.
 // (2) Tasks shall run sequentially, e.g. posting task A then B implies
 //     that A shall run before B.
-// NOTE: we do not make any assumptions about what thread tasks shall run on.
 class TaskRunner {
  public:
   using Task = std::packaged_task<void() noexcept>;
@@ -50,6 +49,44 @@
   virtual void PostPackagedTaskWithDelay(Task task, Clock::duration delay) = 0;
 };
 
+// Class used to post the same task repeatedly to the task runner, with the
+// frequency of repetition determined by the result of the underlying function.
+// TODO(rwkeane): Move to separate file in util directory.
+class RepeatingFunction {
+ public:
+  // Posts a delayed task that will run repeatedly. The result of the function
+  // object will determine if the task should be reposted, in that it will be
+  // reposted if and only if the result is not absl::nullopt.
+  static inline void Post(
+      TaskRunner* task_runner,
+      std::function<absl::optional<Clock::duration>()> function,
+      Clock::duration delay = Clock::duration(0)) {
+    task_runner->PostTaskWithDelay(RepeatingFunction(task_runner, function),
+                                   delay);
+  }
+
+  // Executes the underlying task and re-posts it to the task runner.
+  void operator()() {
+    absl::optional<Clock::duration> delay = function_();
+    if (delay.has_value()) {
+      RepeatingFunction::Post(task_runner_, function_, delay.value());
+    }
+  }
+
+ private:
+  // Creates a new task that will be posted repeatedly to the task runner. If
+  // the function returns a valid Clock::duration, it will be reposted with
+  // that delay, and will not be reposted if absl::nullopt is instead
+  // returned.
+  // TODO(rwkeane): Should use a weak pointer once we support those.
+  RepeatingFunction(TaskRunner* task_runner,
+                    std::function<absl::optional<Clock::duration>()> function)
+      : task_runner_(task_runner), function_(function){};
+
+  TaskRunner* task_runner_;
+  std::function<absl::optional<Clock::duration>()> function_;
+};
+
 }  // namespace platform
 }  // namespace openscreen