carb/events/IEvents.h

File members: carb/events/IEvents.h

// Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "../IObject.h"
#include "../Interface.h"
#include "../InterfaceUtils.h"
#include "../dictionary/IDictionary.h"

#include <utility>

namespace carb
{

namespace events
{

using EventType = uint64_t;

using SenderId = uint32_t;

constexpr SenderId kGlobalSenderId = 0;

using Order = int32_t;

constexpr Order kDefaultOrder = 0;

class IEvent : public IObject
{
public:
    EventType type;
    SenderId sender;
    dictionary::Item* payload;

    template <typename ValueT>
    void setValues(const std::pair<const char*, ValueT>& param);

    template <typename ValueT, typename... ValuesT>
    void setValues(const std::pair<const char*, ValueT>& param, ValuesT&&... params);

    virtual void consume() = 0;

    virtual void attachObject(const char* name, carb::IObject* ptr) = 0;

    carb::ObjectPtr<carb::IObject> retrieveObject(const char* name)
    {
        // cannot pass ObjectPtr by value across ABI boundaries
        carb::ObjectPtr<carb::IObject> ptr{};
        internalRetrieveObject(name, ptr);
        return ptr;
    }

    virtual void internalRetrieveObject(const char* name, carb::ObjectPtr<carb::IObject>& out) = 0;
};

using IEventPtr = ObjectPtr<IEvent>;

class IEventListener : public IObject
{
public:
    virtual void onEvent(IEvent* e) = 0;
};

using IEventListenerPtr = ObjectPtr<IEventListener>;

class ISubscription : public IObject
{
public:
    virtual void unsubscribe() = 0;

    virtual const char* getName() const noexcept = 0;
};

using ISubscriptionPtr = ObjectPtr<ISubscription>;

#define CARB_EVENTS_TYPE_FROM_STR(STR) CARB_HASH_STRING(STR)

inline EventType typeFromString(const char* str)
{
    return carb::hashString(str);
}

class IEventStream : public IObject
{
public:
    virtual IEvent* createEventPtr(EventType eventType, SenderId sender) = 0;

    template <typename... ValuesT>
    ObjectPtr<IEvent> createEvent(EventType eventType, SenderId sender, ValuesT&&... values);

    virtual void dispatch(IEvent* e) = 0;

    virtual void push(IEvent* e) = 0;

    virtual void pushBlocked(IEvent* e) = 0;

    virtual size_t getCount() = 0;

    ObjectPtr<IEvent> pop();

    virtual IEvent* popPtr() = 0;

    ObjectPtr<IEvent> tryPop();

    virtual IEvent* tryPopPtr() = 0;

    void pump();

    ObjectPtr<ISubscription> createSubscriptionToPop(IEventListener* listener,
                                                     Order order = kDefaultOrder,
                                                     const char* subscriptionName = nullptr);
    ObjectPtr<ISubscription> createSubscriptionToPopByType(EventType eventType,
                                                           IEventListener* listener,
                                                           Order order = kDefaultOrder,
                                                           const char* subscriptionName = nullptr);
    virtual ISubscription* createSubscriptionToPopPtr(IEventListener* listener,
                                                      Order order = kDefaultOrder,
                                                      const char* subscriptionName = nullptr) = 0;
    virtual ISubscription* createSubscriptionToPopByTypePtr(EventType eventType,
                                                            IEventListener* listener,
                                                            Order order = kDefaultOrder,
                                                            const char* subscriptionName = nullptr) = 0;

    ObjectPtr<ISubscription> createSubscriptionToPush(IEventListener* listener,
                                                      Order order = kDefaultOrder,
                                                      const char* subscriptionName = nullptr);
    ObjectPtr<ISubscription> createSubscriptionToPushByType(EventType eventType,
                                                            IEventListener* listener,
                                                            Order order = kDefaultOrder,
                                                            const char* subscriptionName = nullptr);
    virtual ISubscription* createSubscriptionToPushPtr(IEventListener* listener,
                                                       Order order = kDefaultOrder,
                                                       const char* subscriptionName = nullptr) = 0;
    virtual ISubscription* createSubscriptionToPushByTypePtr(EventType eventType,
                                                             IEventListener* listener,
                                                             Order order = kDefaultOrder,
                                                             const char* subscriptionName = nullptr) = 0;

    virtual bool setSubscriptionToPopOrder(const char* subscriptionName, Order order) = 0;
    virtual bool setSubscriptionToPushOrder(const char* subscriptionName, Order order) = 0;

    virtual bool getSubscriptionToPopOrder(const char* subscriptionName, Order* order) = 0;
    virtual bool getSubscriptionToPushOrder(const char* subscriptionName, Order* order) = 0;

    virtual const char* getName() const noexcept = 0;

    //                                                    Helpers                                                     //

    template <typename... ValuesT>
    IEvent* createEventPtr(EventType eventType, SenderId sender, ValuesT&&... values);

    void pushWithSender(EventType eventType, SenderId sender);

    template <typename... ValuesT>
    void pushWithSender(EventType eventType, SenderId sender, ValuesT&&... values);

    template <typename... ValuesT>
    void push(EventType eventType, ValuesT&&... values);

    void pushBlockedWithSender(EventType eventType, SenderId sender);

    template <typename... ValuesT>
    void pushBlockedWithSender(EventType eventType, SenderId sender, ValuesT&&... values);

