Use atomic list for event subscribers
Summary: Replace the *copy on write* vector with an atomic pointer to a linked list. This allows to publish without locking a mutex, at the cost of the slower traversal of a linked list (a vector has better locality). At the moment, the typical use case is to have one subscriber, meaning that the afforementioned slower traversal is not a problem. Adding subscribers is implemented as atomic *compare and swap.* Reviewed By: SidharthGuglani Differential Revision: D15546964 fbshipit-source-id: 41bfa41f1ac6be5c9b6bf4288ea3271ee995877e
This commit is contained in:
committed by
Facebook Github Bot
parent
cea3865c74
commit
f304990656
@@ -5,51 +5,57 @@
|
|||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace yoga {
|
namespace yoga {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::mutex& eventSubscribersMutex() {
|
struct Node {
|
||||||
static std::mutex subscribersMutex;
|
std::function<Event::Subscriber> subscriber = nullptr;
|
||||||
return subscribersMutex;
|
Node* next = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Event::Subscribers>& eventSubscribers() {
|
Node(std::function<Event::Subscriber>&& subscriber)
|
||||||
static auto subscribers = std::make_shared<Event::Subscribers>();
|
: subscriber{std::move(subscriber)} {}
|
||||||
return subscribers;
|
};
|
||||||
|
|
||||||
|
std::atomic<Node*> subscribers{nullptr};
|
||||||
|
|
||||||
|
Node* push(Node* newHead) {
|
||||||
|
Node* oldHead;
|
||||||
|
do {
|
||||||
|
oldHead = subscribers.load(std::memory_order_relaxed);
|
||||||
|
if (newHead != nullptr) {
|
||||||
|
newHead->next = oldHead;
|
||||||
|
}
|
||||||
|
} while (!subscribers.compare_exchange_weak(
|
||||||
|
oldHead, newHead, std::memory_order_release, std::memory_order_relaxed));
|
||||||
|
return oldHead;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void Event::reset() {
|
void Event::reset() {
|
||||||
eventSubscribers() = std::make_shared<Event::Subscribers>();
|
auto head = push(nullptr);
|
||||||
|
while (head != nullptr) {
|
||||||
|
auto current = head;
|
||||||
|
head = head->next;
|
||||||
|
delete current;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Event::subscribe(std::function<Subscriber>&& subscriber) {
|
void Event::subscribe(std::function<Subscriber>&& subscriber) {
|
||||||
std::lock_guard<std::mutex> guard(eventSubscribersMutex());
|
push(new Node{std::move(subscriber)});
|
||||||
eventSubscribers() =
|
|
||||||
std::make_shared<Event::Subscribers>(*eventSubscribers());
|
|
||||||
eventSubscribers()->push_back(subscriber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Event::publish(const YGNode& node, Type eventType, const Data& eventData) {
|
void Event::publish(const YGNode& node, Type eventType, const Data& eventData) {
|
||||||
std::shared_ptr<Event::Subscribers> subscribers;
|
for (auto subscriber = subscribers.load(std::memory_order_relaxed);
|
||||||
{
|
subscriber != nullptr;
|
||||||
std::lock_guard<std::mutex> guard(eventSubscribersMutex());
|
subscriber = subscriber->next) {
|
||||||
subscribers = eventSubscribers();
|
subscriber->subscriber(node, eventType, eventData);
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& subscriber : *subscribers) {
|
|
||||||
if (subscriber) {
|
|
||||||
subscriber(node, eventType, eventData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user