Add fd input (such as socket) event to shill_event
It's understood that the main event loop is changing, but
here's a first cut at describing the sort of things we will
need in that respect. I'm using this facility for my RTNL
prototype.
BUG=n0ne (this will likely be overwritten)
TEST=Unit tests added to unittest.cc and pass
Change-Id: I4ee89fc4119da0871e9f852707cce9341a5018c0
Reviewed-on: http://gerrit.chromium.org/gerrit/605
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/shill_event.cc b/shill_event.cc
index 6b1356a..b78680c 100644
--- a/shill_event.cc
+++ b/shill_event.cc
@@ -50,4 +50,60 @@
}
}
+static gboolean DispatchIOHandler(GIOChannel *chan, GIOCondition cond,
+ gpointer data);
+
+class GlibIOInputHandler : public IOInputHandler {
+public:
+ GIOChannel *channel_;
+ Callback<InputData *> *callback_;
+ guint source_id_;
+ EventDispatcher *dispatcher_;
+
+ GlibIOInputHandler(EventDispatcher *dispatcher, int fd,
+ Callback<InputData *> *callback) :
+ dispatcher_(dispatcher), callback_(callback) {
+ channel_ = g_io_channel_unix_new(fd);
+ g_io_channel_set_close_on_unref(channel_, TRUE);
+ source_id_ = g_io_add_watch(channel_,
+ (GIOCondition)(G_IO_IN | G_IO_NVAL |
+ G_IO_HUP | G_IO_ERR),
+ DispatchIOHandler, this);
+ }
+
+ ~GlibIOInputHandler() {
+ g_source_remove(source_id_);
+ g_io_channel_shutdown(channel_, TRUE, NULL);
+ g_io_channel_unref(channel_);
+ }
+};
+
+static gboolean DispatchIOHandler(GIOChannel *chan, GIOCondition cond,
+ gpointer data) {
+ GlibIOInputHandler *handler = static_cast<GlibIOInputHandler *>(data);
+ unsigned char buf[4096];
+ gsize len;
+ GIOError err;
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+ return FALSE;
+
+ err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
+ if (err) {
+ if (err == G_IO_ERROR_AGAIN)
+ return TRUE;
+ return FALSE;
+ }
+
+ InputData input_data = { buf, len };
+ handler->callback_->Run(&input_data);
+
+ return TRUE;
+}
+
+IOInputHandler *EventDispatcher::CreateInputHandler(int fd,
+ Callback<InputData *> *callback) {
+ return new GlibIOInputHandler(this, fd, callback);
+}
+
} // namespace shill
diff --git a/shill_event.h b/shill_event.h
index 09a85e4..cda8872 100644
--- a/shill_event.h
+++ b/shill_event.h
@@ -97,6 +97,17 @@
std::vector<Arg> event_queue_;
};
+struct InputData {
+ unsigned char *buf;
+ size_t len;
+};
+
+class IOInputHandler {
+ public:
+ IOInputHandler() {}
+ virtual ~IOInputHandler() {}
+};
+
// This is the main event dispatcher. It contains a central instance, and
// is the entity responsible for dispatching events out of all queues to
// their listeners during the idle loop.
@@ -106,6 +117,7 @@
void ExecuteOnIdle();
void RegisterCallbackQueue(EventQueueItem *queue);
void UnregisterCallbackQueue(EventQueueItem *queue);
+ IOInputHandler *CreateInputHandler(int fd, Callback<InputData *> *callback);
private:
std::vector<EventQueueItem*> queue_list_;
};
diff --git a/shill_unittest.cc b/shill_unittest.cc
index 5fdf7f2..214a5f7 100644
--- a/shill_unittest.cc
+++ b/shill_unittest.cc
@@ -25,10 +25,14 @@
class MockEventDispatchTester {
public:
explicit MockEventDispatchTester(EventDispatcher *dispatcher)
- : triggered_(false),
+ : dispatcher_(dispatcher),
+ triggered_(false),
int_callback_(new ClassCallback<MockEventDispatchTester, int>(this,
- &MockEventDispatchTester::HandleInt)),
- int_callback_queue_(dispatcher) {
+ &MockEventDispatchTester::HandleInt)),
+ int_callback_queue_(dispatcher),
+ got_data_(false),
+ data_callback_(new ClassCallback<MockEventDispatchTester, InputData *>
+ (this, &MockEventDispatchTester::HandleData)) {
int_callback_queue_.AddCallback(int_callback_);
timer_source_ = g_timeout_add_seconds(1,
&MockEventDispatchTester::CallbackFunc, this);
@@ -60,11 +64,34 @@
void ResetTrigger() { triggered_ = false; }
void RemoveCallback() { int_callback_queue_.RemoveCallback(int_callback_); }
+
+ void HandleData(InputData *inputData) {
+ printf("MockEventDispatchTester handling data len %d %.*s\n",
+ inputData->len, inputData->len, inputData->buf);
+ got_data_ = true;
+ IOComplete(inputData->len);
+ }
+ bool GetData() { return got_data_; }
+
+ void ListenIO(int fd) {
+ input_handler_ = dispatcher_->CreateInputHandler(fd, data_callback_);
+ }
+
+ void StopListenIO() {
+ got_data_ = false;
+ delete(input_handler_);
+ }
+
MOCK_METHOD1(CallbackComplete, void(int));
+ MOCK_METHOD1(IOComplete, void(int));
private:
+ EventDispatcher *dispatcher_;
bool triggered_;
Callback<int> *int_callback_;
EventQueue<int>int_callback_queue_;
+ bool got_data_;
+ Callback<InputData *> *data_callback_;
+ IOInputHandler *input_handler_;
int timer_source_;
static gboolean CallbackFunc(gpointer data) {
static int i = 0;
@@ -111,6 +138,21 @@
for (int main_loop_count = 0;
main_loop_count < 6 && !dispatcher_test_.GetTrigger(); ++main_loop_count)
g_main_context_iteration(NULL, TRUE);
+
+
+ EXPECT_CALL(dispatcher_test_, IOComplete(16));
+ int pipefd[2];
+ EXPECT_EQ(pipe(pipefd), 0);
+
+ dispatcher_test_.ListenIO(pipefd[0]);
+ EXPECT_EQ(write(pipefd[1], "This is a test?!", 16), 16);
+
+ // Crank the glib main loop a few times
+ for (int main_loop_count = 0;
+ main_loop_count < 6 && !dispatcher_test_.GetData(); ++main_loop_count)
+ g_main_context_iteration(NULL, TRUE);
+
+ dispatcher_test_.StopListenIO();
}
}