Commit 1a61493f authored by Haim Grosman's avatar Haim Grosman Committed by Alecs King

EventBase::runAfterDelay to throw an exception

Summary:
EventBase::runAfterDelay to throw an exception in case it fails to
schedule a proper execution of the given callback
(instead of silently returning false)

it appears to be the right thing to do, since:
@davejwatson: Digging through the layers of code, it appears this can only fail if
epoll_ctl() with EPOLL_CTL_ADD fails. Ignoring libevent bugs, it looks
like the only relevant errors could be ENOMEM or ENOSPC. So
nonrecoverable

Test Plan: Unit tests

Reviewed By: anca@fb.com

Subscribers: yzhan, haijunz, simpkins, net-systems@, varunk, zeus-diffs@, nli, dfechete, fugalh, atlas2-eng@, alandau, bmatheny, everstore-dev@, zhuohuang, wormhole-diffs@, anca, mwa, jgehring, oujin, alikhtarov, fuegen, mshneer, wch, bil, sanketh, zippydb, maxwellsayles, jsedgwick, trunkagent, fbcode-common-diffs@, chaoyc, search-fbcode-diffs@, andrewcox, unicorn-diffs@, tw-eng@, xie, kennyyu, yfeldblum, folly-diffs@, davejwatson

FB internal diff: D1805125