    template <typename... ValuesT>
    void pushBlocked(EventType eventType, ValuesT&&... values);

    template <typename... ValuesT>
    void dispatch(EventType eventType, SenderId sender, ValuesT&&... values);

    void dispatch(EventType eventType, SenderId sender);
};

using IEventStreamPtr = ObjectPtr<IEventStream>;

struct IEvents
{
    // 1.0 - Initial release
    // 1.1 - added internalCreateEventStream
    // 1.2 - added attachObject() and retrieveObject() to IEvent
    CARB_PLUGIN_INTERFACE("carb::events::IEvents", 1, 2)

    IEventStreamPtr createEventStream(const char* name = nullptr) noexcept;

    CARB_DEPRECATED("Use internalCreateEventStream instead") virtual IEventStream* createEventStreamPtr() = 0;

    virtual SenderId acquireUniqueSenderId() = 0;

    virtual void releaseUniqueSenderId(SenderId senderId) = 0;

    virtual IEventStream* internalCreateEventStream(const char* name) noexcept = 0;
};

//                                                Inline Functions                                                    //

template <typename ValueT>
inline void IEvent::setValues(const std::pair<const char*, ValueT>& param)
{
    carb::getCachedInterface<dictionary::IDictionary>()->makeAtPath<ValueT>(this->payload, param.first, param.second);
}

template <typename ValueT, typename... ValuesT>
inline void IEvent::setValues(const std::pair<const char*, ValueT>& param, ValuesT&&... params)
{
    this->setValues<ValueT>(param);
    this->setValues(std::forward<ValuesT>(params)...);
}

template <typename... ValuesT>
inline IEvent* IEventStream::createEventPtr(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender);
    e->setValues(std::forward<ValuesT>(values)...);
    return e;
}

inline ObjectPtr<ISubscription> IEventStream::createSubscriptionToPop(IEventListener* listener,
                                                                      Order order,
                                                                      const char* name)
{
    return stealObject(this->createSubscriptionToPopPtr(listener, order, name));
}

inline ObjectPtr<ISubscription> IEventStream::createSubscriptionToPopByType(EventType eventType,
                                                                            IEventListener* listener,
                                                                            Order order,
                                                                            const char* name)
{
    return stealObject(this->createSubscriptionToPopByTypePtr(eventType, listener, order, name));
}

inline ObjectPtr<ISubscription> IEventStream::createSubscriptionToPush(IEventListener* listener,
                                                                       Order order,
                                                                       const char* name)
{
    return stealObject(this->createSubscriptionToPushPtr(listener, order, name));
}

inline ObjectPtr<ISubscription> IEventStream::createSubscriptionToPushByType(EventType eventType,
                                                                             IEventListener* listener,
                                                                             Order order,
                                                                             const char* name)
{
    return stealObject(this->createSubscriptionToPushByTypePtr(eventType, listener, order, name));
}

template <typename... ValuesT>
inline void IEventStream::pushWithSender(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender, std::forward<ValuesT>(values)...);
    push(e);
    e->release();
}

inline void IEventStream::pushWithSender(EventType type, SenderId sender)
{
    IEvent* e = createEventPtr(type, sender);
    push(e);
    e->release();
}

template <typename... ValuesT>
inline ObjectPtr<IEvent> IEventStream::createEvent(EventType eventType, SenderId sender, ValuesT&&... values)
{
    return carb::stealObject(this->createEventPtr(eventType, sender, std::forward<ValuesT>(values)...));
}

template <typename... ValuesT>
inline void IEventStream::push(EventType type, ValuesT&&... values)
{
    return pushWithSender(type, kGlobalSenderId, std::forward<ValuesT>(values)...);
}

template <typename... ValuesT>
inline void IEventStream::pushBlockedWithSender(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender, std::forward<ValuesT>(values)...);
    pushBlocked(e);
    e->release();
}

inline void IEventStream::pushBlockedWithSender(EventType type, SenderId sender)
{
    IEvent* e = createEventPtr(type, sender);
    pushBlocked(e);
    e->release();
}

template <typename... ValuesT>
inline void IEventStream::pushBlocked(EventType type, ValuesT&&... values)
{
    return pushBlockedWithSender(type, kGlobalSenderId, std::forward<ValuesT>(values)...);
}

template <typename... ValuesT>
inline void IEventStream::dispatch(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender, std::forward<ValuesT>(values)...);
    dispatch(e);
    e->release();
}

inline void IEventStream::dispatch(EventType type, SenderId sender)
{
    IEvent* e = createEventPtr(type, sender);
    dispatch(e);
    e->release();
}

inline void IEventStream::pump()
{
    const size_t eventCount = this->getCount();
    for (size_t i = 0; i < eventCount; i++)
    {
        IEvent* e = this->tryPopPtr();
        if (!e)
            break;
        e->release();
    }
}

inline ObjectPtr<IEvent> IEventStream::pop()
{
    return carb::stealObject(this->popPtr());
}

inline ObjectPtr<IEvent> IEventStream::tryPop()
{
    return carb::stealObject(this->tryPopPtr());
}

inline ObjectPtr<IEventStream> IEvents::createEventStream(const char* name) noexcept
{
    return carb::stealObject(this->internalCreateEventStream(name));
}

} // namespace events
} // namespace carb