carb/tasking/TaskingTypes.h

File members: carb/tasking/TaskingTypes.h

// Copyright (c) 2020-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 "../Defines.h"

namespace carb
{
namespace tasking
{

class Counter DOXYGEN_EMPTY_CLASS;

class Mutex DOXYGEN_EMPTY_CLASS;

class Semaphore DOXYGEN_EMPTY_CLASS;

class SharedMutex DOXYGEN_EMPTY_CLASS;

class ConditionVariable DOXYGEN_EMPTY_CLASS;

struct ITasking;

constexpr uint64_t kInfinite = uint64_t(-1);

enum class Priority
{
    eLow,
    eMedium,
    eHigh,

    eMain,

    eCount,

    // Aliases
    eDefault = eMedium,
};

enum class ObjectType
{
    eNone,
    eCounter,
    eTaskContext,
    ePtrTaskContext,
    eTaskGroup,
    eSharedState,
    eFutex1,
    eFutex2,
    eFutex4,
    eFutex8,
    eTrackerGroup,
    eTaskName,
    eTaskNameLiteral,
};

using OnTaskFn = void (*)(void* taskArg);

using ApplyFn = void (*)(size_t index, void* taskArg);

using ApplyBatchFn = void (*)(size_t startIndex, size_t endIndex, void* taskArg);

using TaskStorageDestructorFn = void (*)(void* arg);

using TaskStorageKey = size_t;

constexpr TaskStorageKey kInvalidTaskStorageKey = size_t(-1);

using TaskContext = size_t;

constexpr TaskContext kInvalidTaskContext = 0;

constexpr uint32_t kMaxFibers = 1048575;

struct Object
{
    ObjectType type;
    void* data;
};

struct TaskDesc
{
    size_t size{ sizeof(TaskDesc) };

    OnTaskFn task;

    void* taskArg;

    Priority priority;

    Object requiredObject;

    Semaphore* waitSemaphore;

    OnTaskFn cancel;

    // Internal only
    Object const* trackers{ nullptr };
    size_t numTrackers{ 0 };

    constexpr TaskDesc(OnTaskFn task_ = nullptr,
                       void* taskArg_ = nullptr,
                       Priority priority_ = Priority::eLow,
                       Counter* requiredCounter_ = nullptr,
                       Semaphore* waitSemaphore_ = nullptr,
                       OnTaskFn cancel_ = nullptr)
        : task(task_),
          taskArg(taskArg_),
          priority(priority_),
          requiredObject{ ObjectType::eCounter, requiredCounter_ },
          waitSemaphore(waitSemaphore_),
          cancel(cancel_)
    {
    }
};

struct TaskingDesc
{
    uint32_t fiberCount;

    uint32_t threadCount;

    uint32_t* threadAffinity;

    uint64_t stackSize;
};

enum class TaskDebugState
{
    Pending,
    New,
    Running,
    Waiting,
    Finished,
};

struct TaskDebugInfo
{
    size_t sizeOf{ sizeof(TaskDebugInfo) };

    TaskContext context{};

    TaskDebugState state{};

    OnTaskFn task{};

    void* taskArg{};

    size_t numCreationFrames{ 0 };

    void** creationCallstack{ nullptr };

    size_t numWaitingFrames{ 0 };

    void** waitingCallstack{ nullptr };

    size_t taskNameSize{ 0 };

    char* taskName{ nullptr };
};

using TaskDebugInfoFn = bool (*)(const TaskDebugInfo& info, void* context);

#ifndef DOXYGEN_BUILD
namespace detail
{

template <class T>
struct GenerateFuture;

template <class T>
class SharedState;

} // namespace detail

struct Trackers;
struct RequiredObject;

template <class T>
class Promise;
template <class T>
class SharedFuture;
#endif

template <class T = void>
class Future
{
public:
    constexpr Future() noexcept = default;

    ~Future();

    Future(Future&& rhs) noexcept;

    Future& operator=(Future&& rhs) noexcept;

    bool valid() const noexcept;

    bool try_wait() const;

    void wait() const;

    template <class Rep, class Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& dur) const;

    template <class Clock, class Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& when) const;

    T get();

    bool isCanceled() const;

    SharedFuture<T> share();

    const TaskContext* task_if() const;

    operator RequiredObject() const;

    template <class Callable, class... Args>
    auto then(Priority prio, Trackers&& trackers, Callable&& f, Args&&... args);

private:
    template <class U>
    friend struct detail::GenerateFuture;
    template <class U>
    friend class Promise;
    template <class U>
    friend class SharedFuture;
    CARB_PREVENT_COPY(Future);

    constexpr Future(detail::SharedState<T>* state) noexcept;
    Future(TaskContext task, detail::SharedState<T>* state) noexcept;

    detail::SharedState<T>* m_state{ nullptr };
};

#ifndef DOXYGEN_BUILD
template <>
class Future<void>
{
public:
    constexpr Future() noexcept = default;
    ~Future();

    Future(Future&& rhs) noexcept;
    Future& operator=(Future&& rhs) noexcept;

    bool valid() const noexcept;

