105 lines
3.6 KiB
C++
105 lines
3.6 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <atomic>
|
|
|
|
#include <folly/Portability.h>
|
|
#include <folly/executors/IOExecutor.h>
|
|
#include <folly/executors/ThreadPoolExecutor.h>
|
|
#include <folly/io/async/EventBaseManager.h>
|
|
|
|
namespace folly {
|
|
|
|
FOLLY_PUSH_WARNING
|
|
// Suppress "IOThreadPoolExecutor inherits DefaultKeepAliveExecutor
|
|
// keepAliveAcquire/keepAliveRelease via dominance"
|
|
FOLLY_MSVC_DISABLE_WARNING(4250)
|
|
|
|
/**
|
|
* A Thread Pool for IO bound tasks
|
|
*
|
|
* @note Uses event_fd for notification, and waking an epoll loop.
|
|
* There is one queue (NotificationQueue specifically) per thread/epoll.
|
|
* If the thread is already running and not waiting on epoll,
|
|
* we don't make any additional syscalls to wake up the loop,
|
|
* just put the new task in the queue.
|
|
* If any thread has been waiting for more than a few seconds,
|
|
* its stack is madvised away. Currently however tasks are scheduled round
|
|
* robin on the queues, so unless there is no work going on,
|
|
* this isn't very effective.
|
|
* Since there is one queue per thread, there is hardly any contention
|
|
* on the queues - so a simple spinlock around an std::deque is used for
|
|
* the tasks. There is no max queue size.
|
|
* By default, there is one thread per core - it usually doesn't make sense to
|
|
* have more IO threads than this, assuming they don't block.
|
|
*
|
|
* @note ::getEventBase() will return an EventBase you can schedule IO work on
|
|
* directly, chosen round-robin.
|
|
*
|
|
* @note N.B. For this thread pool, stop() behaves like join() because
|
|
* outstanding tasks belong to the event base and will be executed upon its
|
|
* destruction.
|
|
*/
|
|
class IOThreadPoolExecutor : public ThreadPoolExecutor, public IOExecutor {
|
|
public:
|
|
explicit IOThreadPoolExecutor(
|
|
size_t numThreads,
|
|
std::shared_ptr<ThreadFactory> threadFactory =
|
|
std::make_shared<NamedThreadFactory>("IOThreadPool"),
|
|
folly::EventBaseManager* ebm = folly::EventBaseManager::get(),
|
|
bool waitForAll = false);
|
|
|
|
~IOThreadPoolExecutor() override;
|
|
|
|
void add(Func func) override;
|
|
void add(
|
|
Func func,
|
|
std::chrono::milliseconds expiration,
|
|
Func expireCallback = nullptr) override;
|
|
|
|
folly::EventBase* getEventBase() override;
|
|
|
|
static folly::EventBase* getEventBase(ThreadPoolExecutor::ThreadHandle*);
|
|
|
|
folly::EventBaseManager* getEventBaseManager();
|
|
|
|
private:
|
|
struct alignas(folly::cacheline_align_v) IOThread : public Thread {
|
|
IOThread(IOThreadPoolExecutor* pool)
|
|
: Thread(pool), shouldRun(true), pendingTasks(0) {}
|
|
std::atomic<bool> shouldRun;
|
|
std::atomic<size_t> pendingTasks;
|
|
folly::EventBase* eventBase;
|
|
std::mutex eventBaseShutdownMutex_;
|
|
};
|
|
|
|
ThreadPtr makeThread() override;
|
|
std::shared_ptr<IOThread> pickThread();
|
|
void threadRun(ThreadPtr thread) override;
|
|
void stopThreads(size_t n) override;
|
|
size_t getPendingTaskCountImpl() const override final;
|
|
|
|
std::atomic<size_t> nextThread_;
|
|
folly::ThreadLocal<std::shared_ptr<IOThread>> thisThread_;
|
|
folly::EventBaseManager* eventBaseManager_;
|
|
};
|
|
|
|
FOLLY_POP_WARNING
|
|
|
|
} // namespace folly
|