/* * 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 namespace folly { SerialExecutor::SerialExecutor(KeepAlive parent) : parent_(std::move(parent)) {} SerialExecutor::~SerialExecutor() { DCHECK(!keepAliveCounter_); } Executor::KeepAlive SerialExecutor::create( KeepAlive parent) { return makeKeepAlive(new SerialExecutor(std::move(parent))); } SerialExecutor::UniquePtr SerialExecutor::createUnique( std::shared_ptr parent) { auto executor = new SerialExecutor(getKeepAliveToken(parent.get())); return {executor, Deleter{std::move(parent)}}; } bool SerialExecutor::keepAliveAcquire() { auto keepAliveCounter = keepAliveCounter_.fetch_add(1, std::memory_order_relaxed); DCHECK(keepAliveCounter > 0); return true; } void SerialExecutor::keepAliveRelease() { auto keepAliveCounter = keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel); DCHECK(keepAliveCounter > 0); if (keepAliveCounter == 1) { delete this; } } void SerialExecutor::add(Func func) { queue_.enqueue(Task{std::move(func), RequestContext::saveContext()}); parent_->add([keepAlive = getKeepAliveToken(this)] { keepAlive->run(); }); } void SerialExecutor::addWithPriority(Func func, int8_t priority) { queue_.enqueue(Task{std::move(func), RequestContext::saveContext()}); parent_->addWithPriority( [keepAlive = getKeepAliveToken(this)] { keepAlive->run(); }, priority); } void SerialExecutor::run() { // We want scheduled_ to guard side-effects of completed tasks, so we can't // use std::memory_order_relaxed here. if (scheduled_.fetch_add(1, std::memory_order_acquire) > 0) { return; } do { Task task; queue_.dequeue(task); try { folly::RequestContextScopeGuard ctxGuard(std::move(task.ctx)); auto func = std::move(task.func); func(); } catch (std::exception const& ex) { LOG(ERROR) << "SerialExecutor: func threw unhandled exception " << folly::exceptionStr(ex); } catch (...) { LOG(ERROR) << "SerialExecutor: func threw unhandled non-exception " "object"; } // We want scheduled_ to guard side-effects of completed tasks, so we can't // use std::memory_order_relaxed here. } while (scheduled_.fetch_sub(1, std::memory_order_release) > 1); } } // namespace folly