Notifications

Being a fiber-based system, things like profiling may need to know additional context about what is running on a thread. To facilitate this, carb.tasking.plugin will load all instances of a special notification interface: IFiberEvents.

This notification interface will be called by carb.tasking.plugin on worker threads when they start and stop running fibers. Consider the use case of a profiler: the profiler took a timestamp at the beginning of an operation. If the thread that was running that operation swaps to a different fiber, the profiler would need to know that. Likewise, if the fiber was resumed on a different thread, the profiler would have to keep track of that as well. By paying attention to these events, when the operation completes (possibly on a different thread), the profiler can be aware even if the fiber changed threads.

Note

carb.tasking.plugin only gathers carb::tasking::IFiberEvents interfaces at startup, or when carb::tasking::ITasking::changeParameters is called, or when carb::tasking::ITasking::reloadFiberEvents is called. If you register a plugin that exports the carb::tasking::IFiberEvents interface, perform one of these actions in order to ensure that you receive notifications.

How it works

Whenever a carb.tasking thread starts running a fiber, the carb::tasking::IFiberEvents::notifyFiberStart function is called within the context of the task thread. When the fiber yields execution back to the thread, the carb::tasking::IFiberEvents::notifyFiberStop function is called within the context of the task thread.

Implementing IFiberEvents

Your plugin can implement the IFiberEvents interface easily. The first step is using the CARB_PLUGIN_IMPL macro:

#include <carb/Defines.h>

#include <carb/PluginUtils.h>
#include <carb/tasking/IFiberEvents.h>

#define PLUGIN_IMPL_NAME "carb.fibereventtest.plugin"
const struct carb::PluginImplDesc kPluginImpl = { PLUGIN_IMPL_NAME, "FiberEvent test", "NVIDIA",
                                                  carb::PluginHotReload::eDisabled, "dev" };
CARB_PLUGIN_IMPL_EX(kPluginImpl, carb::tasking::IFiberEvents, carb::fibereventtest::FiberEventTest)
CARB_PLUGIN_IMPL_NO_DEPS()

Then implement a fillInterface function:

template <>
bool fillInterface<carb::tasking::IFiberEvents>(carb::Version* ver, void* iface)
{
    using namespace carb::fibereventtest;
    // clang-format off
    new (iface) carb::tasking::IFiberEvents 
    {
        notifyFiberStart,
        notifyFiberStop,
    };
    // clang-format on
    return true;
}

A common usage pattern is to track the fiber ID as thread-local data, or to report an event to the profiler.

This example merely counts the start and stop events for testing purposes:

void notifyFiberStart(const uint64_t fiberId)
{
    startEvents.fetch_add(1, std::memory_order_release);
}

void notifyFiberStop(const uint64_t fiberId)
{
    stopEvents.fetch_add(1, std::memory_order_release);
}

Attention

Keep your notification functions as fast as possible because they are called in the context of the carb.tasking worker threads.

Warning

Your notification functions will likely be called from several carb.tasking worker threads simultaneously. You are required to ensure thread safety!