/* * Copyright (c) Meta Platforms, Inc. and 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. */ #pragma once #include #include #include #include #include namespace folly { // Callback object for HHWheelTimer template struct WTCallback : public TBase::Callback { struct PrivateConstructorTag {}; public: explicit WTCallback(PrivateConstructorTag) {} // Only allow creation by this factory, to ensure heap allocation. static std::pair, SemiFuture> create( EventBase* base) { // optimization opportunity: memory pool auto cob = std::make_shared(PrivateConstructorTag{}); auto& state = cob->state_.unsafeGetUnlocked().emplace(State{base, {}}); // Capture shared_ptr of cob in lambda so that Core inside Promise will // hold a ref count to it. The ref count will be released when Core goes // away which happens when both Promise and Future go away state.promise.setInterruptHandler([cob](exception_wrapper ew) mutable { interruptHandler(std::move(cob), std::move(ew)); }); return {std::move(cob), state.promise.getSemiFuture()}; } protected: struct State { EventBase* base; Promise promise; }; // First thread that can fulfill the promise unsets the state, breaking the // circular reference WTCallback -> promise -> core -> WTCallback. folly::Synchronized> state_; void timeoutExpired() noexcept override { if (auto state = state_.exchange({})) { state->promise.setValue(); } } void callbackCanceled() noexcept override { if (auto state = state_.exchange({})) { state->promise.setException(FutureNoTimekeeper{}); } } static void interruptHandler( std::shared_ptr self, exception_wrapper ew) { // Hold the lock while scheduling the callback, so that callbackCanceled() // blocks the timekeeper destructor keeping the base pointer valid. auto wState = self->state_.wlock(); if (!*wState) { return; } auto state = std::exchange(*wState, {}); auto* base = state->base; base->runInEventBaseThreadAlwaysEnqueue( [self, state = std::move(state), ew = std::move(ew)]() mutable { self->cancelTimeout(); state->promise.setException(std::move(ew)); }); } }; } // namespace folly