Split EventBaseThread from ScopedEventBaseThread
authorYedidya Feldblum <yfeldblum@fb.com>
Sat, 17 Dec 2016 04:20:35 +0000 (20:20 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Sat, 17 Dec 2016 04:32:54 +0000 (20:32 -0800)
Summary:
[Folly] Split `EventBaseThread` from `ScopedEventBaseThread`.

Now `ScopedEventBaseThread` is really scoped and immovable, while `EventBaseThread` is movable and can be started and stopped.

Users which will never move, and will never start or stop, the `ScopedEventBaseThread` can continue using it. Users which need to move, or which need to start and stop, the object will use `EventBaseThread` instead.

Reviewed By: andriigrynenko

Differential Revision: D4338447

fbshipit-source-id: 57c186630bc199a7a7b7223b1fcb077ce3d86743

folly/Makefile.am
folly/io/async/EventBaseThread.cpp [new file with mode: 0644]
folly/io/async/EventBaseThread.h [new file with mode: 0644]
folly/io/async/ScopedEventBaseThread.cpp
folly/io/async/ScopedEventBaseThread.h
folly/io/async/test/EventBaseThreadTest.cpp [new file with mode: 0644]
folly/io/async/test/ScopedEventBaseThreadTest.cpp

index bb87d5127ffc4afc537efc41fceac174214df190..19cf569b346d75c36bde49a77715be4bec471912 100644 (file)
@@ -223,6 +223,7 @@ nobase_follyinclude_HEADERS = \
        io/async/EventBase.h \
        io/async/EventBaseLocal.h \
        io/async/EventBaseManager.h \
+       io/async/EventBaseThread.h \
        io/async/EventFDWrapper.h \
        io/async/EventHandler.h \
        io/async/EventUtil.h \
@@ -450,6 +451,7 @@ libfolly_la_SOURCES = \
        io/async/EventBase.cpp \
        io/async/EventBaseLocal.cpp \
        io/async/EventBaseManager.cpp \
+       io/async/EventBaseThread.cpp \
        io/async/EventHandler.cpp \
        io/async/Request.cpp \
        io/async/SSLContext.cpp \
diff --git a/folly/io/async/EventBaseThread.cpp b/folly/io/async/EventBaseThread.cpp
new file mode 100644 (file)
index 0000000..ebe9ac5
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/io/async/EventBaseThread.h>
+
+#include <folly/Memory.h>
+#include <folly/io/async/ScopedEventBaseThread.h>
+
+namespace folly {
+
+EventBaseThread::EventBaseThread() : EventBaseThread(true) {}
+
+EventBaseThread::EventBaseThread(bool autostart, EventBaseManager* ebm)
+    : ebm_(ebm) {
+  if (autostart) {
+    start();
+  }
+}
+
+EventBaseThread::EventBaseThread(EventBaseManager* ebm)
+    : EventBaseThread(true, ebm) {}
+
+EventBaseThread::~EventBaseThread() = default;
+
+EventBaseThread::EventBaseThread(EventBaseThread&&) noexcept = default;
+EventBaseThread& EventBaseThread::operator=(EventBaseThread&&) noexcept =
+    default;
+
+EventBase* EventBaseThread::getEventBase() const {
+  return th_ ? th_->getEventBase() : nullptr;
+}
+
+bool EventBaseThread::running() const {
+  return !!th_;
+}
+
+void EventBaseThread::start() {
+  if (th_) {
+    return;
+  }
+  th_ = make_unique<ScopedEventBaseThread>(ebm_);
+}
+
+void EventBaseThread::stop() {
+  th_ = nullptr;
+}
+}
diff --git a/folly/io/async/EventBaseThread.h b/folly/io/async/EventBaseThread.h
new file mode 100644 (file)
index 0000000..a4ac541
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+namespace folly {
+
+class EventBase;
+class EventBaseManager;
+class ScopedEventBaseThread;
+
+class EventBaseThread {
+ public:
+  EventBaseThread();
+  explicit EventBaseThread(bool autostart, EventBaseManager* ebm = nullptr);
+  explicit EventBaseThread(EventBaseManager* ebm);
+  ~EventBaseThread();
+
+  EventBaseThread(EventBaseThread&&) noexcept;
+  EventBaseThread& operator=(EventBaseThread&&) noexcept;
+
+  EventBase* getEventBase() const;
+
+  bool running() const;
+  void start();
+  void stop();
+
+ private:
+  EventBaseThread(EventBaseThread const&) = default;
+  EventBaseThread& operator=(EventBaseThread const&) = default;
+
+  EventBaseManager* ebm_;
+  std::unique_ptr<ScopedEventBaseThread> th_;
+};
+}
index 29fa4522ff553af02080376e7605243b69b09736..1e78f5eea031a897882b917ec4a0e243b45c77f9 100644 (file)
@@ -17,7 +17,9 @@
 #include <folly/io/async/ScopedEventBaseThread.h>
 
 #include <thread>
+
 #include <folly/Memory.h>
+#include <folly/io/async/EventBaseManager.h>
 
 using namespace std;
 
@@ -25,55 +27,22 @@ namespace folly {
 
 static void run(EventBaseManager* ebm, EventBase* eb) {
   ebm->setEventBase(eb, false);
-  CHECK_NOTNULL(eb)->loopForever();
+  eb->loopForever();
   ebm->clearEventBase();
 }
 
-ScopedEventBaseThread::ScopedEventBaseThread(
-    bool autostart,
-    EventBaseManager* ebm)
+ScopedEventBaseThread::ScopedEventBaseThread()
+    : ScopedEventBaseThread(nullptr) {}
+
+ScopedEventBaseThread::ScopedEventBaseThread(EventBaseManager* ebm)
     : ebm_(ebm ? ebm : EventBaseManager::get()) {
-  if (autostart) {
-    start();
-  }
+  th_ = thread(run, ebm_, &eb_);
+  eb_.waitUntilRunning();
 }
 
-ScopedEventBaseThread::ScopedEventBaseThread(
-    EventBaseManager* ebm) :
-  ScopedEventBaseThread(true, ebm) {}
-
 ScopedEventBaseThread::~ScopedEventBaseThread() {
-  stop();
-}
-
-ScopedEventBaseThread::ScopedEventBaseThread(
-    ScopedEventBaseThread&& /* other */) noexcept = default;
-
-ScopedEventBaseThread& ScopedEventBaseThread::operator=(
-    ScopedEventBaseThread&& /* other */) noexcept = default;
-
-void ScopedEventBaseThread::start() {
-  if (running()) {
-    return;
-  }
-  eventBase_ = make_unique<EventBase>();
-  thread_ = make_unique<thread>(run, ebm_, eventBase_.get());
-  eventBase_->waitUntilRunning();
-}
-
-void ScopedEventBaseThread::stop() {
-  if (!running()) {
-    return;
-  }
-  eventBase_->terminateLoopSoon();
-  thread_->join();
-  eventBase_ = nullptr;
-  thread_ = nullptr;
-}
-
-bool ScopedEventBaseThread::running() {
-  CHECK(bool(eventBase_) == bool(thread_));
-  return eventBase_ && thread_;
+  eb_.terminateLoopSoon();
+  th_.join();
 }
 
 }
index 0370eb9b9d96dda010c6db387f538b6c0d95fab9..c59b275e08e063c67a43294b974752bc8f44d937 100644 (file)
 #include <memory>
 #include <thread>
 #include <folly/io/async/EventBase.h>
-#include <folly/io/async/EventBaseManager.h>
 
 namespace folly {
 
+class EventBaseManager;
+
 /**
  * A helper class to start a new thread running a EventBase loop.
  *
@@ -32,34 +33,24 @@ namespace folly {
  */
 class ScopedEventBaseThread {
  public:
-  explicit ScopedEventBaseThread(
-      bool autostart = true,
-      EventBaseManager* ebm = nullptr);
-  explicit ScopedEventBaseThread(
-      EventBaseManager* ebm);
+  ScopedEventBaseThread();
+  explicit ScopedEventBaseThread(EventBaseManager* ebm);
   ~ScopedEventBaseThread();
 
-  ScopedEventBaseThread(ScopedEventBaseThread&& other) noexcept;
-  ScopedEventBaseThread &operator=(ScopedEventBaseThread&& other) noexcept;
-
-  /**
-   * Get a pointer to the EventBase driving this thread.
-   */
   EventBase* getEventBase() const {
-    return eventBase_.get();
+    return &eb_;
   }
 
-  void start();
-  void stop();
-  bool running();
-
  private:
+  ScopedEventBaseThread(ScopedEventBaseThread&& other) = delete;
+  ScopedEventBaseThread& operator=(ScopedEventBaseThread&& other) = delete;
+
   ScopedEventBaseThread(const ScopedEventBaseThread& other) = delete;
   ScopedEventBaseThread& operator=(const ScopedEventBaseThread& other) = delete;
 
   EventBaseManager* ebm_;
-  std::unique_ptr<EventBase> eventBase_;
-  std::unique_ptr<std::thread> thread_;
+  mutable EventBase eb_;
+  std::thread th_;
 };
 
 }
diff --git a/folly/io/async/test/EventBaseThreadTest.cpp b/folly/io/async/test/EventBaseThreadTest.cpp
new file mode 100644 (file)
index 0000000..0675929
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/io/async/EventBaseThread.h>
+
+#include <chrono>
+
+#include <folly/Baton.h>
+#include <folly/io/async/EventBaseManager.h>
+#include <folly/portability/GTest.h>
+
+using namespace std;
+using namespace std::chrono;
+using namespace folly;
+
+class EventBaseThreadTest : public testing::Test {};
+
+TEST_F(EventBaseThreadTest, example) {
+  EventBaseThread ebt;
+
+  Baton<> done;
+  ebt.getEventBase()->runInEventBaseThread([&] { done.post(); });
+  ASSERT_TRUE(done.timed_wait(seconds(1)));
+}
+
+TEST_F(EventBaseThreadTest, start_stop) {
+  EventBaseThread ebt(false);
+
+  for (size_t i = 0; i < 4; ++i) {
+    EXPECT_EQ(nullptr, ebt.getEventBase());
+    ebt.start();
+    EXPECT_NE(nullptr, ebt.getEventBase());
+
+    Baton<> done;
+    ebt.getEventBase()->runInEventBaseThread([&] { done.post(); });
+    ASSERT_TRUE(done.timed_wait(seconds(1)));
+
+    EXPECT_NE(nullptr, ebt.getEventBase());
+    ebt.stop();
+    EXPECT_EQ(nullptr, ebt.getEventBase());
+  }
+}
+
+TEST_F(EventBaseThreadTest, move) {
+  auto ebt0 = EventBaseThread();
+  auto ebt1 = std::move(ebt0);
+  auto ebt2 = std::move(ebt1);
+
+  EXPECT_EQ(nullptr, ebt0.getEventBase());
+  EXPECT_EQ(nullptr, ebt1.getEventBase());
+  EXPECT_NE(nullptr, ebt2.getEventBase());
+
+  Baton<> done;
+  ebt2.getEventBase()->runInEventBaseThread([&] { done.post(); });
+  ASSERT_TRUE(done.timed_wait(seconds(1)));
+}
+
+TEST_F(EventBaseThreadTest, self_move) {
+  EventBaseThread ebt0;
+  auto ebt = std::move(ebt0);
+
+  EXPECT_NE(nullptr, ebt.getEventBase());
+
+  Baton<> done;
+  ebt.getEventBase()->runInEventBaseThread([&] { done.post(); });
+  ASSERT_TRUE(done.timed_wait(seconds(1)));
+}
+
+TEST_F(EventBaseThreadTest, default_manager) {
+  auto ebm = EventBaseManager::get();
+  EventBaseThread ebt;
+  auto ebt_eb = ebt.getEventBase();
+  auto ebm_eb = static_cast<EventBase*>(nullptr);
+  ebt_eb->runInEventBaseThreadAndWait([&] { ebm_eb = ebm->getEventBase(); });
+  EXPECT_EQ(uintptr_t(ebt_eb), uintptr_t(ebm_eb));
+}
+
+TEST_F(EventBaseThreadTest, custom_manager) {
+  EventBaseManager ebm;
+  EventBaseThread ebt(&ebm);
+  auto ebt_eb = ebt.getEventBase();
+  auto ebm_eb = static_cast<EventBase*>(nullptr);
+  ebt_eb->runInEventBaseThreadAndWait([&] { ebm_eb = ebm.getEventBase(); });
+  EXPECT_EQ(uintptr_t(ebt_eb), uintptr_t(ebm_eb));
+}
index 6d7ba4c149852e19d98368ebbbe680abd2b2c441..f9ed2bf889d2c95f58a3305bbb6ccbb1566dd5f2 100644 (file)
@@ -17,7 +17,9 @@
 #include <folly/io/async/ScopedEventBaseThread.h>
 
 #include <chrono>
+
 #include <folly/Baton.h>
+#include <folly/io/async/EventBaseManager.h>
 #include <folly/portability/GTest.h>
 
 using namespace std;
@@ -34,49 +36,6 @@ TEST_F(ScopedEventBaseThreadTest, example) {
   ASSERT_TRUE(done.timed_wait(seconds(1)));
 }
 
-TEST_F(ScopedEventBaseThreadTest, start_stop) {
-  ScopedEventBaseThread sebt(false);
-
-  for (size_t i = 0; i < 4; ++i) {
-    EXPECT_EQ(nullptr, sebt.getEventBase());
-    sebt.start();
-    EXPECT_NE(nullptr, sebt.getEventBase());
-
-    Baton<> done;
-    sebt.getEventBase()->runInEventBaseThread([&] { done.post(); });
-    ASSERT_TRUE(done.timed_wait(seconds(1)));
-
-    EXPECT_NE(nullptr, sebt.getEventBase());
-    sebt.stop();
-    EXPECT_EQ(nullptr, sebt.getEventBase());
-  }
-}
-
-TEST_F(ScopedEventBaseThreadTest, move) {
-  auto sebt0 = ScopedEventBaseThread();
-  auto sebt1 = std::move(sebt0);
-  auto sebt2 = std::move(sebt1);
-
-  EXPECT_EQ(nullptr, sebt0.getEventBase());
-  EXPECT_EQ(nullptr, sebt1.getEventBase());
-  EXPECT_NE(nullptr, sebt2.getEventBase());
-
-  Baton<> done;
-  sebt2.getEventBase()->runInEventBaseThread([&] { done.post(); });
-  ASSERT_TRUE(done.timed_wait(seconds(1)));
-}
-
-TEST_F(ScopedEventBaseThreadTest, self_move) {
-  ScopedEventBaseThread sebt0;
-  auto sebt = std::move(sebt0);
-
-  EXPECT_NE(nullptr, sebt.getEventBase());
-
-  Baton<> done;
-  sebt.getEventBase()->runInEventBaseThread([&] { done.post(); });
-  ASSERT_TRUE(done.timed_wait(seconds(1)));
-}
-
 TEST_F(ScopedEventBaseThreadTest, default_manager) {
   auto ebm = EventBaseManager::get();
   ScopedEventBaseThread sebt;