unioil-loyalty-rn-app/ios/Pods/Flipper-Folly/folly/io/async/EventBaseAtomicNotification...

198 lines
5.8 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 <folly/io/async/AtomicNotificationQueue.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/EventHandler.h>
#include <folly/portability/Fcntl.h>
#include <folly/portability/Sockets.h>
#include <folly/portability/Unistd.h>
#if defined(__linux__) && !defined(__ANDROID__)
#define FOLLY_HAVE_EVENTFD
#include <folly/io/async/EventFDWrapper.h>
#endif
namespace folly {
/**
* A producer-consumer queue for passing tasks to EventBase thread.
*
* Tasks can be added to the queue from any thread. A single EventBase
* thread can be listening to the queue. Tasks are processed in FIFO order.
*/
template <typename Task, typename Consumer>
class EventBaseAtomicNotificationQueue : private EventBase::LoopCallback,
private EventHandler {
static_assert(
noexcept(std::declval<Consumer>()(std::declval<Task&&>())),
"Consumer::operator()(Task&&) should be noexcept.");
public:
explicit EventBaseAtomicNotificationQueue(Consumer&& consumer);
template <
typename C = Consumer,
typename = std::enable_if_t<std::is_default_constructible<C>::value>>
EventBaseAtomicNotificationQueue()
: EventBaseAtomicNotificationQueue(Consumer()) {}
~EventBaseAtomicNotificationQueue() override;
/*
* Set the maximum number of tasks processed in a single round.
* Can be called from consumer thread only.
*/
void setMaxReadAtOnce(uint32_t maxAtOnce);
/*
* Returns the number of tasks in the queue.
* Can be called from any thread.
*/
size_t size() const;
/*
* Checks if the queue is empty.
* Can be called from consumer thread only.
*/
bool empty() const;
/*
* Executes all tasks until the queue is empty.
* Can be called from consumer thread only.
*/
void drain();
/*
* Adds a task into the queue.
* Can be called from any thread.
*/
template <typename... Args>
void putMessage(Args&&... task);
/**
* Adds a task into the queue unless the max queue size is reached.
* Returns true iff the task was queued.
* Can be called from any thread.
*/
FOLLY_NODISCARD bool tryPutMessage(Task&& task, uint32_t maxSize);
/*
* Detaches the queue from an EventBase.
* Can be called from consumer thread only.
*/
void stopConsuming();
/*
* Attaches the queue to an EventBase.
* Can be called from consumer thread only.
*/
void startConsuming(EventBase* evb);
/*
* Attaches the queue to an EventBase.
* Can be called from consumer thread only.
*
* Unlike startConsuming, startConsumingInternal registers this queue as
* an internal event. This means that this event may be skipped if
* EventBase doesn't have any other registered events. This generally should
* only be used for queues managed by an EventBase itself.
*/
void startConsumingInternal(EventBase* evb);
/*
* Executes one round of tasks. Re-activates the event if more tasks are
* available.
* Can be called from consumer thread only.
*/
void execute();
private:
/*
* Adds a task to the queue without incrementing the push count.
*/
template <typename T>
void putMessageImpl(T&& task);
template <typename T>
bool drive(T&& t);
/*
* Write into the signal fd to wake up the consumer thread.
*/
void notifyFd();
/*
* Read all messages from the signal fd.
*/
void drainFd();
/*
* Either arm the queue or reactivate the EventBase event.
* This has to be a loop callback because the event can't be activated from
* within the event callback. It also allows delayed re-arming the queue.
*/
void runLoopCallback() noexcept override;
void startConsumingImpl(EventBase* evb, bool internal);
void handlerReady(uint16_t) noexcept override;
void activateEvent();
/**
* Check that the AtomicNotificationQueue is being used from the correct
* process.
*
* If you create a AtomicNotificationQueue in one process, then fork, and try
* to send messages to the queue from the child process, you're going to have
* a bad time. Unfortunately users have (accidentally) run into this.
*
* Because we use an eventfd/pipe, the child process can actually signal the
* parent process that an event is ready. However, it can't put anything on
* the parent's queue, so the parent wakes up and finds an empty queue. This
* check ensures that we catch the problem in the misbehaving child process
* code, and crash before signalling the parent process.
*/
void checkPid() const;
[[noreturn]] FOLLY_NOINLINE void checkPidFail() const;
int eventfd_{-1};
int pipeFds_[2]{-1, -1}; // to fallback to on older/non-linux systems
/*
* If event is registered with the EventBase, this describes whether
* edge-triggered flag was set for it. For edge-triggered events we don't
* need to drain the fd to deactivate them.
*/
EventBase* evb_{nullptr};
const pid_t pid_;
AtomicNotificationQueue<Task> notificationQueue_;
Consumer consumer_;
ssize_t successfulArmCount_{0};
ssize_t consumerDisarmedCount_{0};
ssize_t writesObserved_{0};
ssize_t writesLocal_{0};
bool armed_{false};
bool edgeTriggeredSet_{false};
};
} // namespace folly
#include <folly/io/async/EventBaseAtomicNotificationQueue-inl.h>