1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 
5 #ifndef OPENCV_DNN_SRC_CUDA4DNN_CSL_STREAM_HPP
6 #define OPENCV_DNN_SRC_CUDA4DNN_CSL_STREAM_HPP
7 
8 #include "error.hpp"
9 
10 #include <opencv2/core.hpp>
11 #include <opencv2/core/utils/logger.hpp>
12 
13 #include <cuda_runtime_api.h>
14 
15 #include <memory>
16 #include <sstream>
17 #include <utility>
18 
19 namespace cv { namespace dnn { namespace cuda4dnn { namespace csl {
20 
21     /** \file stream.hpp
22      *
23      * Default streams are not supported as they limit flexiblity. All operations are always
24      * carried out in non-default streams in the CUDA backend. The stream classes sacrifice
25      * the ability to support default streams in exchange for better error detection. That is,
26      * a default constructed stream represents no stream and any attempt to use it will throw an
27      * exception.
28      */
29 
30     /** @brief non-copyable smart CUDA stream
31      *
32      * UniqueStream is a smart non-sharable wrapper for CUDA stream handle which ensures that
33      * the handle is destroyed after use. Unless explicitly specified by a constructor argument,
34      * the stream object does not represent any stream by default.
35      */
36     class UniqueStream {
37     public:
UniqueStream()38         UniqueStream() noexcept : stream{ 0 } { }
39         UniqueStream(UniqueStream&) = delete;
UniqueStream(UniqueStream && other)40         UniqueStream(UniqueStream&& other) noexcept {
41             stream = other.stream;
42             other.stream = 0;
43         }
44 
45         /** creates a non-default stream if `create` is true; otherwise, no stream is created */
UniqueStream(bool create)46         UniqueStream(bool create) : stream{ 0 } {
47             if (create) {
48                 /* we create non-blocking streams to avoid inrerruptions from users using the default stream */
49                 CUDA4DNN_CHECK_CUDA(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));
50             }
51         }
52 
~UniqueStream()53         ~UniqueStream() {
54             try {
55                 /* cudaStreamDestroy does not throw if a valid stream is passed unless a previous
56                  * asynchronous operation errored.
57                  */
58                 if (stream != 0)
59                     CUDA4DNN_CHECK_CUDA(cudaStreamDestroy(stream));
60             } catch (const CUDAException& ex) {
61                 std::ostringstream os;
62                 os << "Asynchronous exception caught during CUDA stream destruction.\n";
63                 os << ex.what();
64                 os << "Exception will be ignored.\n";
65                 CV_LOG_WARNING(0, os.str().c_str());
66             }
67         }
68 
69         UniqueStream& operator=(const UniqueStream&) = delete;
operator =(UniqueStream && other)70         UniqueStream& operator=(UniqueStream&& other) noexcept {
71             CV_Assert(other);
72             if (&other != this) {
73                 UniqueStream(std::move(*this)); /* destroy current stream */
74                 stream = other.stream;
75                 other.stream = 0;
76             }
77             return *this;
78         }
79 
80         /** returns the raw CUDA stream handle */
get() const81         cudaStream_t get() const noexcept {
82             CV_Assert(stream);
83             return stream;
84         }
85 
86         /** blocks the calling thread until all pending operations in the stream finish */
synchronize() const87         void synchronize() const {
88             CV_Assert(stream);
89             CUDA4DNN_CHECK_CUDA(cudaStreamSynchronize(stream));
90         }
91 
92         /** returns true if there are pending operations in the stream */
busy() const93         bool busy() const {
94             CV_Assert(stream);
95 
96             auto status = cudaStreamQuery(stream);
97             if (status == cudaErrorNotReady)
98                 return true;
99             CUDA4DNN_CHECK_CUDA(status);
100             return false;
101         }
102 
103         /** returns true if the stream is valid */
operator bool() const104         explicit operator bool() const noexcept { return static_cast<bool>(stream); }
105 
106     private:
107         cudaStream_t stream;
108     };
109 
110     /** @brief sharable smart CUDA stream
111      *
112      * Stream is a smart sharable wrapper for CUDA stream handle which ensures that
113      * the handle is destroyed after use. Unless explicitly specified in the constructor,
114      * the stream object represents no stream.
115      */
116     class Stream {
117     public:
Stream()118         Stream() { }
119         Stream(const Stream&) = default;
120         Stream(Stream&&) = default;
121 
122         /** if \p create is `true`, a new stream will be created; otherwise, no stream is created */
Stream(bool create)123         Stream(bool create) {
124             if (create)
125                 stream = std::make_shared<UniqueStream>(create);
126         }
127 
128         Stream& operator=(const Stream&) = default;
129         Stream& operator=(Stream&&) = default;
130 
131         /** blocks the caller thread until all operations in the stream are complete */
synchronize() const132         void synchronize() const {
133             CV_Assert(stream);
134             stream->synchronize();
135         }
136 
137         /** returns true if there are operations pending in the stream */
busy() const138         bool busy() const {
139             CV_Assert(stream);
140             return stream->busy();
141         }
142 
143         /** returns true if the object points has a valid stream */
operator bool() const144         explicit operator bool() const noexcept {
145             if (!stream)
146                 return false;
147             return stream->operator bool();
148         }
149 
get() const150         cudaStream_t get() const noexcept {
151             CV_Assert(stream);
152             return stream->get();
153         }
154 
155     private:
156         std::shared_ptr<UniqueStream> stream;
157     };
158 
159 }}}} /* namespace cv::dnn::cuda4dnn::csl */
160 
161 #endif /* OPENCV_DNN_SRC_CUDA4DNN_CSL_STREAM_HPP */
162