Signature: t1:1805125:1424927912:8bebb4c3b9f1fa189c0ce97b12cdb8f95dba97ae
parent 74651ade
...@@ -610,15 +610,23 @@ bool EventBase::runInEventBaseThreadAndWait(const Cob& fn) { ...@@ -610,15 +610,23 @@ bool EventBase::runInEventBaseThreadAndWait(const Cob& fn) {
return true; return true;
} }
bool EventBase::runAfterDelay(const Cob& cob, void EventBase::runAfterDelay(const Cob& cob,
int milliseconds, int milliseconds,
TimeoutManager::InternalEnum in) { TimeoutManager::InternalEnum in) {
if (!tryRunAfterDelay(cob, milliseconds, in)) {
folly::throwSystemError(
"error in EventBase::runAfterDelay(), failed to schedule timeout");
}
}
bool EventBase::tryRunAfterDelay(const Cob& cob,
int milliseconds,
TimeoutManager::InternalEnum in) {
CobTimeout* timeout = new CobTimeout(this, cob, in); CobTimeout* timeout = new CobTimeout(this, cob, in);
if (!timeout->scheduleTimeout(milliseconds)) { if (!timeout->scheduleTimeout(milliseconds)) {
delete timeout; delete timeout;
return false; return false;
} }
pendingCobTimeouts_.push_back(*timeout); pendingCobTimeouts_.push_back(*timeout);
return true; return true;
} }
......
...@@ -386,12 +386,23 @@ class EventBase : private boost::noncopyable, ...@@ -386,12 +386,23 @@ class EventBase : private boost::noncopyable,
* Runs the given Cob at some time after the specified number of * Runs the given Cob at some time after the specified number of
* milliseconds. (No guarantees exactly when.) * milliseconds. (No guarantees exactly when.)
* *
* @return true iff the cob was successfully registered. * Throws a std::system_error if an error occurs.
*/ */
bool runAfterDelay( void runAfterDelay(
const Cob& c, const Cob& c,
int milliseconds, int milliseconds,
TimeoutManager::InternalEnum = TimeoutManager::InternalEnum::NORMAL); TimeoutManager::InternalEnum in = TimeoutManager::InternalEnum::NORMAL);
/**
* @see tryRunAfterDelay for more details
*
* @return true iff the cob was successfully registered.
*
* */
bool tryRunAfterDelay(
const Cob& cob,
int milliseconds,
TimeoutManager::InternalEnum in = TimeoutManager::InternalEnum::NORMAL);
/** /**
* Set the maximum desired latency in us and provide a callback which will be * Set the maximum desired latency in us and provide a callback which will be
......
...@@ -131,7 +131,7 @@ struct ScheduledEvent { ...@@ -131,7 +131,7 @@ struct ScheduledEvent {
void scheduleEvents(EventBase* eventBase, int fd, ScheduledEvent* events) { void scheduleEvents(EventBase* eventBase, int fd, ScheduledEvent* events) {
for (ScheduledEvent* ev = events; ev->milliseconds > 0; ++ev) { for (ScheduledEvent* ev = events; ev->milliseconds > 0; ++ev) {
eventBase->runAfterDelay(std::bind(&ScheduledEvent::perform, ev, fd), eventBase->tryRunAfterDelay(std::bind(&ScheduledEvent::perform, ev, fd),
ev->milliseconds); ev->milliseconds);
} }
} }
...@@ -240,7 +240,7 @@ TEST(EventBaseTest, ReadPersist) { ...@@ -240,7 +240,7 @@ TEST(EventBaseTest, ReadPersist) {
scheduleEvents(&eb, sp[1], events); scheduleEvents(&eb, sp[1], events);
// Schedule a timeout to unregister the handler after the third write // Schedule a timeout to unregister the handler after the third write
eb.runAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 85); eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 85);
// Loop // Loop
TimePoint start; TimePoint start;
...@@ -288,7 +288,7 @@ TEST(EventBaseTest, ReadImmediate) { ...@@ -288,7 +288,7 @@ TEST(EventBaseTest, ReadImmediate) {
scheduleEvents(&eb, sp[1], events); scheduleEvents(&eb, sp[1], events);
// Schedule a timeout to unregister the handler // Schedule a timeout to unregister the handler
eb.runAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 20); eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 20);
// Loop // Loop
TimePoint start; TimePoint start;
...@@ -379,7 +379,7 @@ TEST(EventBaseTest, WritePersist) { ...@@ -379,7 +379,7 @@ TEST(EventBaseTest, WritePersist) {
scheduleEvents(&eb, sp[1], events); scheduleEvents(&eb, sp[1], events);
// Schedule a timeout to unregister the handler after the third read // Schedule a timeout to unregister the handler after the third read
eb.runAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 85); eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 85);
// Loop // Loop
TimePoint start; TimePoint start;
...@@ -421,7 +421,7 @@ TEST(EventBaseTest, WriteImmediate) { ...@@ -421,7 +421,7 @@ TEST(EventBaseTest, WriteImmediate) {
// Schedule a timeout to unregister the handler // Schedule a timeout to unregister the handler
int64_t unregisterTimeout = 40; int64_t unregisterTimeout = 40;
eb.runAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler),
unregisterTimeout); unregisterTimeout);
// Loop // Loop
...@@ -601,7 +601,7 @@ TEST(EventBaseTest, ReadWritePersist) { ...@@ -601,7 +601,7 @@ TEST(EventBaseTest, ReadWritePersist) {
scheduleEvents(&eb, sp[1], events); scheduleEvents(&eb, sp[1], events);
// Schedule a timeout to unregister the handler // Schedule a timeout to unregister the handler
eb.runAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 80); eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 80);
// Loop // Loop
TimePoint start; TimePoint start;
...@@ -679,7 +679,7 @@ TEST(EventBaseTest, ReadPartial) { ...@@ -679,7 +679,7 @@ TEST(EventBaseTest, ReadPartial) {
scheduleEvents(&eb, sp[1], events); scheduleEvents(&eb, sp[1], events);
// Schedule a timeout to unregister the handler // Schedule a timeout to unregister the handler
eb.runAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 30); eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 30);
// Loop // Loop
TimePoint start; TimePoint start;
...@@ -746,7 +746,7 @@ TEST(EventBaseTest, WritePartial) { ...@@ -746,7 +746,7 @@ TEST(EventBaseTest, WritePartial) {
scheduleEvents(&eb, sp[1], events); scheduleEvents(&eb, sp[1], events);
// Schedule a timeout to unregister the handler // Schedule a timeout to unregister the handler
eb.runAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 30); eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 30);
// Loop // Loop
TimePoint start; TimePoint start;
...@@ -800,7 +800,7 @@ TEST(EventBaseTest, DestroyHandler) { ...@@ -800,7 +800,7 @@ TEST(EventBaseTest, DestroyHandler) {
// After 10ms, read some data, so that the handler // After 10ms, read some data, so that the handler
// will be notified that it can write. // will be notified that it can write.
eb.runAfterDelay(std::bind(checkReadUntilEmpty, sp[1], initialBytesWritten), eb.tryRunAfterDelay(std::bind(checkReadUntilEmpty, sp[1], initialBytesWritten),
10); 10);
// Start a timer to destroy the handler after 25ms // Start a timer to destroy the handler after 25ms
...@@ -833,9 +833,9 @@ TEST(EventBaseTest, RunAfterDelay) { ...@@ -833,9 +833,9 @@ TEST(EventBaseTest, RunAfterDelay) {
TimePoint timestamp1(false); TimePoint timestamp1(false);
TimePoint timestamp2(false); TimePoint timestamp2(false);
TimePoint timestamp3(false); TimePoint timestamp3(false);
eb.runAfterDelay(std::bind(&TimePoint::reset, &timestamp1), 10); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp1), 10);
eb.runAfterDelay(std::bind(&TimePoint::reset, &timestamp2), 20); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp2), 20);
eb.runAfterDelay(std::bind(&TimePoint::reset, &timestamp3), 40); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp3), 40);
TimePoint start; TimePoint start;
eb.loop(); eb.loop();
...@@ -848,7 +848,7 @@ TEST(EventBaseTest, RunAfterDelay) { ...@@ -848,7 +848,7 @@ TEST(EventBaseTest, RunAfterDelay) {
} }
/** /**
* Test the behavior of runAfterDelay() when some timeouts are * Test the behavior of tryRunAfterDelay() when some timeouts are
* still scheduled when the EventBase is destroyed. * still scheduled when the EventBase is destroyed.
*/ */
TEST(EventBaseTest, RunAfterDelayDestruction) { TEST(EventBaseTest, RunAfterDelayDestruction) {
...@@ -863,15 +863,15 @@ TEST(EventBaseTest, RunAfterDelayDestruction) { ...@@ -863,15 +863,15 @@ TEST(EventBaseTest, RunAfterDelayDestruction) {
EventBase eb; EventBase eb;
// Run two normal timeouts // Run two normal timeouts
eb.runAfterDelay(std::bind(&TimePoint::reset, &timestamp1), 10); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp1), 10);
eb.runAfterDelay(std::bind(&TimePoint::reset, &timestamp2), 20); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp2), 20);
// Schedule a timeout to stop the event loop after 40ms // Schedule a timeout to stop the event loop after 40ms
eb.runAfterDelay(std::bind(&EventBase::terminateLoopSoon, &eb), 40); eb.tryRunAfterDelay(std::bind(&EventBase::terminateLoopSoon, &eb), 40);
// Schedule 2 timeouts that would fire after the event loop stops // Schedule 2 timeouts that would fire after the event loop stops
eb.runAfterDelay(std::bind(&TimePoint::reset, &timestamp3), 80); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp3), 80);
eb.runAfterDelay(std::bind(&TimePoint::reset, &timestamp4), 160); eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp4), 160);
start.reset(); start.reset();
eb.loop(); eb.loop();
...@@ -1003,9 +1003,9 @@ TEST(EventBaseTest, RescheduleTimeout) { ...@@ -1003,9 +1003,9 @@ TEST(EventBaseTest, RescheduleTimeout) {
&AsyncTimeout::scheduleTimeout); &AsyncTimeout::scheduleTimeout);
// after 10ms, reschedule t2 to run sooner than originally scheduled // after 10ms, reschedule t2 to run sooner than originally scheduled
eb.runAfterDelay(std::bind(f, &t2, 10), 10); eb.tryRunAfterDelay(std::bind(f, &t2, 10), 10);
// after 10ms, reschedule t3 to run later than originally scheduled // after 10ms, reschedule t3 to run later than originally scheduled
eb.runAfterDelay(std::bind(f, &t3, 40), 10); eb.tryRunAfterDelay(std::bind(f, &t3, 40), 10);
TimePoint start; TimePoint start;
eb.loop(); eb.loop();
...@@ -1030,7 +1030,7 @@ TEST(EventBaseTest, CancelTimeout) { ...@@ -1030,7 +1030,7 @@ TEST(EventBaseTest, CancelTimeout) {
ReschedulingTimeout t(&eb, timeouts); ReschedulingTimeout t(&eb, timeouts);
t.start(); t.start();
eb.runAfterDelay(std::bind(&AsyncTimeout::cancelTimeout, &t), 50); eb.tryRunAfterDelay(std::bind(&AsyncTimeout::cancelTimeout, &t), 50);
TimePoint start; TimePoint start;
eb.loop(); eb.loop();
...@@ -1137,7 +1137,7 @@ TEST(EventBaseTest, RunInThread) { ...@@ -1137,7 +1137,7 @@ TEST(EventBaseTest, RunInThread) {
// Once the last thread exits, it will stop the loop(). However, this // Once the last thread exits, it will stop the loop(). However, this
// timeout also stops the loop in case there is a bug performing the normal // timeout also stops the loop in case there is a bug performing the normal
// stop. // stop.
data.evb.runAfterDelay(std::bind(&EventBase::terminateLoopSoon, &data.evb), data.evb.tryRunAfterDelay(std::bind(&EventBase::terminateLoopSoon, &data.evb),
3000); 3000);
TimePoint start; TimePoint start;
...@@ -1628,7 +1628,7 @@ TEST(EventBaseTest, RunBeforeLoop) { ...@@ -1628,7 +1628,7 @@ TEST(EventBaseTest, RunBeforeLoop) {
TEST(EventBaseTest, RunBeforeLoopWait) { TEST(EventBaseTest, RunBeforeLoopWait) {
EventBase base; EventBase base;
CountedLoopCallback cb(&base, 1); CountedLoopCallback cb(&base, 1);
base.runAfterDelay([&](){ base.tryRunAfterDelay([&](){
base.terminateLoopSoon(); base.terminateLoopSoon();
}, 500); }, 500);
base.runBeforeLoop(&cb); base.runBeforeLoop(&cb);
......
...@@ -36,7 +36,7 @@ class TestClientPipelineFactory : public PipelineFactory<Pipeline> { ...@@ -36,7 +36,7 @@ class TestClientPipelineFactory : public PipelineFactory<Pipeline> {
CHECK(sock->good()); CHECK(sock->good());
// We probably aren't connected immedately, check after a small delay // We probably aren't connected immedately, check after a small delay
EventBaseManager::get()->getEventBase()->runAfterDelay([sock](){ EventBaseManager::get()->getEventBase()->tryRunAfterDelay([sock](){
CHECK(sock->readable()); CHECK(sock->readable());
}, 100); }, 100);
return nullptr; return nullptr;
......
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