    bool try_wait() const;
    void wait() const;
    template <class Rep, class Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& dur) const;
    template <class Clock, class Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& when) const;

    void get();
    SharedFuture<void> share();

    const TaskContext* task_if() const;

    operator RequiredObject() const;

    template <class Callable, class... Args>
    auto then(Priority prio, Trackers&& trackers, Callable&& f, Args&&... args);

private:
    template <class U>
    friend struct detail::GenerateFuture;
    template <class U>
    friend class Future;
    template <class U>
    friend class Promise;
    template <class U>
    friend class SharedFuture;
    friend struct Tracker;

    TaskContext* ptask();

    detail::SharedState<void>* state() const noexcept;

    Future(TaskContext task);
    Future(detail::SharedState<void>* state);

    Object m_obj{ ObjectType::eNone, nullptr };
};
#endif

template <class T = void>
class SharedFuture
{
public:
    SharedFuture() noexcept = default;

    SharedFuture(const SharedFuture<T>& other) noexcept;

    SharedFuture(SharedFuture<T>&& other) noexcept;

    SharedFuture(Future<T>&& fut) noexcept;

    ~SharedFuture();

    SharedFuture<T>& operator=(const SharedFuture<T>& other);

    SharedFuture<T>& operator=(SharedFuture<T>&& other) noexcept;

    const T& get() const;

    bool valid() const noexcept;

    bool try_wait() const;

    void wait() const;

    template <class Rep, class Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& dur) const;

    template <class Clock, class Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& when) const;

    bool isCanceled() const;

    operator RequiredObject() const;

    const TaskContext* task_if() const;

    template <class Callable, class... Args>
    auto then(Priority prio, Trackers&& trackers, Callable&& f, Args&&... args);

private:
    detail::SharedState<T>* m_state{ nullptr };
};

#ifndef DOXYGEN_BUILD
template <class T>
class SharedFuture<T&>
{
public:
    constexpr SharedFuture() noexcept = default;
    SharedFuture(const SharedFuture& other) noexcept;
    SharedFuture(SharedFuture&& other) noexcept;
    SharedFuture(Future<T&>&& fut) noexcept;
    ~SharedFuture();

    SharedFuture& operator=(const SharedFuture& other);
    SharedFuture& operator=(SharedFuture&& other) noexcept;

    T& get() const;
    bool valid() const noexcept;

    bool try_wait() const;
    void wait() const;
    template <class Rep, class Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& dur) const;
    template <class Clock, class Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& when) const;

    bool isCanceled() const;

    operator RequiredObject() const;

    const TaskContext* task_if() const;

    template <class Callable, class... Args>
    auto then(Priority prio, Trackers&& trackers, Callable&& f, Args&&... args);

private:
    detail::SharedState<T&>* m_state{ nullptr };
};

template <>
class SharedFuture<void>
{
public:
    constexpr SharedFuture() noexcept = default;
    SharedFuture(const SharedFuture<void>& other) noexcept;
    SharedFuture(SharedFuture<void>&& other) noexcept;
    SharedFuture(Future<void>&& fut) noexcept;
    ~SharedFuture();

    SharedFuture<void>& operator=(const SharedFuture<void>& other);
    SharedFuture<void>& operator=(SharedFuture<void>&& other) noexcept;

    void get() const;
    bool valid() const noexcept;

    bool try_wait() const;
    void wait() const;
    template <class Rep, class Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& dur) const;
    template <class Clock, class Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& when) const;
    operator RequiredObject() const;

    const TaskContext* task_if() const;

    template <class Callable, class... Args>
    auto then(Priority prio, Trackers&& trackers, Callable&& f, Args&&... args);

private:
    friend struct Tracker;
    TaskContext* ptask();
    detail::SharedState<void>* state() const;
    Object m_obj{ ObjectType::eNone, nullptr };
};
#endif

template <class T = void>
class Promise
{
    CARB_PREVENT_COPY(Promise);

public:
    Promise();

    Promise(Promise&& other) noexcept;

    ~Promise();

    Promise& operator=(Promise&& other) noexcept;

    void swap(Promise& other) noexcept;

    Future<T> get_future();

    void set_value(const T& value);

    void set_value(T&& value);

    void setCanceled();

private:
    using State = detail::SharedState<T>;
    State* m_state{ nullptr };
};

#ifndef DOXYGEN_BUILD
template <class T>
class Promise<T&>
{
    CARB_PREVENT_COPY(Promise);

public:
    Promise();
    Promise(Promise&& other) noexcept;

    ~Promise();

    Promise& operator=(Promise&& other) noexcept;

    void swap(Promise& other) noexcept;

    Future<T&> get_future();

    void set_value(T& value);
    void setCanceled();

private:
    using State = detail::SharedState<T&>;
    State* m_state{ nullptr };
};

template <>
class Promise<void>
{
    CARB_PREVENT_COPY(Promise);

public:
    Promise();
    Promise(Promise&& other) noexcept;

    ~Promise();

    Promise& operator=(Promise&& other) noexcept;

    void swap(Promise& other) noexcept;

    Future<void> get_future();

    void set_value();

private:
    using State = detail::SharedState<void>;
    State* m_state{ nullptr };
};
#endif

} // namespace tasking
} // namespace carb