OSDN Git Service

Implement watching file descriptor for wificond event loop
authorNingyuan Wang <nywang@google.com>
Fri, 27 May 2016 20:44:22 +0000 (13:44 -0700)
committerNingyuan Wang <nywang@google.com>
Thu, 2 Jun 2016 16:53:08 +0000 (09:53 -0700)
This CL allows wificond event loop to watch a file descriptor.
Specific callback will be executed when a file descriptor is
ready for input or output.

BUG=28982981
BUG=28867514
TEST=compile
TEST=run out/host/linux-x86/bin/wificond_unit_test

Change-Id: I6417ccdf08fa175acf90824dab16804d4bc624c5

event_loop.h
looper_backed_event_loop.cpp
looper_backed_event_loop.h
main.cpp
tests/looper_backed_event_loop_unittest.cpp

index b7945d3..d058d1c 100644 (file)
@@ -25,6 +25,11 @@ namespace wificond {
 // Abstract class for dispatching tasks.
 class EventLoop {
  public:
+  enum ReadyMode {
+      kModeInput,
+      kModeOutput
+  };
+
   virtual ~EventLoop() {}
 
   // Enqueues a callback.
@@ -36,7 +41,16 @@ class EventLoop {
   // This function can be called on any thread.
   virtual void PostDelayedTask(const std::function<void()>& callback,
                                int64_t delay_ms) = 0;
-  //TODO(nywang): monitoring file descriptor for data
+
+  // Monitoring file descriptor for data.
+  // Callback will be executed when specific file descriptor is ready.
+  // File descriptor is provided as a parameter to this callback:
+  // This function can be called on any thread.
+  // This returns true upon success and returns false when it failed.
+  virtual bool WatchFileDescriptor(
+      int fd,
+      ReadyMode mode,
+      const std::function<void(int)>& callback_) = 0;
 };
 
 }  // namespace wificond
index db7197a..879ada6 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <looper_backed_event_loop.h>
 
+#include <android-base/logging.h>
 #include <utils/Looper.h>
 #include <utils/Timers.h>
 
@@ -39,6 +40,25 @@ class EventLoopCallback : public android::MessageHandler {
   DISALLOW_COPY_AND_ASSIGN(EventLoopCallback);
 };
 
+class WatchFdCallback : public android::LooperCallback {
+ public:
+  explicit WatchFdCallback(const std::function<void(int)>& callback)
+      : callback_(callback) {
+  }
+
+  ~WatchFdCallback() override = default;
+
+  virtual int handleEvent(int fd, int events, void* data) {
+    callback_(fd);
+    return 0;
+  }
+
+ private:
+  const std::function<void(int)> callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(WatchFdCallback);
+};
+
 }  // namespace
 
 namespace android {
@@ -66,6 +86,31 @@ void LooperBackedEventLoop::PostDelayedTask(
   looper_->sendMessageDelayed(ms2ns(delay_ms), looper_callback, NULL);
 }
 
+bool LooperBackedEventLoop::WatchFileDescriptor(
+    int fd,
+    ReadyMode mode,
+    const std::function<void(int)>& callback) {
+  sp<android::LooperCallback>  watch_fd_callback = new WatchFdCallback(callback);
+  int event;
+  if (mode == kModeInput) {
+    event = Looper::EVENT_INPUT;
+  } else if (mode == kModeOutput) {
+    event = Looper::EVENT_OUTPUT;
+  } else {
+    LOG(ERROR) << "Invalid mode for WatchFileDescriptor().";
+    return false;
+  }
+  // addFd() returns 1 if descriptor was added, 0 if arguments were invalid.
+  // Since we are using non-NULL callback, the second parameter 'ident' will
+  // always be ignored. It is OK to use 0 for 'ident'.
+  // See Looper.h for more details.
+  if (looper_->addFd(fd, 0, event, watch_fd_callback, NULL) == 0) {
+    LOG(ERROR) << "Invalid arguments for Looper::addFd().";
+    return false;
+  }
+  return true;
+}
+
 void LooperBackedEventLoop::Poll() {
   while (should_continue_) {
     looper_->pollOnce(-1);
index 6da847d..56c3df6 100644 (file)
@@ -36,6 +36,11 @@ class LooperBackedEventLoop: public EventLoop {
   // See event_loop.h
   void PostDelayedTask(const std::function<void()>& callback,
                        int64_t delay_ms) override;
+  // See event_loop.h
+  bool WatchFileDescriptor(
+      int fd,
+      ReadyMode mode,
+      const std::function<void(int)>& callback) override;
 
   // Performs all pending callbacks and waiting for new events until
   // TriggerExit() is called.
index 028b787..1f0e3f1 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -59,19 +59,12 @@ android::wificond::LooperBackedEventLoop*
 
 }  // namespace
 
-void callback_for_test() {
-  LOG(INFO) << "callback is executed...";
-}
-
 int main(int argc, char** argv) {
   android::base::InitLogging(argv);
   LOG(INFO) << "wificond is starting up...";
   std::unique_ptr<android::wificond::LooperBackedEventLoop> event_dispatcher_(
       new android::wificond::LooperBackedEventLoop());
   ScopedSignalHandler scoped_signal_handler(event_dispatcher_.get());
-  //TODO(nywang): b//28982981, Remove this when we have unittest for event loop checked in.
-  event_dispatcher_->PostTask(&callback_for_test);
-  event_dispatcher_->PostDelayedTask(&callback_for_test, 5000);
   event_dispatcher_->Poll();
   LOG(INFO) << "Leaving the loop...";
   return 0;
index 180f406..27f0881 100644 (file)
  * limitations under the License.
  */
 
+#include <string.h>
+
 #include <memory>
 
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
+#include <utils/Errors.h>
 #include <utils/StopWatch.h>
 
 #include <looper_backed_event_loop.h>
@@ -25,6 +30,44 @@ namespace {
 
 const int kTimingToleranceMs = 25;
 
+// Adapt from libutils/tests/TestHelpers.h
+class Pipe {
+public:
+  android::base::unique_fd send_fd;
+  android::base::unique_fd receive_fd;
+
+  Pipe() {
+    int fds[2];
+    ::pipe(fds);
+
+    receive_fd = android::base::unique_fd(fds[0]);
+    send_fd = android::base::unique_fd(fds[1]);
+  }
+
+  bool writeSignal() {
+    ssize_t n_written = ::write(send_fd, "*", 1);
+    if (n_written != 1) {
+      LOG(ERROR) << "Failed to write signal to pipe: " << strerror(errno);
+      return false;
+    }
+    return true;
+  }
+
+  bool readSignal() {
+    char buf[1];
+    ssize_t n_read = ::read(receive_fd, buf, 1);
+    if (n_read != 1) {
+      if (n_read == 0) {
+        LOG(ERROR) << "No data from pipe";
+      } else {
+        LOG(ERROR) << "Failed to read signal from pipe: " << strerror(errno);
+      }
+      return false;
+    }
+    return true;
+  }
+};
+
 }  // namespace
 
 namespace android {
@@ -61,5 +104,37 @@ TEST_F(WificondLooperBackedEventLoopTest,
   EXPECT_TRUE(task_executed);
 }
 
+TEST_F(WificondLooperBackedEventLoopTest, LooperBackedEventLoopWatchFdInputReadyTest) {
+  Pipe pipe;
+  android::status_t read_result;
+  android::status_t write_result;
+  event_loop_->PostTask([&write_result, &pipe]() {write_result = pipe.writeSignal();});
+  // Read data from pipe when fd is ready for input.
+  EXPECT_TRUE(event_loop_->WatchFileDescriptor(
+      pipe.receive_fd,
+      EventLoop::kModeInput,
+      [&read_result, &pipe, this](int fd) {
+          read_result = pipe.readSignal();
+          event_loop_->TriggerExit();}));
+  event_loop_->Poll();
+  EXPECT_EQ(true, read_result);
+  EXPECT_EQ(true, write_result);
+}
+
+TEST_F(WificondLooperBackedEventLoopTest, LooperBackedEventLoopWatchFdOutputReadyTest) {
+  Pipe pipe;
+  android::status_t write_result;
+  // Write data to pipe when fd is ready for output.
+  EXPECT_TRUE(event_loop_->WatchFileDescriptor(
+      pipe.send_fd,
+      EventLoop::kModeOutput,
+      [&write_result, &pipe, this](int fd) {
+          write_result = pipe.writeSignal();
+          event_loop_->TriggerExit();}));
+  event_loop_->Poll();
+  EXPECT_EQ(true, write_result);
+  EXPECT_EQ(true, pipe.readSignal());
+}
+
 }  // namespace wificond
 }  // namespace android