/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include #include #include #define FRIEND_TEST(test_case_name, test_name) friend class test_case_name##_##test_name##_Test namespace facebook::react { class EventSubscription { public: explicit EventSubscription(std::function remove) : remove_(std::move(remove)) {} ~EventSubscription() = default; EventSubscription(EventSubscription &&) noexcept = default; EventSubscription &operator=(EventSubscription &&) noexcept = default; EventSubscription(const EventSubscription &) = delete; EventSubscription &operator=(const EventSubscription &) = delete; void remove() { remove_(); } private: friend Bridging; std::function remove_; }; template <> struct Bridging { static EventSubscription fromJs(jsi::Runtime &rt, const jsi::Object &value, const std::shared_ptr &jsInvoker) { auto listener = bridging::fromJs>(rt, value.getProperty(rt, "remove"), jsInvoker); return EventSubscription([listener = std::move(listener)]() mutable { listener(); }); } static jsi::Object toJs(jsi::Runtime &rt, const EventSubscription &eventSubscription, const std::shared_ptr &jsInvoker) { auto result = jsi::Object(rt); result.setProperty(rt, "remove", bridging::toJs(rt, eventSubscription.remove_, jsInvoker)); return result; } }; class IAsyncEventEmitter { public: IAsyncEventEmitter() noexcept = default; virtual ~IAsyncEventEmitter() noexcept = default; IAsyncEventEmitter(IAsyncEventEmitter &&) noexcept = default; IAsyncEventEmitter &operator=(IAsyncEventEmitter &&) noexcept = default; IAsyncEventEmitter(const IAsyncEventEmitter &) = delete; IAsyncEventEmitter &operator=(const IAsyncEventEmitter &) = delete; virtual jsi::Object get(jsi::Runtime &rt, const std::shared_ptr &jsInvoker) const = 0; }; template class AsyncEventEmitter : public IAsyncEventEmitter { static_assert(sizeof...(Args) <= 1, "AsyncEventEmitter must have at most one argument"); public: AsyncEventEmitter() : state_(std::make_shared()) { listen_ = [state = state_](AsyncCallback listener) { std::lock_guard lock(state->mutex); auto listenerId = state->listenerId++; state->listeners.emplace(listenerId, std::move(listener)); return EventSubscription([state, listenerId]() { std::lock_guard innerLock(state->mutex); state->listeners.erase(listenerId); }); }; } ~AsyncEventEmitter() override = default; AsyncEventEmitter(AsyncEventEmitter &&) noexcept = default; AsyncEventEmitter &operator=(AsyncEventEmitter &&) noexcept = default; AsyncEventEmitter(const AsyncEventEmitter &) = delete; AsyncEventEmitter &operator=(const AsyncEventEmitter &) = delete; void emit(std::function &&converter) { std::lock_guard lock(state_->mutex); for (auto &[_, listener] : state_->listeners) { listener.call([converter](jsi::Runtime &rt, jsi::Function &jsFunction) { jsFunction.call(rt, converter(rt)); }); } } void emit(Args... value) { std::lock_guard lock(state_->mutex); for (const auto &[_, listener] : state_->listeners) { listener.call(static_cast(value)...); } } jsi::Object get(jsi::Runtime &rt, const std::shared_ptr &jsInvoker) const override { return bridging::toJs(rt, listen_, jsInvoker); } private: friend Bridging; FRIEND_TEST(BridgingTest, eventEmitterTest); struct SharedState { std::mutex mutex; std::unordered_map> listeners; size_t listenerId{}; }; std::function)> listen_; std::shared_ptr state_; }; template struct Bridging> { static jsi::Object toJs(jsi::Runtime &rt, const AsyncEventEmitter &eventEmitter, const std::shared_ptr &jsInvoker) { return eventEmitter.get(rt, jsInvoker); } }; } // namespace facebook::react