CUDADirective-1.2¶
Manage resource lifetimes with constructors and destructors
Required inputs: IR, StaticSemanticAnalysis
CUDA DIRECTIVE 1.2 [raii] Manage resource lifetimes with constructors and destructors
Use resource management types that acquire resources during construction and release resources upon de- struction. This pattern is commonly known as Resource Acquisition is Initialization (RAII).
Scope: Host, Device.
Audience: CUDA C++, CUDA Libraries.
Hardware Applicability: All Compute Capabilities.
Rationale
Manual resource management is unreliable. Manually managing resource lifetimes is prone to programmer error as it is easy to forget to release resources. Additionally, it is not exception safe. RAII is a zero-cost alternative that is less error prone and operates correctly in the face of exceptions.
Example 1 (Bad)
# include <memory> # include <cassert> int main() { int32_t* raw_u = nullptr; cudaError_t const error0 = cudaMalloc(&raw_u, sizeof(int32_t)); assert(cudaSuccess == error0); // If an exception or abnormal exit occurs, the resource will not be reclaimed. // There is no recourse if manual reclamation is forgotten. cudaError_t const error1 = cudaFree(raw_u); assert(cudaSuccess == error1); }
Example 2 (Good)
# include <memory> # include <cassert> int main() { auto deleter = [] (int32_t* ptr) { cudaError_t const error0 = cudaFree(ptr); assert(cudaSuccess == error0); }; int32_t* raw_u = nullptr; cudaError_t const error1 = cudaMalloc(&raw_u, sizeof(int32_t)); assert(cudaSuccess == error1); std::unique_ptr<int32_t, decltype(deleter)> up(raw_u, deleter); // Reclamation occurs when `up` is destroyed, either at the natural end of // the scope or due to an exception or abnormal exit. }
Example 3 (Bad)
# include <memory> # include <cassert> int main() { CUstream_st* raw_stream = nullptr; cudaError_t const error0 = cudaStreamCreate(&raw_stream); assert(cudaSuccess == error0); // If an exception or abnormal exit occurs, the resource will not be reclaimed. // There is no recourse if manual reclamation is forgotten. cudaError_t const error1 = cudaStreamDestroy(raw_stream); assert(cudaSuccess == error1); }
Example 4 (Good)
# include <memory> # include <cassert> int main() { auto deleter = [] (CUstream_st* ptr) { cudaError_t const error0 = cudaStreamDestroy(ptr); assert(cudaSuccess == error0); }; CUstream_st* raw_stream = nullptr; cudaError_t const error1 = cudaStreamCreate(&raw_stream); assert(cudaSuccess == error1); std::unique_ptr<CUstream_st, decltype(deleter)> stream(raw_stream, deleter); // Reclamation occurs when `stream` is destroyed, either at the natural end of // the scope or due to an exception or abnormal exit. }Excerpt from NVIDIA CUDA C++ Guidelines for robust and safety-critical programming, Version 3.0.1, Copyright (C) 2018-2023 NVIDIA Corporation.
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
not_passed_to_smart_ptr |
Use RAII: Result of allocation is not only passed to smart pointer. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
allocating_routines¶
allocating_routines : set[bauhaus.analysis.config.QualifiedName] = {'cudaMalloc', 'cudaStreamCreate'}