/* * Copyright (c) Facebook, Inc. and its affiliates. * * 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 #include #include #include #include #include #include namespace folly { struct TimeoutManager::CobTimeouts { // small object used as a callback arg with enough info to execute the // appropriate client-provided Cob class CobTimeout : public AsyncTimeout { public: CobTimeout(TimeoutManager* timeoutManager, Func cob, InternalEnum internal) : AsyncTimeout(timeoutManager, internal), cob_(std::move(cob)) {} void timeoutExpired() noexcept override { // For now, we just swallow any exceptions that the callback threw. try { cob_(); } catch (const std::exception& ex) { LOG(ERROR) << "TimeoutManager::runAfterDelay() callback threw " << typeid(ex).name() << " exception: " << ex.what(); } catch (...) { LOG(ERROR) << "TimeoutManager::runAfterDelay() callback threw " << "non-exception type"; } // The CobTimeout object was allocated on the heap by runAfterDelay(), // so delete it now that the it has fired. delete this; } private: Func cob_; public: using ListHook = boost::intrusive::list_member_hook< boost::intrusive::link_mode>; ListHook hook; using List = boost::intrusive::list< CobTimeout, boost::intrusive::member_hook, boost::intrusive::constant_time_size>; }; CobTimeout::List list; }; TimeoutManager::TimeoutManager() : cobTimeouts_(std::make_unique()) {} bool TimeoutManager::scheduleTimeoutHighRes( AsyncTimeout* obj, timeout_type_high_res timeout) { timeout_type timeout_ms = folly::chrono::ceil(timeout); return scheduleTimeout(obj, timeout_ms); } void TimeoutManager::runAfterDelay( Func cob, uint32_t milliseconds, InternalEnum internal) { if (!tryRunAfterDelay(std::move(cob), milliseconds, internal)) { folly::throwSystemError( "error in TimeoutManager::runAfterDelay(), failed to schedule timeout"); } } bool TimeoutManager::tryRunAfterDelay( Func cob, uint32_t milliseconds, InternalEnum internal) { if (!cobTimeouts_) { return false; } auto timeout = std::make_unique(this, std::move(cob), internal); if (!timeout->scheduleTimeout(milliseconds)) { return false; } cobTimeouts_->list.push_back(*timeout.release()); return true; } void TimeoutManager::clearCobTimeouts() { if (!cobTimeouts_) { return; } // Delete any unfired callback objects, so that we don't leak memory // Note that we don't fire them. while (!cobTimeouts_->list.empty()) { auto* timeout = &cobTimeouts_->list.front(); delete timeout; } } TimeoutManager::~TimeoutManager() { clearCobTimeouts(); } } // namespace folly