Implement eloop for Fuchsia.

Use the mtl message loop to implement hostap's eloop interface. This
implementation only includes registering and cancelling timeouts. The
other features are not necessary for wpa_supplicant.

Accidentally submitted and reverted.
Previous-Change-Id: I7260f15547d28133d33aaa9467b0eb6e920ee79d

Change-Id: I34f01e70da8e0fc184b84b2dccff367b88c15ae2
diff --git a/src/utils/eloop_fuchsia.cc b/src/utils/eloop_fuchsia.cc
index a938f26..59baefe 100644
--- a/src/utils/eloop_fuchsia.cc
+++ b/src/utils/eloop_fuchsia.cc
@@ -3,13 +3,70 @@
 // See README for more details.
 
 extern "C" {
-// Hostap code requires this be the first file included
+// Hostap does not include "includes.h" in their header files, though it is
+// required.
+// So this must be included before "eloop.h" in order to compile.
 #include "includes.h"
 
 #include "eloop.h"
-} // extern "C"
+}  // extern "C"
 
-extern "C" int eloop_init() { return 0; }
+#include <stdint.h>
+
+#include <algorithm>
+#include <list>
+#include <memory>
+
+#include "lib/ftl/logging.h"
+#include "lib/ftl/time/time_delta.h"
+#include "lib/ftl/time/time_point.h"
+#include "lib/mtl/tasks/message_loop.h"
+
+extern "C" {
+#include "os.h"
+}  // extern "C"
+
+constexpr int64_t kUsecPerSec = 1000000L;
+
+struct EloopTimeout {
+  int id;
+  eloop_timeout_handler handler;
+  void *eloop_data;
+  void *user_data;
+  ftl::TimeDelta delay;
+  ftl::TimePoint approx_end_time;
+};
+
+static mtl::MessageLoop *loop = nullptr;
+static std::list<EloopTimeout> *timeouts = nullptr;
+static int current_registration_id = 0;
+
+static std::list<EloopTimeout>::iterator FindTimeout(
+    eloop_timeout_handler handler, void *eloop_data, void *user_data) {
+  return std::find_if(timeouts->begin(), timeouts->end(),
+                      [handler, eloop_data, user_data](EloopTimeout timeout) {
+                        return timeout.handler == handler &&
+                               timeout.eloop_data == eloop_data &&
+                               timeout.user_data == user_data;
+                      });
+}
+
+static ftl::TimeDelta TimeDelta(unsigned int secs, unsigned int usecs) {
+  return ftl::TimeDelta::FromSeconds(secs) +
+         ftl::TimeDelta::FromMicroseconds(usecs);
+}
+
+static ftl::TimeDelta RemainingTimeDelta(ftl::TimePoint end_time) {
+  ftl::TimePoint now = ftl::TimePoint::Now();
+  return end_time > now ? ftl::TimeDelta::Zero() : end_time - now;
+}
+
+extern "C" int eloop_init() {
+  FTL_CHECK(loop == nullptr);
+  loop = new mtl::MessageLoop();
+  timeouts = new std::list<EloopTimeout>();
+  return 0;
+}
 
 extern "C" int eloop_register_read_sock(int sock, eloop_sock_handler handler,
                                         void *eloop_data, void *user_data) {
@@ -46,43 +103,103 @@
 extern "C" int eloop_register_timeout(unsigned int secs, unsigned int usecs,
                                       eloop_timeout_handler handler,
                                       void *eloop_data, void *user_data) {
-  // TODO(alangardner): Implement
-  return -1;
+  ftl::TimeDelta delay = TimeDelta(secs, usecs);
+  int registration_id = ++current_registration_id;
+  EloopTimeout timeout = {registration_id, handler,
+                          eloop_data,      user_data,
+                          delay,           ftl::TimePoint::Now() + delay};
+  timeouts->push_back(timeout);
+  loop->task_runner()->PostDelayedTask(
+      [registration_id] {
+        for (auto timeout_iter = timeouts->begin();
+             timeout_iter != timeouts->end(); ++timeout_iter) {
+          if (registration_id == timeout_iter->id) {
+            timeout_iter->handler(timeout_iter->eloop_data,
+                                  timeout_iter->user_data);
+            timeouts->erase(timeout_iter);
+            break;
+          }
+        }
+      },
+      delay);
+  return 0;
 }
 
 extern "C" int eloop_cancel_timeout(eloop_timeout_handler handler,
                                     void *eloop_data, void *user_data) {
-  // TODO(alangardner): Implement
-  return -1;
+  size_t old_size = std::distance(timeouts->begin(), timeouts->end());
+  timeouts->erase(
+      std::remove_if(timeouts->begin(), timeouts->end(),
+                     [handler, eloop_data, user_data](EloopTimeout timeout) {
+                       return timeout.handler == handler &&
+                              (timeout.eloop_data == eloop_data ||
+                               eloop_data == ELOOP_ALL_CTX) &&
+                              (timeout.user_data == user_data ||
+                               user_data == ELOOP_ALL_CTX);
+                     }),
+      timeouts->end());
+  size_t new_size = std::distance(timeouts->begin(), timeouts->end());
+  return old_size - new_size;
 }
 
 extern "C" int eloop_cancel_timeout_one(eloop_timeout_handler handler,
                                         void *eloop_data, void *user_data,
                                         struct os_reltime *remaining) {
-  // TODO(alangardner): Implement
-  return -1;
+  std::list<EloopTimeout>::iterator timeout =
+      FindTimeout(handler, eloop_data, user_data);
+  if (timeout == timeouts->end()) {
+    return 0;
+  } else {
+    ftl::TimeDelta remaining_delta =
+        RemainingTimeDelta(timeout->approx_end_time);
+    remaining->sec = remaining_delta.ToSeconds();
+    remaining->usec = remaining_delta.ToMicroseconds() % kUsecPerSec;
+    timeouts->erase(timeout);
+    return 1;
+  }
 }
 
 extern "C" int eloop_is_timeout_registered(eloop_timeout_handler handler,
                                            void *eloop_data, void *user_data) {
-  // TODO(alangardner): Implement
-  return -1;
+  return FindTimeout(handler, eloop_data, user_data) == timeouts->end() ? 0 : 1;
 }
 
 extern "C" int eloop_deplete_timeout(unsigned int req_secs,
                                      unsigned int req_usecs,
                                      eloop_timeout_handler handler,
                                      void *eloop_data, void *user_data) {
-  // TODO(alangardner): Implement
-  return -1;
+  std::list<EloopTimeout>::iterator timeout =
+      FindTimeout(handler, eloop_data, user_data);
+  if (timeout == timeouts->end()) {
+    return -1;
+  } else if (RemainingTimeDelta(timeout->approx_end_time) >
+             TimeDelta(req_secs, req_usecs)) {
+    timeouts->erase(timeout);
+    // Note: Current implementation of register cannot fail.
+    eloop_register_timeout(req_secs, req_usecs, handler, eloop_data, user_data);
+    return 1;
+  } else {
+    return 0;
+  }
 }
 
 extern "C" int eloop_replenish_timeout(unsigned int req_secs,
                                        unsigned int req_usecs,
                                        eloop_timeout_handler handler,
                                        void *eloop_data, void *user_data) {
-  // TODO(alangardner): Implement
-  return -1;
+  std::list<EloopTimeout>::iterator timeout =
+      FindTimeout(handler, eloop_data, user_data);
+  if (timeout == timeouts->end()) {
+    return -1;
+  } else if (RemainingTimeDelta(timeout->approx_end_time) <
+             TimeDelta(req_secs, req_usecs)) {
+    timeouts->erase(timeout);
+    // Note: Current implementation of register cannot fail.
+    eloop_register_timeout(req_secs, req_usecs, handler, eloop_data, user_data);
+    return 1;
+  } else {
+    return 0;
+  }
 }
 
 extern "C" int eloop_register_signal(int sig, eloop_signal_handler handler,
@@ -104,21 +221,24 @@
 }
 
 extern "C" void eloop_run() {
-  // TODO(alangardner): Implement
+  FTL_CHECK(loop);
+  loop->Run();
 }
 
 extern "C" void eloop_terminate() {
-  // TODO(alangardner): Implement
+  FTL_CHECK(loop);
+  loop->QuitNow();
 }
 
 extern "C" void eloop_destroy() {
-  // TODO(alangardner): Implement
+  FTL_CHECK(loop);
+  loop->QuitNow();
+  delete loop;
+  loop = nullptr;
+  delete timeouts;
 }
 
-extern "C" int eloop_terminated() {
-  // TODO(alangardner): Implement
-  return 1;
-}
+extern "C" int eloop_terminated() { return loop ? 0 : 1; }
 
 extern "C" void eloop_wait_for_read_sock(int sock) {
   // Not implemented.
diff --git a/wpa_supplicant/BUILD.gn b/wpa_supplicant/BUILD.gn
index b28e87f..1eb68e3 100644
--- a/wpa_supplicant/BUILD.gn
+++ b/wpa_supplicant/BUILD.gn
@@ -94,4 +94,10 @@
   ]
 
   configs += [ ":wpa_supplicant_private" ]
+
+  deps = [
+    "//lib/ftl",
+    "//lib/mtl",
+    "//mojo/system",
+  ]
 }