Central Object Registry in bpftime: handler_manager.hpp Implementation Guide
The central object registry in bpftime is the handler_manager class located in runtime/src/handler/handler_manager.hpp, which maintains a shared-memory vector of all eBPF objects (maps, programs, links, and perf events) accessible across multiple processes by file-descriptor-like identifiers.
The bpftime runtime requires a unified mechanism to track eBPF objects across process boundaries. The central object registry solves this by storing maps, programs, links, and perf events in a single shared-memory data structure. This registry acts as the single source of truth for object lifecycle management, enabling the daemon, agents, and user programs to access eBPF resources consistently.
What Is the bpftime Central Object Registry?
The central object registry is a shared-memory-backed index that assigns integer identifiers (similar to file descriptors) to every eBPF object created within the bpftime ecosystem. Unlike traditional in-process registries, this design allows multiple independent processes to reference the same eBPF map or program without duplicating kernel state.
According to the bpftime source code, the registry implementation resides in runtime/src/handler/handler_manager.hpp. The handler_manager class declared at lines 95-127 encapsulates a handler_variant_vector handlers member that stores all object instances in a boost::interprocess managed vector. This placement in shared memory makes the registry accessible to the daemon, agents, and user applications simultaneously.
handler_manager.hpp Architecture
The handler_manager Class (Lines 95-127)
The core registry logic centers on the handler_manager class definition spanning lines 95-127. This class encapsulates the handlers vector and provides type-safe access to heterogeneous eBPF objects through a variant-based design.
Key design elements include:
- Indexed Storage: Objects reside in a
vector<handler_variant>indexed by integer IDs that mimic Unix file descriptors - Process-Agnostic: The backing storage uses
boost::interprocess::managed_shared_memoryrather than process-local heap memory - Lifecycle Control: Methods like
set_handler,clear_id_at, andclear_allmanage object insertion and removal atomically
Shared Memory Integration
The registry leverages Boost.Interprocess to place the handlers vector inside a named shared memory segment. When initializing the manager, the code opens or creates a managed_shared_memory object using bpftime::get_global_shm_name() as the segment identifier.
This architecture ensures that when one process creates a BPF map via the registry, another process can immediately look up that same map using its assigned identifier. The shared memory segment persists independently of any single process lifetime, allowing the daemon to outlive short-lived agent processes.
Handler Type Variants (Lines 84-88)
Lines 84-88 define the handler_variant type alias that enumerates all storable eBPF objects:
using handler_variant = boost::variant<
bpf_map_handler,
bpf_link_handler,
bpf_prog_handler,
bpf_perf_event_handler
// ... additional handler types
>;
This variant type enables the heterogeneous storage of maps, programs, links, and perf events within a single homogeneous vector. When retrieving an object, code uses std::visit or boost::get to dispatch to the concrete handler type.
Core Registry Operations
Allocating and Registering Objects
The registry provides two primary methods for inserting objects:
set_handler(int idx, handler_variant &&handler): Places a handler at a specific index, overwriting any existing entryset_handler_at_empty_slot(handler_variant &&handler, managed_shared_memory &shm): Automatically finds the first unallocated slot and returns its index (the pseudo-file-descriptor)
The set_handler_at_empty_slot method is the preferred entry point for new registrations because it mimics the kernel's behavior of returning the lowest available file descriptor number.
Object Retrieval by File Descriptor
To access stored objects, the registry exposes:
const handler_variant &get_handler(int idx) const: Returns a const reference to the handler at the specified indexhandler_variant &operator[](int idx): Provides mutable access with bounds checkingbool is_allocated(int idx) const: Verifies whether a slot contains a valid object before access
These methods enable safe lookup patterns where agents validate an ID's existence via is_allocated() before dereferencing through get_handler().
Registry Cleanup and Management
The manager supports granular and bulk deletion:
void clear_id_at(int idx, managed_shared_memory &shm): Removes a single handler and frees its shared memory resourcesvoid clear_all(managed_shared_memory &shm): Empties the entire registry, invoked during daemon shutdown to prevent resource leaks
Both operations properly invoke destructors within the shared memory context to prevent segmentation faults when other processes detach.
Working with the Registry: Code Examples
Initializing the Handler Manager
This example demonstrates creating the shared memory segment and instantiating the registry with capacity for 1024 objects:
#include <boost/interprocess/managed_shared_memory.hpp>
#include "runtime/src/handler/handler_manager.hpp"
int main() {
// Open or create the shared memory segment used by bpftime
boost::interprocess::managed_shared_memory shm{
boost::interprocess::open_or_create,
bpftime::get_global_shm_name(),
10 * 1024 * 1024 // 10 MiB – size chosen for the example
};
// Allocate space for up to 1024 "fd" entries
constexpr std::size_t max_fds = 1024;
bpftime::handler_manager hm{shm, max_fds};
// Registry is now ready for object insertion
}
Registering a BPF Map Handler
The following snippet allocates a map handler to the first available slot and receives a pseudo-file-descriptor:
// Assume map_handler is a previously constructed bpf_map_handler
bpftime::bpf_map_handler map_handler{/*...*/};
// Move the handler into the manager, automatically picking the first free slot
int fd = hm.set_handler_at_empty_slot(std::move(map_handler), shm);
if (fd < 0) {
std::cerr << "Failed to allocate a map handler slot\n";
} else {
std::cout << "Map registered with pseudo-fd: " << fd << '\n';
}
Looking Up Objects by File Descriptor
This pattern shows type-safe retrieval using std::visit to handle different handler variants:
int target_fd = 5; // example fd obtained earlier
if (hm.is_allocated(target_fd)) {
const auto &variant = hm.get_handler(target_fd);
// Use std::visit to act on the concrete handler type
std::visit([](auto &&h){
using T = std::decay_t<decltype(h)>;
if constexpr (std::is_same_v<T, bpftime::bpf_prog_handler>) {
// operate on program handler
h.run_some_action();
}
// add branches for other handler types as needed
}, variant);
}
Cleaning Up the Registry
During shutdown or cleanup phases, remove all registered objects:
hm.clear_all(shm); // removes every handler from shared memory
Summary
runtime/src/handler/handler_manager.hppimplements the central object registry for the bpftime runtime- The
handler_managerclass (lines 95-127) stores all eBPF objects in aboost::interprocessbackedvector<handler_variant> - Objects are indexed by pseudo-file-descriptors and shared across processes via
managed_shared_memory - The
handler_varianttype (lines 84-88) supports maps, programs, links, and perf events through Boost.Variant - Core methods include
set_handler_at_empty_slotfor registration,get_handlerfor retrieval, andclear_allfor cleanup
Frequently Asked Questions
What file contains the central object registry in bpftime?
The central object registry is implemented in runtime/src/handler/handler_manager.hpp. This file defines the handler_manager class that acts as the single source of truth for all eBPF object lifecycles within the bpftime ecosystem.
How does bpftime share eBPF objects between processes?
bpftime uses Boost.Interprocess to place the handler_manager and its underlying vector<handler_variant> inside a named shared memory segment. Multiple processes open this segment using bpftime::get_global_shm_name() to access the same object table concurrently.
What types of eBPF handlers are stored in the registry?
According to lines 84-88 of handler_manager.hpp, the registry stores bpf_map_handler, bpf_link_handler, bpf_prog_handler, and bpf_perf_event_handler instances within the handler_variant type. Additional handler types may be appended to this variant as the runtime evolves.
How do I look up a specific eBPF object by its file descriptor?
Use the is_allocated(int idx) method to verify the slot is active, then call get_handler(int idx) to retrieve the handler_variant. Apply std::visit or boost::get to extract the concrete handler type (e.g., bpf_map_handler) from the variant for type-specific operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →