Commit 62cc0672 authored by Dave Watson's avatar Dave Watson Committed by Facebook Github Bot 3

Ensure getVia(eventbase) does not busy wait

Summary:
Currently, getVia(eventbase) will busy wait if no work is scheduled on the event base.

Tweak the DrivableExecutor API a bit to support sleeping/wakeups.
There was already a similar fix for the only other existing DrivableExecutor, the ManualExecutor, in
D2906858.

Reviewed By: andriigrynenko

Differential Revision: D3613954

fbshipit-source-id: 9ff9f2e010040d9886fdf51a665e3afabbff57c0
parent 4bd3e568
......@@ -36,11 +36,19 @@ namespace folly {
* These will be most helpful in tests, for instance if you need to pump a mock
* EventBase until Futures complete.
*/
class DrivableExecutor : public virtual Executor {
public:
virtual ~DrivableExecutor() = default;
// Make progress on this Executor's work.
//
// Drive *must not* busy wait if there is no work to do. Instead,
// sleep (using a semaphore or similar) until at least one event is
// processed.
// I.e. make_future().via(foo).then(...).getVia(DrivableExecutor)
// must not spin, even though nothing happens on the drivable
// executor.
virtual void drive() = 0;
};
......
......@@ -987,7 +987,7 @@ void waitViaImpl(Future<T>& f, DrivableExecutor* e) {
// always have a callback to satisfy it
if (f.isReady())
return;
f = f.then([](T&& t) { return std::move(t); });
f = f.via(e).then([](T&& t) { return std::move(t); });
while (!f.isReady()) {
e->drive();
}
......
......@@ -592,6 +592,7 @@ class EventBase : private boost::noncopyable,
/// Implements the DrivableExecutor interface
void drive() override {
auto keepAlive = loopKeepAlive();
loopOnce();
}
......
......@@ -27,6 +27,8 @@
#include <folly/io/async/test/Util.h>
#include <folly/portability/Unistd.h>
#include <folly/futures/Promise.h>
#include <atomic>
#include <iostream>
#include <memory>
......@@ -1822,3 +1824,32 @@ TEST(EventBaseTest, LoopKeepAliveShutdown) {
t.join();
}
TEST(EventBaseTest, DrivableExecutorTest) {
folly::Promise<bool> p;
auto f = p.getFuture();
EventBase base;
bool finished = false;
std::thread t([&] {
/* sleep override */
std::this_thread::sleep_for(std::chrono::microseconds(10));
finished = true;
base.runInEventBaseThread([&]() { p.setValue(true); });
});
// Ensure drive does not busy wait
base.drive(); // TODO: fix notification queue init() extra wakeup
base.drive();
EXPECT_TRUE(finished);
folly::Promise<bool> p2;
auto f2 = p2.getFuture();
// Ensure waitVia gets woken up properly, even from
// a separate thread.
base.runAfterDelay([&]() { p2.setValue(true); }, 10);
f2.waitVia(&base);
EXPECT_TRUE(f2.isReady());
t.join();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment