/* * 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; private: friend Bridging; std::function remove_; }; template <> struct Bridging { 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