-
Notifications
You must be signed in to change notification settings - Fork 132
Enhance Rivermax Manager RX and TX to support multiple streams per thread #1131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Remove duplicate docstring headers from source files and overridden functions - Centralize documentation in class definitions to reduce maintenance overhead - Improve code readability by eliminating redundant comments Signed-off-by: Rony Rado <[email protected]>
- Rename all code references from 'rmax' to 'rivermax' for consistency - Update filenames containing 'rmax' to use full 'rivermax' naming - Modify documentation/comments using abbreviated form - Update CMakeLists.txt and build configurations Affected components: * rmax_config_manager → rivermax_config_manager * rmax_utils → rivermax_utils * RmaxParams → RivermaxParams * All instances in source headers/docs/comments Signed-off-by: Rony Rado <[email protected]>
- Update Rivermax SDK from 1.60.6 to 1.70.21 - Replace legacy rmax-ral-lib with modern rivermax-dev-kit components - Implement multi-receiver architecture supporting: - IPO receivers SMPTE 2022-7 - RTP receivers SMPTE 2210-20 - Media Frame Transmitters (SMPTE 2110-20) - Refactor configuration system with typed builders: - Strong type checking for queue parameters - Validation pipelines for settings - Auto-generated documentation hooks - Update build system: - New CMake targets for dev-kit services - Dockerfile SDK version alignment - Dependency cleanup - Improve memory management and resource cleanup. - Add proper shutdown handling for services. - Enhance error handling and logging. Removed components: - Legacy IPO receiver service implementation - RMAX base service classes - Obsolete chunk consumer interfaces Added components: - RivermaxQueueConfigs hierarchy - TypedConfigBuilderHolder pattern - MediaSenderService Affected components: - RivermaxMgr core implementation - Burst management system - Packet processing pipeline - YAML configuration parsing - Docker build infrastructure Dependencies: - Requires Rivermax SDK >=1.70.21 Signed-off-by: Rony Rado <[email protected]>
- Add hardware-accelerated video streaming with three memory modes: (GPU, host pinned, huge pages) - Enable concurrent TX/RX operations with thread-safe buffer management - Support multiple video formats: - YCbCr 4:2:2/4:4:4/4:2:0 - RGB 8/10/12-bit - Add Rivermax TX benchmarking tools for performance evaluation - Update Rivermax SDK from 1.70.21 to 1.70.32 Key components: - MediaSenderService with CUDA-accelerated frame processing - BurstParams lifecycle management system - YAML configuration support for: - Video resolution (HD/4K) - Frame rates (24-60 fps) - Traffic shaping parameters Added configurations: - TX-only scenario (adv_networking_bench_rivermax_tx.yaml) - Combined TX+RX scenario (adv_networking_bench_rivermax_tx_rx.yaml) Signed-off-by: Rony Rado <[email protected]>
- Add comprehensive tracing for TX burst lifecycle: - Implement context-aware error reporting: - Improve thread synchronization: - Add mutex guards for burst parameter access - Remove redundant lock from availability check - Implement RAII-style resource cleanup - Strengthen packet validation: - Add auto-truncation for oversized chunks - Verify header-data split configuration - Optimize configuration parameters: - Increase max_path_diff_us from 100μs → 10ms - Set sleep_between_operations_us to 0 for low latency - Update CPU core assignments in YAML examples - Fix critical issues: - Memory leak in TX buffer release path - Shutdown sequence race conditions - Header-data split flag propagation - Update Readme Key components modified: - MediaSenderService burst handling - RivermaxManager shutdown sequence - ANO benchmark TX operator - Example configurations (TX/RX scenarios) Signed-off-by: Rony Rado <[email protected]>
- Add burst_flags to burst header. Each manager would be able to set it's own flags for controlling flows/logic Signed-off-by: Rony Rado <[email protected]>
- Add Media Sender service with Zero Copy support - Service doesn't own frames memory pool and forwards received frames to the RDK Media Sender service Signed-off-by: Rony Rado <[email protected]>
- Add yaml option "num_of_packets_in_chunk" . Signed-off-by: Rony Rado <[email protected]>
Signed-off-by: Rony Rado <[email protected]>
Update apps installation command Signed-off-by: Rony Rado <[email protected]>
Signed-off-by: Rony Rado <[email protected]>
Signed-off-by: Rony Rado <[email protected]>
Signed-off-by: Rony Rado <[email protected]>
This commit introduces basic testing for Rivermax manager - New pytest test case for Rivermax TX/RX validation (with both receiver applications - ipo_receiver and rtp_receiver) - Rivermax applications uses the NIC IP address, therefore this commit enhance the network interface utilities with IP address detection. Note: Validation currently relies on receiver-side metrics only. Future enhancement will include sender-side packet comparison once Rivermax-dev-kit provides sender logging capabilities. Signed-off-by: Dana Lessner <[email protected]>
- Introduced Python network initialization function `adv_net_init` to handle Holoscan config objects. - Updated various functions for burst creation, packet management, and network interface operations. Signed-off-by: Rony Rado <[email protected]>
- Add AdvNetworkMediaRxOp for receiving media streams over Rivermax - Supports packet-to-frame conversion with ANO Burst processing - Configurable video formats, bit depths, and frame dimensions - Output as VideoBuffer or Tensor entities - Add AdvNetworkMediaTxOp for transmitting media streams over Rivermax - Processes VideoBuffer/Tensor inputs for network transmission - Configurable network interface and queue management - Support for various video formats Features: - SMPTE 2110 compliance for professional media over IP - GPU acceleration and GPUDirect support - Low latency optimizations - Python bindings for both operators These operators build upon the Advanced Network library to provide specialized functionality for broadcast and media streaming use cases requiring strict timing and high throughput performance. Signed-off-by: Rony Rado <[email protected]>
- Add high-performance media streaming application built on Advanced Network Media Tx operator - Utilizes AdvNetworkMediaTxOp for media transmission over Rivermax - Integrates with Advanced Network Manager for optimized network resource management - SMPTE 2110 compliant for professional broadcast applications - Real-time transmission of media files with precise timing control - Support multiple video formats and resolutions - RGB888, YUV420, NV12 and other common video formats - Configurable bit depths (8, 10, 12, 16-bit) - Multiple resolution support up to 4K and beyond - GPU acceleration with GPUDirect for zero-copy operations * Implement dual language support - C++ implementation for maximum performance - Python implementation for ease of use and development * Support professional broadcast features - Frame-accurate timing for live streaming applications - Looping playback for continuous streaming - VideoStreamReplayer integration for file-based sources Pipeline: VideoStreamReplayer → AdvNetworkMediaTxOp → Advanced Network Manager → Network Interface This application demonstrates how to use the Advanced Network Media operators for professional-grade media transmission in broadcast and media production environments requiring ultra-low latency and high throughput performance. Signed-off-by: Rony Rado <[email protected]>
- Add high-performance media receiving application built on Advanced Network Media Rx operator - Utilizes AdvNetworkMediaRxOp for professional media reception over Rivermax - Integrates with Advanced Network Manager for optimized network resource management - SMPTE 2110 compliant for professional broadcast applications - Real-time reception and processing of media streams with ultra-low latency - Support flexible output modes and format handling - Real-time visualization using HolovizOp for live monitoring - File output capability for recording and analysis - Format conversion support for optimal display - Support multiple video formats and resolutions - RGB888, YUV420, NV12, RGBA and other common video formats - Configurable bit depths (8, 10, 12, 16-bit) - Multiple resolution support up to 4K and beyond - GPU acceleration with GPUDirect for zero-copy operations - Implement dual language support - C++ implementation for maximum performance - Python implementation for ease of use and development Pipeline: Network Interface → Advanced Network Manager → AdvNetworkMediaRxOp → [FormatConverter] → HolovizOp/FramesWriter This application demonstrates how to use the Advanced Network Media operators for professional-grade media reception in broadcast and media production environments requiring ultra-low latency and high throughput performance. Signed-off-by: Rony Rado <[email protected]>
f580e1c to
df42744
Compare
5db2626 to
3c8f9f3
Compare
|
@danal97 could you please resolve the conflicts and update this PR with the latest changes in the main branch? Thanks |
Enhanced the Rivermax manager to support multiple streams on the same thread with stream-specific packet processing and queueing capabilities. Key changes: - Enhanced RDK applications (ipo_receiver and rtp_receiver) to get an extension to RDK AppSetting in order to set threads setting. Each thread has it's dedicated core, ID and streams settings. - Enhanced burst manager to support per-stream ID burst queues. (Each stream ID gets its own dedicated burst queue for packet segregation). - Maintained backward compatibility: bench RX processes bursts regardless of stream ID. Signed-off-by: Dana Lessner <[email protected]>
Enhance the media player application to receive and process multiple video streams simultaneously using the advanced networking with Rivermax manager. This application served as a reference implementation for multi-stream media reception scenarios in advanced networking deployment. Key changes: - Receive multiple streams with Rivermax manager. - Save each stream to different file. - Add MockReceiverOp to simulate data reception for testing Note: The application supports visualization using holoviz - this is currently limited and allowed only when receiving a single stream. Signed-off-by: Dana Lessner <[email protected]>
|
Skipped: This PR changes more files than the configured file change limit: ( |
Signed-off-by: Dana Lessner <[email protected]>
Signed-off-by: Dana Lessner <[email protected]>
WalkthroughThis PR adds two new advanced networking applications (media_player and media_sender) with C++ and Python implementations, refactors the advanced_network operator from legacy RMAX to Rivermax dev-kit, introduces advanced_network_media RX/TX operators with burst/packet processing, extends Python bindings for enhanced burst/packet APIs, and includes comprehensive YAML configurations and documentation. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Specific areas requiring extra attention:
Possibly related PRs
Suggested reviewers
Pre-merge checks✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
operators/advanced_network/README.md (1)
325-362: Consolidate duplicate "Transmit Configuration (tx)" sections.Lines 325 and 343 both have the heading "Transmit Configuration (tx)", violating the MD024 markdown rule (no duplicate headings). Additionally, lines 327-342 and 345-362 contain substantial overlap describing the same queue configuration options.
The structure indicates that the older TX section (lines 325-342) was not removed when the new, more detailed extended Rivermax TX section (starting at line 343) was added. Remove the first, incomplete section entirely and keep only the comprehensive section starting at line 343.
Apply this diff to consolidate:
-##### Transmit Configuration (tx) - -- **`queues`**: List of queues on NIC - type: `list` - full path: `cfg\interfaces\rx\queues` - - **`name`**: Name of queue - - type: `string` - - **`id`**: Integer ID used for flow connection or lookup in operator compute method - - type: `integer` - - **`cpu_core`**: CPU core ID. Should be isolated when CPU polls the NIC for best performance.. <mark>Not in use for Doca GPUNetIO</mark> - Rivermax manager can accept coma separated list of CPU IDs - - type: `string` - - **`batch_size`**: Number of packets in a batch passed from the NIC to the downstream operator. A - larger number increases throughput but reduces end-to-end latency, as it takes longer to populate a single - buffer. A smaller number reduces end-to-end latency but can also reduce throughput. - - type: `integer` - - **`memory_regions`**: List of memory regions where buffers are stored. memory regions names are configured in the [Memory Regions](#memory-regions) section - type: `list` - -##### Transmit Configuration (tx) - **`queues`**: List of queues on NIC **Type**: `list`
🟠 Major comments (17)
operators/advanced_network/advanced_network/managers/rivermax/README.md-1-517 (1)
1-517: Add usage instructions, explicit requirements section, and configuration examples.This comprehensive documentation covers architecture and operational flows well, but is missing several essential sections per coding guidelines for operator README files. The document references YAML configuration and describes initialization processes but doesn't demonstrate practical usage.
Missing Sections:
Usage Instructions (critical gap): How does a developer integrate and use the Rivermax Manager? Add a "Usage" or "Getting Started" section with:
- Basic initialization steps
- How to select between RX/TX services
- Integration with applications
Requirements (critical gap): While hardware dependencies are scattered in lines 220-230, create an explicit "Requirements" section covering:
- Hardware requirements (ConnectX-6+, MOFED driver versions)
- Software dependencies (Rivermax SDK version, C++ standard requirements)
- Memory requirements (huge pages configuration, GPU memory if applicable)
Configuration Examples (important gap): Lines 241 and 289 reference "YAML configuration" but never show an example. Add:
- Sample RX configuration (IPO/RTP receiver setup)
- Sample TX configuration (MediaSender vs MediaSenderZeroCopy)
- Memory region configuration
- Thread/core affinity settings (referenced in RDK AppSetting extension)
Recommended additions:
## Quick Start ### Basic RX Configuration Example \`\`\`yaml [example RX YAML configuration] \`\`\` ### Basic TX Configuration Example \`\`\`yaml [example TX YAML configuration] \`\`\` ## Requirements ### Hardware - ConnectX-6 or later NIC - MOFED driver version X.X+ - CPU with isolated cores for pinning ### Software - Rivermax SDK version [version] - C++17 or later - NVIDIA CUDA [version if GPU support needed] ### Memory Configuration - Huge pages enabled: [how many/setup instructions] - GPU memory [if applicable]applications/adv_networking_bench/cpp/testing/nvidia_nic_utils.py-36-40 (1)
36-40: Makeip_addresstype optional to match runtime values
ip_addressis annotated asstrbut is later set toNonewhen IP discovery fails, which can confuse type checkers and callers expecting a string. Consider updating the type to be explicitly optional and documenting the semantics:-from typing import List +from typing import List, Optional @@ - ip_address: str # IP address + ip_address: Optional[str] # IP address (None if no IPv4 address found)The rest of the
__str__update looks good and will handleNoneby printingip=None.Committable suggestion skipped: line range outside the PR's diff.
applications/adv_networking_media_player/cpp/metadata.json-16-16 (1)
16-16: Correct the category tag.The first tag in the
tagsarray serves as the category and must match one of the approved categories. "Network" is not an approved category. Based on the application's purpose, use "Networking and Distributed Computing" as the first tag.As per coding guidelines, the approved categories are: Benchmarking, Camera, Computer Vision and Perception, Converter, Deployment, Development, Extended Reality, Healthcare AI, Image Processing, Inference, Interoperability, Medical Imaging, Natural Language and Conversational AI, Networking and Distributed Computing, Optimization, Quantum Computing, Rendering, Robotics, Scheduler, Signal Processing, Streaming, Threading, Video, Video Capture, Visualization, XR.
- "tags": ["Network", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"], + "tags": ["Networking and Distributed Computing", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],applications/adv_networking_media_sender/cpp/metadata.json-16-16 (1)
16-16: Correct the category tag.The first tag in the
tagsarray serves as the category and must match one of the approved categories. "Network" is not an approved category. Based on the application's purpose, use "Networking and Distributed Computing" as the first tag.As per coding guidelines, the approved categories are: Benchmarking, Camera, Computer Vision and Perception, Converter, Deployment, Development, Extended Reality, Healthcare AI, Image Processing, Inference, Interoperability, Medical Imaging, Natural Language and Conversational AI, Networking and Distributed Computing, Optimization, Quantum Computing, Rendering, Robotics, Scheduler, Signal Processing, Streaming, Threading, Video, Video Capture, Visualization, XR.
- "tags": ["Network", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax", "Media", "Media Sender"], + "tags": ["Networking and Distributed Computing", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax", "Media", "Media Sender"],operators/advanced_network/advanced_network/managers/rivermax/applications/ano_rtp_receiver.h-30-33 (1)
30-33: Remove using-namespace directives from header file.Placing
using namespacedirectives in a header file pollutes the namespace for all translation units that include this header, potentially causing naming conflicts and violating C++ best practices.Use fully qualified names or namespace aliases instead:
-using namespace rivermax::dev_kit::apps; -using namespace rivermax::dev_kit::io_node; -using namespace rivermax::dev_kit::services; -using namespace rivermax::dev_kit::core; +namespace rdk_apps = rivermax::dev_kit::apps; +namespace rdk_io = rivermax::dev_kit::io_node; +namespace rdk_services = rivermax::dev_kit::services; +namespace rdk_core = rivermax::dev_kit::core;Then use these aliases or fully qualified names throughout the file.
operators/advanced_network/advanced_network/managers/rivermax/rivermax_service/CMakeLists.txt-73-82 (1)
73-82: Bug: Second patch uses potentially stale variableNEW_CONTENTS.The second
ifblock on line 79 checksCONTENTSbut operates onNEW_CONTENTS, which is only defined if the firstifblock executed. If only the second patch is needed (first condition false, second true),NEW_CONTENTSwill be undefined, causing incorrect behavior.Apply this diff to fix the sequential patching logic:
# Will be removed once RDK makes the change file(READ "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" CONTENTS) +set(PATCHED_CONTENTS "${CONTENTS}") if(NOT CONTENTS MATCHES "virtual ReturnStatus find_internal_stream_index\\(") - string(REPLACE "ReturnStatus find_internal_stream_index(" "virtual ReturnStatus find_internal_stream_index(" NEW_CONTENTS "${CONTENTS}") - file(WRITE "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" "${NEW_CONTENTS}") + string(REPLACE "ReturnStatus find_internal_stream_index(" "virtual ReturnStatus find_internal_stream_index(" PATCHED_CONTENTS "${PATCHED_CONTENTS}") endif() -if (NOT CONTENTS MATCHES "virtual ReturnStatus get_app_settings\\(") - string(REPLACE "ReturnStatus get_app_settings(" "virtual ReturnStatus get_app_settings(" NEW_CONTENTS2 "${NEW_CONTENTS}") - file(WRITE "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" "${NEW_CONTENTS2}") +if(NOT CONTENTS MATCHES "virtual ReturnStatus get_app_settings\\(") + string(REPLACE "ReturnStatus get_app_settings(" "virtual ReturnStatus get_app_settings(" PATCHED_CONTENTS "${PATCHED_CONTENTS}") endif() +if(NOT "${PATCHED_CONTENTS}" STREQUAL "${CONTENTS}") + file(WRITE "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" "${PATCHED_CONTENTS}") +endif()operators/advanced_network/python/adv_network_common_pybind.cpp-225-235 (1)
225-235: Verifyformat_eth_addrvalidates and handles invalid MAC address formats.The wrapper calls
format_eth_addrwithout error checking. Ensure this utility validates the input string format (e.g., rejects"invalid", truncated strings, invalid hex characters) and does not cause undefined behavior. Standard validation should include format checking (XX:XX:XX:XX:XX:XX pattern), hex digit validation, and optionally semantic checks (all-zero, broadcast, multicast rejection). The wrapper should also handle and propagate any errors fromformat_eth_addrrather than silently passing invalid results toset_eth_header.applications/adv_networking_media_sender/cpp/adv_networking_media_sender.cpp-17-17 (1)
17-17: Remove unused include.The
assert.hheader is included but never used in this file. Noassert()calls are present.Apply this diff:
-#include <assert.h> #include <sys/time.h>operators/advanced_network/advanced_network/manager.cpp-319-321 (1)
319-321: Add documentation for the new stream-specific overload.The new
get_rx_burstoverload accepting a stream parameter is a key addition for multi-stream support, but it lacks documentation. Please add a comment block explaining when this overload should be used and when subclasses should override it.Apply this diff:
+/** + * @brief Get RX burst for a specific port, queue, and stream + * This method enables stream-specific burst retrieval for managers that support + * multiple streams per queue (e.g., Rivermax). Base implementation returns NOT_SUPPORTED. + * + * @param burst Pointer to receive burst parameters + * @param port Port ID + * @param q Queue ID + * @param stream Stream ID within the queue + * @return Status::NOT_SUPPORTED in base implementation, subclasses may override + */ Status Manager::get_rx_burst(BurstParams** burst, int port, int q, int stream) { return Status::NOT_SUPPORTED; }applications/adv_networking_media_player/cpp/CMakeLists.txt-26-37 (1)
26-37: Useadd_holohub_application()instead ofadd_executable()for proper Holohub build system integration.Applications in this repository must use
add_holohub_application()for build system integration rather than raw CMake commands. Replaceadd_executable(${PROJECT_NAME} ...)with the appropriateadd_holohub_application()call to ensure consistent handling of dependencies, installation, and testing across the Holohub framework.operators/advanced_network_media/advanced_network_media_rx/burst_processor.cpp-34-57 (1)
34-57: Static variables prevent correct multi-instance behavior.The
staticvariables (burst_info_initialized,last_header_stride, etc.) are shared across allBurstProcessorinstances. This conflicts with the PR's multi-stream objectives—if multiple streams use separateBurstProcessorinstances, they will incorrectly share configuration state.Consider moving these to instance members:
+// In burst_processor.h class BurstProcessor: +private: + bool burst_info_initialized_ = false; + size_t last_header_stride_ = 0; + size_t last_payload_stride_ = 0; + bool last_hds_on_ = false; + bool last_payload_on_cpu_ = false; + bool strategy_logged_ = false; // In burst_processor.cpp: void BurstProcessor::ensure_converter_configuration(BurstParams* burst) { - static bool burst_info_initialized = false; - static size_t last_header_stride = 0; - static size_t last_payload_stride = 0; - static bool last_hds_on = false; - static bool last_payload_on_cpu = false; + // Use instance members instead of static variablesCommittable suggestion skipped: line range outside the PR's diff.
applications/adv_networking_media_sender/cpp/CMakeLists.txt-52-60 (1)
52-60: Security concern:WORLD_WRITEpermission on installed files.Setting
WORLD_WRITEpermission on configuration files is overly permissive and could allow any user to modify application configuration. UseWORLD_READinstead.install( FILES ../adv_networking_media_sender.yaml DESTINATION examples/${PROJECT_NAME} COMPONENT ${PROJECT_NAME}-configs PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE - WORLD_READ WORLD_WRITE + WORLD_READ )Apply the same fix to the other
install()blocks at lines 62-70 and 72-80.applications/adv_networking_media_player/cpp/adv_networking_media_player.cpp-406-414 (1)
406-414: Mock receiver is added but not connected to any data flow.When neither
visualizenorwrite_to_fileis enabled, themock_receiveroperator is added usingadd_operator()but there's no data flow from the RX operators to it. The RX operators created inadv_net_media_rx_mapremain unconnected, potentially causing data to be dropped or causing pipeline issues.Consider connecting the mock receiver to the RX operators or using a different approach to consume the data:
} else { HOLOSCAN_LOG_WARN("No output type (write_to_file/visualize) defined. Data will be received but not processed."); - auto mock_receiver = make_operator<ops::MockReceiverOp>( - "mock_receiver", - Arg("interface_name", interface_name), - make_condition<BooleanCondition>("is_alive", true) - ); - add_operator(mock_receiver); + for (const auto& [stream_name, rx_op] : adv_net_media_rx_map) { + auto mock_receiver = make_operator<ops::MockReceiverOp>( + "mock_receiver_" + stream_name, + Arg("interface_name", interface_name), + make_condition<BooleanCondition>("is_alive", true) + ); + add_flow(rx_op, mock_receiver); + } }operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/adv_network_rivermax_mgr.cpp-224-231 (1)
224-231: Memory leak: burst_tx_pool arrays are never freed.The destructor is empty, but
initialize()allocates arrays for each element inburst_tx_pool(lines 228-230). These allocations are never released.Add cleanup in the destructor:
RivermaxMgr::RivermaxMgrImpl::~RivermaxMgrImpl() { + for (int i = 0; i < MAX_TX_BURST; i++) { + delete[] burst_tx_pool[i].pkts[0]; + delete[] burst_tx_pool[i].pkt_lens[0]; + delete[] burst_tx_pool[i].pkt_extra_info; + } }Also applies to: 404-404
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/burst_manager.h-33-33 (1)
33-33: Avoidusing namespaceat namespace scope in headers.Same issue as in
ano_media_sender.h- this pollutes the namespace for all includers.-using namespace rivermax::dev_kit::services; +// Prefer explicit namespace qualification or local using declarationsCommittable suggestion skipped: line range outside the PR's diff.
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_media_sender.h-33-36 (1)
33-36: Avoidusing namespacedirectives at namespace scope in headers.These directives pollute the namespace for all translation units that include this header, potentially causing name collisions and ambiguity for downstream code.
Consider using explicit namespace prefixes or importing only specific symbols within function/method scope:
-using namespace rivermax::dev_kit::apps; -using namespace rivermax::dev_kit::io_node; -using namespace rivermax::dev_kit::services; -using namespace rivermax::dev_kit::core; +// Use explicit prefixes like rivermax::dev_kit::apps::AppSettings +// or import within function scope onlyCommittable suggestion skipped: line range outside the PR's diff.
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp-422-436 (1)
422-436: Status check occurs after the settings pointer is already used.The
dynamic_castat line 425-426 usessettingswhich could be invalid ifget_app_settingsfailed. The status check at line 433 happens too late.bool MediaSenderService::post_init_setup() { const AppSettings* settings = nullptr; auto status = tx_service_->get_app_settings(settings); + + if (status != ReturnStatus::success) { + HOLOSCAN_LOG_ERROR("Failed to get settings from TX service"); + return false; + } + const ANOMediaSenderSettings* media_settings = dynamic_cast<const ANOMediaSenderSettings*>(settings); if (!media_settings) { HOLOSCAN_LOG_ERROR("Failed to cast AppSettings to ANOMediaSenderSettings"); return false; } - - if (status != ReturnStatus::success) { - HOLOSCAN_LOG_ERROR("Failed to get settings from TX service"); - return false; - } size_t pool_buffer_size = settings->media.bytes_per_frame * MEDIA_FRAME_POOL_SIZE;
🟡 Minor comments (28)
operators/advanced_network/CHANGELOG.md-18-27 (1)
18-27: Fix unordered list indentation.The nested list items use 4-space indentation before the dash, but Markdown best practices and markdownlint (MD007) require 2-space indentation for nested lists.
Apply this diff to correct the indentation:
Rivermax Manager: - - **Framework Migration** - - Migrated from legacy rmax-ral-lib to modern rivermax-dev-kit framework (1.70.32). - - **SMPTE 2110-20 Support** - - Implemented hardware-accelerated video streaming with support for multiple memory modes (GPU, host pinned, huge pages) and video formats (YCbCr 4:2:2/4:4:4/4:2:0, RGB 8/10/12-bit). - - **Zero-Copy Performance** - - Added MediaSenderZeroCopyService that forwards received frames without owning memory pools for improved performance. - - **Enhanced Configuration** - - Added configurable packet chunking (`num_of_packets_in_chunk`) and burst flags for better flow control. - - **Code Quality** - - Standardized naming convention from 'rmax' to 'rivermax' project-wide and removed redundant documentation. + - **Framework Migration** + - Migrated from legacy rmax-ral-lib to modern rivermax-dev-kit framework (1.70.32). + - **SMPTE 2110-20 Support** + - Implemented hardware-accelerated video streaming with support for multiple memory modes (GPU, host pinned, huge pages) and video formats (YCbCr 4:2:2/4:4:4/4:2:0, RGB 8/10/12-bit). + - **Zero-Copy Performance** + - Added MediaSenderZeroCopyService that forwards received frames without owning memory pools for improved performance. + - **Enhanced Configuration** + - Added configurable packet chunking (`num_of_packets_in_chunk`) and burst flags for better flow control. + - **Code Quality** + - Standardized naming convention from 'rmax' to 'rivermax' project-wide and removed redundant documentation.operators/advanced_network/README.md-235-235 (1)
235-235: Fix hard tabs and list indentation per linter violations.Lines 235 and 261 contain hard tabs (MD010) that should be replaced with spaces. Additionally, Line 261 has incorrect list indentation (MD007: expected 2 spaces, actual 1 space).
Replace hard tabs with spaces throughout these lines and adjust list indentation to 2 spaces.
- - **`memory_registration`**: Flag, when enabled, reduces the number of memory keys in use by registering all the memory in a single pass on the application side. + - **`memory_registration`**: Flag, when enabled, reduces the number of memory keys in use by registering all the memory in a single pass on the application side.Apply similar formatting to line 261 and any other hard tabs in the document.
Also applies to: 261-261
operators/advanced_network/README.md-355-355 (1)
355-355: Fix sentence structure: provide subject for dangling phrase.Line 355 begins with a sentence fragment. The phrase "Should be isolated when CPU polls the NIC..." lacks a clear grammatical subject. Revise to form a complete sentence.
- - **`cpu_core`**: CPU core ID. Should be isolated when CPU polls the NIC for best performance. <mark>Not in use for DOCA GPUNetIO</mark>. Rivermax Manager can accept comma-separated list of CPU IDs. + - **`cpu_core`**: CPU core ID on which the NIC should poll for best performance. <mark>Not in use for DOCA GPUNetIO</mark>. Rivermax Manager can accept comma-separated list of CPU IDs.operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_chunk_consumer_ano.h-94-98 (1)
94-98: Silent packet discarding may cause data loss.When
chunk_size > max_burst_size_, the code logs a warning but silently discards the excess packets. This could lead to data loss that's difficult to debug. Consider either:
- Returning an error status to signal the overflow condition
- Processing packets in multiple batches
- Adding metrics/counters to track discarded packets
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_queue_configs.cpp-321-326 (1)
321-326: Inconsistent stream ID validation between IPO and RTP receivers.IPO receiver (lines 322-325) has stream ID sequential check commented out, while RTP receiver (lines 386-390) enforces it. This inconsistency may cause confusion. Consider aligning the behavior or documenting the difference.
applications/adv_networking_bench/cpp/testing/benchmark_utils.py-598-603 (1)
598-603: Handle edge case wherefile_pathhas no directory component.If
file_pathis just a filename (e.g.,"output.bin"),os.path.dirname()returns an empty string, andos.makedirs("")will raiseFileNotFoundError.Apply this diff:
# Create the directory if it doesn't exist try: - os.makedirs(os.path.dirname(file_path), exist_ok=True) + dir_name = os.path.dirname(file_path) + if dir_name: + os.makedirs(dir_name, exist_ok=True) except OSError as e: - logger.error(f"Failed to create directory: {e}") + logger.exception(f"Failed to create directory: {e}") raiseapplications/adv_networking_media_player/python/metadata.json-16-16 (1)
16-16: Use approved category tag.The first tag "Network" is not in the approved categories list. Based on coding guidelines, the category must be one of the approved values. For networking-related applications, use "Networking and Distributed Computing" instead.
Apply this diff:
- "tags": ["Network", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"], + "tags": ["Networking and Distributed Computing", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],As per coding guidelines, the category (first tag in the tags array) must match one of the approved categories.
operators/advanced_network_media/advanced_network_media_rx/metadata.json-15-15 (1)
15-15: Use approved category tag.The first tag "Network" is not in the approved categories list. For networking-related operators, use "Networking and Distributed Computing" as the first tag.
Apply this diff:
- "tags": ["Network", "Networking", "DPDK", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"], + "tags": ["Networking and Distributed Computing", "Networking", "DPDK", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],As per coding guidelines, the category (first tag in the tags array) must match one of the approved categories.
applications/adv_networking_media_sender/cpp/adv_networking_media_sender.cpp-43-46 (1)
43-46: Remove duplicate log message.Lines 43 and 46 both log the manager type with nearly identical messages. Keep one and remove the other to avoid redundancy.
Apply this diff:
HOLOSCAN_LOG_INFO("Using Advanced Network manager {}", advanced_network::manager_type_to_string(mgr_type)); - HOLOSCAN_LOG_INFO("Using ANO manager {}", advanced_network::manager_type_to_string(mgr_type)); if (!tx_en) {applications/adv_networking_bench/adv_networking_bench_rivermax_tx_rx.yaml-65-86 (1)
65-86: Align RX configuration format with other Rivermax YAMLs.This file uses a legacy flat format for Rivermax RX settings (lines 73-84) with direct IP address arrays, while
adv_networking_bench_rivermax_rx.yamluses the newer hierarchicalrx_threads→network_settingsstructure. This inconsistency may confuse users and doesn't showcase the multi-stream per thread capability.Consider updating to match the hierarchical format:
rivermax_rx_settings: settings_type: "ipo_receiver" memory_registration: true #allocator_type: "huge_page_2mb" verbose: true max_path_diff_us: 10000 ext_seq_num: true sleep_between_operations_us: 0 - local_ip_addresses: - - 2.1.0.12 - - 2.1.0.12 - source_ip_addresses: - - 2.1.0.2 - - 2.1.0.2 - destination_ip_addresses: - - 224.1.1.1 - - 224.1.1.2 - destination_ports: - - 50001 - - 50001 + rx_threads: + - thread_id: 0 + network_settings: + - stream_id: 0 + local_ip_addresses: + - 2.1.0.12 + source_ip_addresses: + - 2.1.0.2 + destination_ip_addresses: + - 224.1.1.1 + destination_ports: + - 50001 + - stream_id: 1 + local_ip_addresses: + - 2.1.0.12 + source_ip_addresses: + - 2.1.0.2 + destination_ip_addresses: + - 224.1.1.2 + destination_ports: + - 50001 stats_report_interval_ms: 1000 send_packet_ext_info: trueoperators/advanced_network_media/advanced_network_media_rx/burst_processor.cpp-105-115 (1)
105-115: Staticstrategy_loggedalso affects multi-instance behavior.Similar to the configuration statics, this
static bool strategy_loggedmeans the strategy detection message will only be logged for the first stream that detects a strategy, not for each stream independently. Consider making this an instance member as well.applications/adv_networking_media_sender/adv_networking_media_sender.yaml-1-3 (1)
1-3: Update copyright year for new file.This is a new file added in the PR, but the copyright header shows 2022-2023. Consider updating to 2023-2025 to match other new files in this PR.
%YAML 1.2 -# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.applications/adv_networking_media_player/cpp/CMakeLists.txt-1-2 (1)
1-2: Update copyright year for new file.The copyright shows 2022-2023 but this is a new file added in this PR. Consider updating to 2023-2025 for consistency.
-# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.applications/adv_networking_media_player/python/adv_networking_media_player.py-3-4 (1)
3-4: Update copyright year for new file.Similar to the YAML file, the copyright header shows 2022-2023 but this is a new file. Consider updating to 2023-2025.
-# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.operators/advanced_network_media/advanced_network_media_rx/burst_processor.cpp-1-3 (1)
1-3: Update copyright year for consistency.The copyright shows 2023 only. For consistency with other new files in this PR, consider updating to 2023-2025.
/* - * SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.operators/advanced_network_media/advanced_network_media_rx/adv_network_media_rx.cpp-395-399 (1)
395-399: Typo in parameter description: "devices" should be "device".The description says
'host' or 'devices'but should say'host' or 'device'to match the actual comparison logic at line 95.spec.param(memory_location_, "memory_location", "Memory Location for Output Frames", - "Memory location for output frames ('host' or 'devices')", + "Memory location for output frames ('host' or 'device')", std::string("device"));applications/adv_networking_media_sender/python/adv_networking_media_sender.py-3-3 (1)
3-3: Update copyright year for new file.The copyright year
2022-2023appears outdated for a new file. Consider updating to include 2025.-# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.applications/adv_networking_bench/cpp/rivermax_bench_op_tx.h-568-574 (1)
568-574: Typo in video format string: "YUV442" should likely be "YUV444".Line 572 maps "YUV442" to
YCbCr_4_4_4, but the standard naming for 4:4:4 subsampling would be "YUV444". This appears to be a typo.if (format == "RGB888") return VideoSampling::RGB; if (format == "YUV422") return VideoSampling::YCbCr_4_2_2; if (format == "YUV420") return VideoSampling::YCbCr_4_2_0; - if (format == "YUV442") return VideoSampling::YCbCr_4_4_4; + if (format == "YUV444") return VideoSampling::YCbCr_4_4_4; return VideoSampling::Unknown;applications/adv_networking_media_player/cpp/adv_networking_media_player.cpp-129-129 (1)
129-129: Off-by-one error in frame recording check.The condition
frames_recorded_ > num_of_frames_to_record_.get()allows recordingnum_of_frames_to_record + 1frames before stopping. Should use>=for exact count.- if (frames_recorded_ > num_of_frames_to_record_.get()) { return; } + if (frames_recorded_ >= num_of_frames_to_record_.get()) { return; }operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/burst_manager.cpp-400-402 (1)
400-402: Missing braces around multi-statement conditional.Line 400-401 appears to be a conditional check, but line 401 is executed unconditionally due to missing braces. If
burst_out_size_validation is intended to be conditional, add braces.- if (burst_out_size_ > RivermaxBurst::MAX_PKT_IN_BURST || burst_out_size_ == 0) - burst_out_size_ = RivermaxBurst::MAX_PKT_IN_BURST; + if (burst_out_size_ > RivermaxBurst::MAX_PKT_IN_BURST || burst_out_size_ == 0) { + burst_out_size_ = RivermaxBurst::MAX_PKT_IN_BURST; + }applications/adv_networking_bench/cpp/rivermax_bench_op_tx.h-387-392 (1)
387-392: Infinite retry loop whenadd_item_blockingfails.If
add_item_blockingreturns a non-SUCCESS status other than queue being full (e.g., if stopped), the inner while loop will spin indefinitely with only a sleep, never breaking out. Theadd_item_blockingalready handles waiting for space, so the retry loop may be unnecessary.Consider simplifying to handle the stop case:
- while (!stop_) { - auto status = frame_buffer_queue_.add_item_blocking(frame_buffer); - - if (status == Status::SUCCESS) { break; } - std::this_thread::sleep_for(std::chrono::microseconds(sleep_duration_microseconds_)); - } + auto status = frame_buffer_queue_.add_item_blocking(frame_buffer); + if (status != Status::SUCCESS) { + HOLOSCAN_LOG_DEBUG("Failed to add frame to queue: {}", static_cast<int>(status)); + }applications/adv_networking_bench/cpp/rivermax_bench_op_tx.h-520-525 (1)
520-525: Missing CUDA error checking oncudaMemcpy.The
cudaMemcpycall on line 522 doesn't check for errors. If the copy fails, the corrupt or incomplete data will be sent.Consider using the CUDA_TRY pattern or checking the return value:
// Copy frame data into the burst if (payload_memory_.get() == "device") { - cudaMemcpy(cur_msg_->pkts[0][0], frame->get(), frame->get_size(), cudaMemcpyDefault); + cudaError_t err = cudaMemcpy(cur_msg_->pkts[0][0], frame->get(), frame->get_size(), cudaMemcpyDefault); + if (err != cudaSuccess) { + HOLOSCAN_LOG_ERROR("cudaMemcpy failed: {}", cudaGetErrorString(err)); + free_tx_burst(cur_msg_); + cur_msg_ = nullptr; + return; + } } else { std::memcpy(cur_msg_->pkts[0][0], frame->get(), frame->get_size()); }operators/advanced_network/advanced_network/managers/rivermax/applications/ano_media_sender.h-74-75 (1)
74-75: Incorrect comment: "recv flows" should be "send flows" or similar.The comment says "Network recv flows" but this is the sender application, and the member stores thread/stream configuration for sending.
- /* Network recv flows */ + /* Per-thread stream configurations */ std::vector<std::vector<TwoTupleFlow>> m_threads_streams;operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp-352-358 (1)
352-358:newoperator throws on failure; null check is ineffective.The
new byte_t[pool_buffer_size]expression will throwstd::bad_allocon failure rather than returningnullptr. The null check will never trigger.Consider using
new (std::nothrow)if you want to handle allocation failure gracefully:} else { - pool_buffer = new byte_t[pool_buffer_size]; - if (pool_buffer == nullptr) { - HOLOSCAN_LOG_ERROR("Failed to allocate host memory"); - return nullptr; - } + pool_buffer = new (std::nothrow) byte_t[pool_buffer_size]; + if (pool_buffer == nullptr) { + HOLOSCAN_LOG_ERROR("Failed to allocate host memory"); + return nullptr; + } HOLOSCAN_LOG_INFO("Allocated host memory for frames memory pool at address: {}", pool_buffer); }operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_config_manager.cpp-359-360 (1)
359-360: Copy-paste error: Log message says "RX" but this is a TX function.The log message incorrectly refers to "Rivermax RX ANO settings" in the TX configuration path.
Apply this fix:
HOLOSCAN_LOG_INFO( - "Rivermax RX ANO settings for {} ({}) on port {}", q.common_.name_, queue_id, port_id); + "Rivermax TX ANO settings for {} ({}) on port {}", q.common_.name_, queue_id, port_id);operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_config_manager.cpp-857-883 (1)
857-883: Unreachable code after try block.The
return true;at line 882 is unreachable because all paths in the try block either return or fall through to the catch block which also returns.Remove the unreachable statement:
} catch (const std::out_of_range& e) { if (num_of_mrs == 1) HOLOSCAN_LOG_ERROR("Invalid memory region for Rivermax RX queue: {}", queue_mr_names[0]); else HOLOSCAN_LOG_ERROR("Invalid memory region for Rivermax RX queue: {} or {}", queue_mr_names[0], queue_mr_names[1]); return false; } - - return true; }operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.h-85-90 (1)
85-90: Uninitialized protected membersport_id_andqueue_id_.These members are declared but not initialized in the base class constructor. If derived classes access them before explicitly setting values, this could lead to undefined behavior.
Consider adding default initialization:
protected: uint32_t service_id_; ///< Unique identifier for the service - uint16_t port_id_; ///< Port ID derived from the service ID - uint16_t queue_id_; ///< Queue ID derived from the service ID + uint16_t port_id_ = 0; ///< Port ID derived from the service ID + uint16_t queue_id_ = 0; ///< Queue ID derived from the service ID bool initialized_ = false; ///< Flag indicating whether the service is initialized };operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_config_manager.cpp-779-801 (1)
779-801: Dead variableto_reset_cores_vectorinvalidate_cores.This variable is declared but never used, likely copied from
parse_and_set_coreswhere it serves a purpose.Remove the unused variable:
int ConfigManagerUtilities::validate_cores(const std::string& cores) { std::istringstream iss(cores); std::string coreStr; - bool to_reset_cores_vector = true; int num_cores = 0;
| add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml | ||
| COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | ||
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_rx.yaml" | ||
| ) | ||
| add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_rx_yaml) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect dependency for rivermax_tx_rx target.
Line 108 has the wrong dependency file. The target copies adv_networking_bench_rivermax_tx_rx.yaml but depends on adv_networking_bench_rivermax_rx.yaml, which will cause incorrect rebuild behavior.
Apply this diff:
add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_rx.yaml"
+ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_tx_rx.yaml"
)🤖 Prompt for AI Agents
In applications/adv_networking_bench/cpp/CMakeLists.txt around lines 106 to 110,
the custom target adv_networking_bench_rivermax_tx_rx_yaml copies
adv_networking_bench_rivermax_tx_rx.yaml but incorrectly lists
adv_networking_bench_rivermax_rx.yaml as a DEPENDS; update the DEPENDS to
reference the correct source file (adv_networking_bench_rivermax_tx_rx.yaml with
the same ../ path) so the target depends on the actual file being copied and
rebuilds correctly.
| add_custom_target(adv_networking_bench_rivermax_rx_yaml | ||
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | ||
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" | ||
| ) | ||
| add_dependencies(adv_networking_bench adv_networking_bench_rmax_rx_yaml) | ||
|
|
||
| add_custom_target(adv_networking_bench_rivermax_tx_yaml | ||
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | ||
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" | ||
| ) | ||
| add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_yaml) | ||
|
|
||
| add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml | ||
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | ||
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" | ||
| ) | ||
| add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_rx_yaml) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Target names in add_dependencies don't match add_custom_target names.
The custom targets are defined with rivermax in the name, but add_dependencies references rmax. This mismatch will cause CMake errors.
add_custom_target(adv_networking_bench_rivermax_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml"
)
-add_dependencies(adv_networking_bench adv_networking_bench_rmax_rx_yaml)
+add_dependencies(adv_networking_bench adv_networking_bench_rivermax_rx_yaml)
add_custom_target(adv_networking_bench_rivermax_tx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml"
)
-add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_yaml)
+add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_yaml)
add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml"
)
-add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_rx_yaml)
+add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_rx_yaml)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| add_custom_target(adv_networking_bench_rivermax_rx_yaml | |
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | |
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" | |
| ) | |
| add_dependencies(adv_networking_bench adv_networking_bench_rmax_rx_yaml) | |
| add_custom_target(adv_networking_bench_rivermax_tx_yaml | |
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | |
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" | |
| ) | |
| add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_yaml) | |
| add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml | |
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | |
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" | |
| ) | |
| add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_rx_yaml) | |
| add_custom_target(adv_networking_bench_rivermax_rx_yaml | |
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | |
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" | |
| ) | |
| add_dependencies(adv_networking_bench adv_networking_bench_rivermax_rx_yaml) | |
| add_custom_target(adv_networking_bench_rivermax_tx_yaml | |
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | |
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" | |
| ) | |
| add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_yaml) | |
| add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml | |
| COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR} | |
| DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" | |
| ) | |
| add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_rx_yaml) |
🤖 Prompt for AI Agents
In applications/adv_networking_bench/cpp/CMakeLists.txt.install around lines 85
to 101, the add_dependencies calls reference targets with "rmax" while the
add_custom_target definitions use "rivermax", causing a target name mismatch;
update each add_dependencies line to reference the exact custom target names
(adv_networking_bench_rivermax_rx_yaml, adv_networking_bench_rivermax_tx_yaml,
adv_networking_bench_rivermax_tx_rx_yaml) so they match the corresponding
add_custom_target names.
| from_config("holoviz"), | ||
| Arg("cuda_stream_pool", cuda_stream_pool), | ||
| Arg("allocator") = allocator); | ||
| add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type mismatch: accessing unordered_map<string, ...> with integer key.
adv_net_media_rx_map is declared as std::unordered_map<std::string, std::shared_ptr<ops::AdvNetworkMediaRxOp>> but is accessed with adv_net_media_rx_map[0] using an integer key. This will implicitly convert 0 to a string "0", which is unlikely to match any stream name.
The map should be accessed by stream name. Consider using an iterator to get the first element:
- add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}});
+ add_flow(adv_net_media_rx_map.begin()->second, visualizer, {{"out_video_buffer", "receivers"}});Or store the first stream name and use it:
+ std::string first_stream_name = multi_streams_adv_net_media_rx_yaml[0]["name"].as<std::string>();
- add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}});
+ add_flow(adv_net_media_rx_map[first_stream_name], visualizer, {{"out_video_buffer", "receivers"}});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}}); | |
| add_flow(adv_net_media_rx_map.begin()->second, visualizer, {{"out_video_buffer", "receivers"}}); |
🤖 Prompt for AI Agents
In applications/adv_networking_media_player/cpp/adv_networking_media_player.cpp
around line 383, the code uses adv_net_media_rx_map[0] which treats 0 as string
"0" and is a type/key mismatch; replace this access with a proper lookup of the
desired stream—either use adv_net_media_rx_map.begin()->second to get the first
element (after checking the map is not empty) or use the actual stream name
string key (e.g., adv_net_media_rx_map.at(streamName) or find(streamName) and
handle missing keys); ensure you check the map is non-empty or the key exists
before calling add_flow to avoid dereferencing end().
| cmake_minimum_required(VERSION 3.20) | ||
| project(advanced_network_media_rx) | ||
|
|
||
| find_package(holoscan 2.6 REQUIRED CONFIG | ||
| PATHS "/opt/nvidia/holoscan" "/workspace/holoscan-sdk/install") | ||
|
|
||
| add_library(${PROJECT_NAME} SHARED | ||
| adv_network_media_rx.cpp | ||
| burst_processor.cpp | ||
| packets_to_frames_converter.cpp | ||
| ) | ||
|
|
||
| add_library(holoscan::ops::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) | ||
|
|
||
| target_include_directories(${PROJECT_NAME} PUBLIC | ||
| ${CMAKE_CURRENT_SOURCE_DIR} | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../../advanced_network | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../../advanced_network/advanced_network | ||
| ) | ||
|
|
||
| target_link_libraries(${PROJECT_NAME} | ||
| PRIVATE | ||
| holoscan::core | ||
| GXF::multimedia | ||
| rivermax-dev-kit | ||
| advanced_network_common | ||
| advanced_network_media_common | ||
| ) | ||
|
|
||
| set_target_properties(${PROJECT_NAME} PROPERTIES | ||
| OUTPUT_NAME "holoscan_op_advanced_network_media_rx" | ||
| EXPORT_NAME ops::advanced_network_media_rx | ||
| ) | ||
|
|
||
| # Installation | ||
| install( | ||
| TARGETS | ||
| ${PROJECT_NAME} | ||
| EXPORT holoscan-networking-targets | ||
| COMPONENT advanced_network-cpp | ||
| ) | ||
|
|
||
| if(HOLOHUB_BUILD_PYTHON) | ||
| add_subdirectory(python) | ||
| endif() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find the add_holohub_operator macro definition
fd -t f "add_holohub_operator" --search-path . || rg "macro.*add_holohub_operator" -t cmakeRepository: nvidia-holoscan/holohub
Length of output: 190
🏁 Script executed:
# Search for add_holohub_operator usage in CMakeLists.txt files across operators
rg "add_holohub_operator" --type cmake operators/ -l | head -20Repository: nvidia-holoscan/holohub
Length of output: 190
🏁 Script executed:
# Find the file structure of the advanced_network operator
find operators/advanced_network -type f -name "CMakeLists.txt" | head -10Repository: nvidia-holoscan/holohub
Length of output: 190
🌐 Web query:
holoscan holohub add_holohub_operator cmake macro
💡 Result:
Short answer — what it is and where to use it:
- add_holohub_operator is a Holohub-provided CMake helper that registers an operator component with the Holohub build infrastructure (so the operator can be built, packaged and depended-on by Holohub applications). [1][2][3]
Usage (typical / high-level):
- Call from an operators/CMakeLists.txt inside a Holohub component:
add_holohub_operator(<operator_name> [DEPENDS <other_components>] [EXTENSIONS <gxf_extensions> ...]) - The macro is part of Holohub’s CMake helper files (the HoloHubConfigHelpers / CMake utility files in the holohub repo) and is used by Holohub’s top-level build scripts (the holohub tooling) to wire operator targets, dependencies and packaging. [1][2]
Where to read/examples:
- Holohub repository (cmake helpers and operators/ CMakeLists usage). [1]
- Holoscan/Holohub developer docs and examples (shows patterns for add_holohub_operator, add_holohub_application, add_holohub_extension, etc.). [2][3]
If you want, I can:
- fetch the exact definition/arguments from HoloHubConfigHelpers.cmake in the holohub repo and paste the macro signature + a concrete CMake example.
🌐 Web query:
holoscan holohub operators CMakeLists.txt build integration
💡 Result:
Short summary and steps to integrate Holohub operators into a Holoscan C++ build (with minimal example CMake snippets).
Key points
- Use find_package(holoscan) to load the SDK (point to /opt/nvidia/holoscan or set Holoscan_ROOT). [1][2]
- Build operators as shared libraries that link holoscan::core. [2]
- If the operator and application are in the same CMake project: add the operator target and list it in target_link_libraries() for the app. [2]
- If operator and app are separate projects: export the operator (install/EXPORT) and import it in the app (find_package or find_library / target import), then link the imported operator target to the app. [2]
- Holohub repo includes a holohub script that wraps container/build steps and places build outputs under ./build/. Use ./holohub build or run-container for development. [4]
Minimal CMake examples
-
Operator CMakeLists.txt (operator project)
cmake_minimum_required(VERSION 3.20)
project(my_operator CXX)
find_package(holoscan REQUIRED CONFIG PATHS "/opt/nvidia/holoscan")
add_library(my_operator SHARED my_operator.cpp)
target_link_libraries(my_operator PUBLIC holoscan::core) -
App CMakeLists.txt (same project)
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE holoscan::core my_operator) -
App CMakeLists.txt (separate project; import operator)
find_package(holoscan REQUIRED CONFIG PATHS "/opt/nvidia/holoscan")either find imported operator via find_package/find_library or add dependency to installed target
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE holoscan::core <imported_operator_target_or_library>)
Build commands (example)
cmake -S <src_dir> -B <build_dir> -D Holoscan_ROOT="/opt/nvidia/holoscan"
cmake --build <build_dir> -j
References
- Holoscan SDK — Creating an Application (CMake usage). [1]
- Holoscan SDK — Creating Operators (build/link guidance and same-project vs separate-project usage). [2]
- NVIDIA holohub GitHub (holohub script, container/build conventions). [4]
If you want, I can:
- produce a complete example repo CMakeLists.txt pair (operator + app) you can copy, or
- show the CMake export/install snippet for publishing an operator for separate-project consumption. Which would you prefer?
Replace add_library() with add_holohub_operator() for proper build system integration.
This CMakeLists.txt does not use the required add_holohub_operator() macro. Per Holohub coding guidelines, all operators in the repository must use add_holohub_operator() for proper registration with the Holohub build infrastructure.
Additionally, custom operator/extension dependencies (advanced_network_common and advanced_network_media_common) should be declared via the DEPENDS argument to add_holohub_operator() rather than directly in target_link_libraries().
🤖 Prompt for AI Agents
In operators/advanced_network_media/advanced_network_media_rx/CMakeLists.txt
around lines 15-59, replace the direct add_library() usage with the repository
macro add_holohub_operator() so the operator is properly registered; move custom
operator/extension dependencies (advanced_network_common and
advanced_network_media_common) out of target_link_libraries and declare them via
the DEPENDS argument to add_holohub_operator(); keep non-operator libs
(holoscan::core, GXF::multimedia, rivermax-dev-kit) in target_link_libraries as
PRIVATE, preserve target_include_directories and set_target_properties as needed
(or remove redundant install/export if add_holohub_operator handles them), and
ensure the alias holoscan::ops::${PROJECT_NAME} is created after the operator
target is defined.
| void ANOIPOReceiverApp::distribute_work_for_threads() | ||
| { | ||
| m_streams_per_thread.reserve(m_ipo_receiver_settings->thread_settings.size()); | ||
| m_ipo_receiver_settings->num_of_total_streams = 0; | ||
| int thread_index = 0; | ||
| for (const auto& thread : m_ipo_receiver_settings->thread_settings) { | ||
| m_streams_per_thread[thread_index] = thread.stream_network_settings.size(); | ||
| m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size(); | ||
| thread_index++; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: reserve() followed by direct indexing causes undefined behavior.
m_streams_per_thread.reserve() only allocates capacity without creating elements. Accessing m_streams_per_thread[thread_index] on non-existent indices is undefined behavior. Use resize() to create elements, or use push_back().
void ANOIPOReceiverApp::distribute_work_for_threads()
{
- m_streams_per_thread.reserve(m_ipo_receiver_settings->thread_settings.size());
+ m_streams_per_thread.resize(m_ipo_receiver_settings->thread_settings.size());
m_ipo_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_ipo_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| void ANOIPOReceiverApp::distribute_work_for_threads() | |
| { | |
| m_streams_per_thread.reserve(m_ipo_receiver_settings->thread_settings.size()); | |
| m_ipo_receiver_settings->num_of_total_streams = 0; | |
| int thread_index = 0; | |
| for (const auto& thread : m_ipo_receiver_settings->thread_settings) { | |
| m_streams_per_thread[thread_index] = thread.stream_network_settings.size(); | |
| m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size(); | |
| thread_index++; | |
| } | |
| } | |
| void ANOIPOReceiverApp::distribute_work_for_threads() | |
| { | |
| m_streams_per_thread.resize(m_ipo_receiver_settings->thread_settings.size()); | |
| m_ipo_receiver_settings->num_of_total_streams = 0; | |
| int thread_index = 0; | |
| for (const auto& thread : m_ipo_receiver_settings->thread_settings) { | |
| m_streams_per_thread[thread_index] = thread.stream_network_settings.size(); | |
| m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size(); | |
| thread_index++; | |
| } | |
| } |
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_ipo_receiver.cpp
around lines 164-174, the code calls m_streams_per_thread.reserve(...) then
writes to m_streams_per_thread[thread_index], which is undefined because reserve
only sets capacity not size; change to either call
m_streams_per_thread.resize(m_ipo_receiver_settings->thread_settings.size())
before the loop so indexing is valid, or replace the indexed assignment with
m_streams_per_thread.push_back(thread.stream_network_settings.size()) and
increment num_of_total_streams accordingly; keep the num_of_total_streams
accumulation as-is and remove the manual thread_index if using push_back.
| void ANOMediaSenderApp::distribute_work_for_threads() | ||
| { | ||
| m_streams_per_thread.reserve(m_media_sender_settings->thread_settings.size()); | ||
| m_media_sender_settings->num_of_total_streams = 0; | ||
| m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size(); | ||
| int thread_idx = 0; | ||
| for (const auto& thread : m_media_sender_settings->thread_settings) { | ||
| m_streams_per_thread[thread_idx] = thread.stream_network_settings.size(); | ||
| m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size(); | ||
| thread_idx++; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential out-of-bounds access due to reserve vs resize.
m_streams_per_thread.reserve() only allocates capacity but does not create elements. Accessing m_streams_per_thread[thread_idx] on line 215 causes undefined behavior since the vector is empty.
Apply this diff to fix:
void ANOMediaSenderApp::distribute_work_for_threads()
{
- m_streams_per_thread.reserve(m_media_sender_settings->thread_settings.size());
+ m_streams_per_thread.resize(m_media_sender_settings->thread_settings.size());
m_media_sender_settings->num_of_total_streams = 0;
m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size();
int thread_idx = 0;
for (const auto& thread : m_media_sender_settings->thread_settings) {
m_streams_per_thread[thread_idx] = thread.stream_network_settings.size();
m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_idx++;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| void ANOMediaSenderApp::distribute_work_for_threads() | |
| { | |
| m_streams_per_thread.reserve(m_media_sender_settings->thread_settings.size()); | |
| m_media_sender_settings->num_of_total_streams = 0; | |
| m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size(); | |
| int thread_idx = 0; | |
| for (const auto& thread : m_media_sender_settings->thread_settings) { | |
| m_streams_per_thread[thread_idx] = thread.stream_network_settings.size(); | |
| m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size(); | |
| thread_idx++; | |
| } | |
| } | |
| void ANOMediaSenderApp::distribute_work_for_threads() | |
| { | |
| m_streams_per_thread.resize(m_media_sender_settings->thread_settings.size()); | |
| m_media_sender_settings->num_of_total_streams = 0; | |
| m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size(); | |
| int thread_idx = 0; | |
| for (const auto& thread : m_media_sender_settings->thread_settings) { | |
| m_streams_per_thread[thread_idx] = thread.stream_network_settings.size(); | |
| m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size(); | |
| thread_idx++; | |
| } | |
| } |
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_media_sender.cpp
around lines 208-219, m_streams_per_thread.reserve(...) is used but later
indexed with m_streams_per_thread[thread_idx], which is undefined because
reserve does not create elements; change the allocation to create elements
(e.g., call
m_streams_per_thread.resize(m_media_sender_settings->thread_settings.size())
before the loop) or alternatively replace the indexed assignment with
m_streams_per_thread.push_back(...) inside the loop so elements are valid when
written, and keep the rest of the counting logic unchanged.
| void ANORTPReceiverApp::distribute_work_for_threads() | ||
| { | ||
| m_streams_per_thread.reserve(m_rtp_receiver_settings->thread_settings.size()); | ||
| m_rtp_receiver_settings->num_of_total_streams = 0; | ||
| int thread_index = 0; | ||
| for (const auto& thread : m_rtp_receiver_settings->thread_settings) { | ||
| m_streams_per_thread[thread_index] = thread.stream_network_settings.size(); | ||
| m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size(); | ||
| thread_index++; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: reserve() followed by direct indexing causes undefined behavior.
Same issue as in ano_ipo_receiver.cpp: reserve() only allocates capacity without creating elements. Direct indexing into uninitialized positions is undefined behavior.
void ANORTPReceiverApp::distribute_work_for_threads()
{
- m_streams_per_thread.reserve(m_rtp_receiver_settings->thread_settings.size());
+ m_streams_per_thread.resize(m_rtp_receiver_settings->thread_settings.size());
m_rtp_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_rtp_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| void ANORTPReceiverApp::distribute_work_for_threads() | |
| { | |
| m_streams_per_thread.reserve(m_rtp_receiver_settings->thread_settings.size()); | |
| m_rtp_receiver_settings->num_of_total_streams = 0; | |
| int thread_index = 0; | |
| for (const auto& thread : m_rtp_receiver_settings->thread_settings) { | |
| m_streams_per_thread[thread_index] = thread.stream_network_settings.size(); | |
| m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size(); | |
| thread_index++; | |
| } | |
| } | |
| void ANORTPReceiverApp::distribute_work_for_threads() | |
| { | |
| m_streams_per_thread.resize(m_rtp_receiver_settings->thread_settings.size()); | |
| m_rtp_receiver_settings->num_of_total_streams = 0; | |
| int thread_index = 0; | |
| for (const auto& thread : m_rtp_receiver_settings->thread_settings) { | |
| m_streams_per_thread[thread_index] = thread.stream_network_settings.size(); | |
| m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size(); | |
| thread_index++; | |
| } | |
| } |
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_rtp_receiver.cpp
around lines 154-164, the code calls m_streams_per_thread.reserve(...) and then
assigns via m_streams_per_thread[thread_index], which is undefined because
reserve() doesn't create elements; change to create elements first (e.g., call
m_streams_per_thread.resize(m_rtp_receiver_settings->thread_settings.size())
before the loop) or avoid indexing by using push_back/emplace_back inside the
loop, and keep incrementing num_of_total_streams as before.
| auto out_burst_shared = queue_it->second[stream]->dequeue_burst(); | ||
| if (out_burst_shared == nullptr) { return Status::NULL_PTR; } | ||
| *burst = out_burst_shared.get(); | ||
| return Status::SUCCESS; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Use-after-free with shared_ptr raw pointer extraction.
dequeue_burst() returns a shared_ptr<RivermaxBurst>. Calling .get() and assigning to *burst results in the shared_ptr being destroyed at statement end, potentially freeing the burst while the caller still holds a raw pointer.
This pattern appears in both get_rx_burst overloads. Consider returning the shared_ptr directly or storing it in a member to extend its lifetime:
auto out_burst_shared = queue_it->second[stream]->dequeue_burst();
if (out_burst_shared == nullptr) { return Status::NULL_PTR; }
*burst = out_burst_shared.get(); // Dangling pointer after this lineAlso applies to: 594-597
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/adv_network_rivermax_mgr.cpp
around lines 563-567 (and similarly 594-597) the code extracts a raw pointer via
shared_ptr.get() and assigns it to *burst, causing a use-after-free when the
temporary shared_ptr is destroyed; change the API or caller usage so the
shared_ptr lifetime is preserved — either modify the function to return or
output a std::shared_ptr<RivermaxBurst> (or accept a
std::shared_ptr<RivermaxBurst>& out parameter) or store the dequeued shared_ptr
in a member/longer-lived container before assigning the raw pointer; update
callers accordingly to hold the shared_ptr rather than a raw pointer so the
burst object remains valid.
| Status RivermaxManagerRxService::get_rx_burst(BurstParams** burst, int stream_id) { | ||
| if (rx_bursts_out_queue_.count(stream_id) == 0) { | ||
| HOLOSCAN_LOG_ERROR("RX bursts out queue not initialized for stream_id: {}", stream_id); | ||
| return Status::NOT_READY; | ||
| } | ||
| auto out_burst = rx_bursts_out_queue_[stream_id]->dequeue_burst().get(); | ||
| *burst = static_cast<BurstParams*>(out_burst); | ||
| if (*burst == nullptr) { return Status::NOT_READY; } | ||
| return Status::SUCCESS; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential use-after-free: returning raw pointer from shared_ptr.
dequeue_burst() returns a shared_ptr<RivermaxBurst>, but .get() is called and the raw pointer is returned. Once the local shared_ptr goes out of scope, the burst may be deleted, leaving *burst as a dangling pointer.
The caller receives a raw pointer that could become invalid immediately. Verify that the queue maintains ownership or consider a different ownership pattern.
auto out_burst = rx_bursts_out_queue_[stream_id]->dequeue_burst().get();
*burst = static_cast<BurstParams*>(out_burst); // out_burst may be freed here🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp
around lines 104-113, the code calls dequeue_burst().get() and returns the raw
pointer which becomes a dangling pointer when the local shared_ptr is destroyed;
instead preserve ownership by returning or passing a shared_ptr (e.g., change
the API to return std::shared_ptr<BurstParams> or change the out parameter to
std::shared_ptr<BurstParams>&) and assign the dequeued shared_ptr directly (do
not call .get()), or if the queue guarantees ownership transfer store the
dequeued shared_ptr in a caller-visible container to extend its lifetime; update
the function signature and all call sites accordingly so the burst is kept alive
by shared_ptr rather than returning a raw pointer.
| } else if (media_sender_builder_->memory_pool_location_ == MemoryKind::HOST_PINNED) { | ||
| cudaError_t cuda_status = cudaHostAlloc(&pool_buffer, pool_buffer_size, cudaHostAllocDefault); | ||
| if (cuda_status != cudaSuccess) { | ||
| HOLOSCAN_LOG_ERROR("Failed to allocate pinned host memory: {}", | ||
| cudaGetErrorString(cuda_status)); | ||
| cudaFree(pool_buffer); | ||
| return nullptr; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Wrong API used to free host-pinned memory.
When cudaHostAlloc fails, cudaFree is called on pool_buffer. However, pool_buffer was never successfully allocated (the allocation just failed), so calling cudaFree on an uninitialized/null pointer is undefined behavior. Additionally, if the intent was cleanup, cudaFreeHost should be used for memory allocated with cudaHostAlloc, not cudaFree.
} else if (media_sender_builder_->memory_pool_location_ == MemoryKind::HOST_PINNED) {
cudaError_t cuda_status = cudaHostAlloc(&pool_buffer, pool_buffer_size, cudaHostAllocDefault);
if (cuda_status != cudaSuccess) {
HOLOSCAN_LOG_ERROR("Failed to allocate pinned host memory: {}",
cudaGetErrorString(cuda_status));
- cudaFree(pool_buffer);
return nullptr;
}🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp
around lines 325-332, the code calls cudaFree after cudaHostAlloc failure on
pool_buffer which is undefined and the wrong API for host-pinned memory;
initialize pool_buffer to nullptr before allocation, remove the
cudaFree/cudaFreeHost call in the immediate failure branch (just log the error
and return nullptr), and if you need to free previously allocated pinned memory
in other error paths use cudaFreeHost (not cudaFree) to release host-pinned
allocations.
Enhanced the Rivermax RX manager to support multiple streams on the same thread with stream-specific packet processing and queueing capabilities.
Enhance TX manager to support sending multiple streams on the same thread.
Key changes:
AppSettingin order to set threads setting. Each thread has it's dedicated core, ID and streams settings.This PR is rebased to Rony's PR/970.
The last 4 commits are new.
Summary by CodeRabbit
New Features
Framework Updates
✏️ Tip: You can customize this high-level summary in your review settings.