|
| 1 | +//===----------------------------------------------------------------------===// |
| 2 | +// |
| 3 | +// Part of CUDA Experimental in CUDA C++ Core Libraries, |
| 4 | +// under the Apache License v2.0 with LLVM Exceptions. |
| 5 | +// See https://llvm.org/LICENSE.txt for license information. |
| 6 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 7 | +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. |
| 8 | +// |
| 9 | +//===----------------------------------------------------------------------===// |
| 10 | +#pragma once |
| 11 | + |
| 12 | +#include <cuda/std/detail/__config> |
| 13 | + |
| 14 | +#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) |
| 15 | +# pragma GCC system_header |
| 16 | +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) |
| 17 | +# pragma clang system_header |
| 18 | +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) |
| 19 | +# pragma system_header |
| 20 | +#endif // no system header |
| 21 | + |
| 22 | +#include <cuda/std/__exception/throw_error.h> |
| 23 | +#include <cuda/std/__utility/exchange.h> |
| 24 | +#include <cuda/std/string_view> |
| 25 | + |
| 26 | +#include <cuda/experimental/__cufile/cufile_ref.cuh> |
| 27 | +#include <cuda/experimental/__cufile/driver.cuh> |
| 28 | +#include <cuda/experimental/__cufile/exception.cuh> |
| 29 | +#include <cuda/experimental/__cufile/open_mode.cuh> |
| 30 | + |
| 31 | +#include <string> |
| 32 | + |
| 33 | +#include <cufile.h> |
| 34 | +#include <errno.h> |
| 35 | +#include <fcntl.h> |
| 36 | +#include <unistd.h> |
| 37 | + |
| 38 | +namespace cuda::experimental |
| 39 | +{ |
| 40 | +//! @brief An owning wrapper of \c CUfileHandle_t and the OS specific native file handle. |
| 41 | +class cufile : public cufile_ref |
| 42 | +{ |
| 43 | +public: |
| 44 | + using native_handle_type = __cufile_os_native_type; //!< The underlying OS native handle type. |
| 45 | + |
| 46 | +private: |
| 47 | + using __oflags_type = int; |
| 48 | + |
| 49 | + static constexpr native_handle_type __invalid_native_handle = -1; |
| 50 | + |
| 51 | + native_handle_type __native_handle_{__invalid_native_handle}; //< The native handle. |
| 52 | + |
| 53 | + //! @brief Constructs the object from native handle and cuFile file handle. |
| 54 | + _CCCL_HIDE_FROM_ABI cufile(cufile_ref __cufile_handle, native_handle_type __native_handle) noexcept |
| 55 | + : cufile_ref{__cufile_handle} |
| 56 | + , __native_handle_{__native_handle} |
| 57 | + {} |
| 58 | + |
| 59 | + //! @brief Make open flags from the \c cuda::cufile_open_mode. |
| 60 | + //! |
| 61 | + //! @param __om The cuFile open mode. |
| 62 | + //! |
| 63 | + //! @return The flags mask to be passed to open function. |
| 64 | + [[nodiscard]] static _CCCL_HOST_API constexpr __oflags_type __make_oflags(cufile_open_mode __om) noexcept |
| 65 | + { |
| 66 | + __oflags_type __ret{}; |
| 67 | + if ((__om & (cufile_open_mode::in | cufile_open_mode::out)) == (cufile_open_mode::in | cufile_open_mode::out)) |
| 68 | + { |
| 69 | + __ret |= O_RDWR | O_CREAT; |
| 70 | + } |
| 71 | + else if ((__om & cufile_open_mode::in) == cufile_open_mode::in) |
| 72 | + { |
| 73 | + __ret |= O_RDONLY; |
| 74 | + } |
| 75 | + else if ((__om & cufile_open_mode::out) == cufile_open_mode::out) |
| 76 | + { |
| 77 | + __ret |= O_WRONLY | O_CREAT; |
| 78 | + } |
| 79 | + |
| 80 | + __ret |= ((__om & cufile_open_mode::trunc) == cufile_open_mode::trunc) ? O_TRUNC : 0; |
| 81 | + __ret |= ((__om & cufile_open_mode::noreplace) == cufile_open_mode::noreplace) ? O_EXCL : 0; |
| 82 | + __ret |= ((__om & cufile_open_mode::direct) == cufile_open_mode::direct) ? O_DIRECT : 0; |
| 83 | + return __ret; |
| 84 | + } |
| 85 | + |
| 86 | + //! @brief Wrapper for opening the native handle. |
| 87 | + [[nodiscard]] static _CCCL_HOST_API native_handle_type __open_file(const char* __filename, __oflags_type __oflags) |
| 88 | + { |
| 89 | + // if O_CREAT flag is specified, use the same mode as if opened by fopend |
| 90 | + ::mode_t __ocreat_mode{}; |
| 91 | + if (__oflags & O_CREAT) |
| 92 | + { |
| 93 | + __ocreat_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
| 94 | + } |
| 95 | + |
| 96 | + int __fd = ::open(__filename, __oflags, __ocreat_mode); |
| 97 | + |
| 98 | + if (__fd == -1) |
| 99 | + { |
| 100 | + errno = 0; // clear errno |
| 101 | + ::cuda::std::__throw_runtime_error("Failed to open file."); |
| 102 | + } |
| 103 | + |
| 104 | + return __fd; |
| 105 | + } |
| 106 | + |
| 107 | + //! @brief Wrapper for retrieving the open mode. |
| 108 | + [[nodiscard]] static _CCCL_HOST_API cufile_open_mode __open_mode(native_handle_type __native_handle) |
| 109 | + { |
| 110 | + int __oflags = ::fcntl(__native_handle, F_GETFL); |
| 111 | + |
| 112 | + if (__oflags == -1) |
| 113 | + { |
| 114 | + errno = 0; // clear errno |
| 115 | + ::cuda::std::__throw_runtime_error("Failed to retrieve open flags."); |
| 116 | + } |
| 117 | + |
| 118 | + cufile_open_mode __om{}; |
| 119 | + if (__oflags & O_RDWR) |
| 120 | + { |
| 121 | + __om |= cufile_open_mode::in | cufile_open_mode::out; |
| 122 | + } |
| 123 | + else if (__oflags & O_RDONLY) |
| 124 | + { |
| 125 | + __om |= cufile_open_mode::in; |
| 126 | + } |
| 127 | + else if (__oflags & O_WRONLY) |
| 128 | + { |
| 129 | + __om |= cufile_open_mode::out; |
| 130 | + } |
| 131 | + __om |= (__oflags & O_TRUNC) ? cufile_open_mode::trunc : cufile_open_mode{}; |
| 132 | + __om |= (__oflags & O_EXCL) ? cufile_open_mode::noreplace : cufile_open_mode{}; |
| 133 | + __om |= (__oflags & O_DIRECT) ? cufile_open_mode::direct : cufile_open_mode{}; |
| 134 | + return __om; |
| 135 | + } |
| 136 | + |
| 137 | + //! @brief Wrapper for closing the native handle. |
| 138 | + [[nodiscard]] static _CCCL_HOST_API bool __close_file_no_throw(native_handle_type __native_handle) noexcept |
| 139 | + { |
| 140 | + return ::close(__native_handle) == 0; |
| 141 | + } |
| 142 | + |
| 143 | + //! @brief Wrapper for closing the native handle. Throws \c cuda::std::runtime_error if an error occurs. |
| 144 | + static _CCCL_HOST_API void __close_file(native_handle_type __native_handle) |
| 145 | + { |
| 146 | + if (!__close_file_no_throw(__native_handle)) |
| 147 | + { |
| 148 | + errno = 0; // clear errno |
| 149 | + ::cuda::std::__throw_runtime_error("Failed to close file."); |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | +public: |
| 154 | + //! @brief Make a cufile object from already existing native handle. |
| 155 | + //! |
| 156 | + // The ownership of the handle is transferred to the object and the handle is registered by the cuFile driver. |
| 157 | + //! |
| 158 | + //! @param __native_handle The native handle. |
| 159 | + //! |
| 160 | + //! @return The created cufile object. |
| 161 | + [[nodiscard]] static _CCCL_HOST_API cufile from_native_handle(native_handle_type __native_handle) |
| 162 | + { |
| 163 | + return cufile{cufile_driver.register_native_handle(__native_handle), __native_handle}; |
| 164 | + } |
| 165 | + |
| 166 | + _CCCL_HIDE_FROM_ABI cufile() noexcept = default; |
| 167 | + |
| 168 | + //! @brief Constructs the object by opening file @c __filename in mode @c __open_mode. |
| 169 | + //! |
| 170 | + //! @param __filename Path to the file. Must be a zero terminated string. |
| 171 | + //! @param __open_mode Open mode to open the file with. |
| 172 | + //! |
| 173 | + //! @throws cuda::std::runtime_error if the file cannot be opened. |
| 174 | + //! @throws cuda::cuda_error if a CUDA driver error occurs. |
| 175 | + //! @throws cuda::cufile_error if a cuFile driver error occurs. |
| 176 | + _CCCL_HOST_API cufile(const char* __filename, cufile_open_mode __open_mode) |
| 177 | + { |
| 178 | + __native_handle_ = __open_file(__filename, __make_oflags(__open_mode)); |
| 179 | + try |
| 180 | + { |
| 181 | + __cufile_handle_ = cufile_driver.register_native_handle(__native_handle_).get(); |
| 182 | + } |
| 183 | + catch (...) |
| 184 | + { |
| 185 | + __close_file(__native_handle_); |
| 186 | + throw; |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + cufile(const cufile&) = delete; |
| 191 | + |
| 192 | + //! @brief Move-construct a new @c cufile. |
| 193 | + //! |
| 194 | + //! @param __other The other @c cufile. |
| 195 | + //! |
| 196 | + //! @post `__other` is in moved-from state. |
| 197 | + _CCCL_HOST_API cufile(cufile&& __other) noexcept |
| 198 | + : cufile_ref{::cuda::std::exchange(__other.__cufile_handle_, nullptr)} |
| 199 | + , __native_handle_{::cuda::std::exchange(__other.__native_handle_, __invalid_native_handle)} |
| 200 | + {} |
| 201 | + |
| 202 | + cufile& operator=(const cufile&) = delete; |
| 203 | + |
| 204 | + //! @brief Move-assign from a @c cufile object. |
| 205 | + //! |
| 206 | + //! @param __other The other @c cufile. |
| 207 | + //! |
| 208 | + //! @post `__other` is in moved-from state. |
| 209 | + //! |
| 210 | + //! @throws cuda::std::runtime_error if the currently opened file fails to close. |
| 211 | + _CCCL_HOST_API cufile& operator=(cufile&& __other) |
| 212 | + { |
| 213 | + if (this != ::cuda::std::addressof(__other)) |
| 214 | + { |
| 215 | + close(); |
| 216 | + __native_handle_ = ::cuda::std::exchange(__other.__native_handle_, __invalid_native_handle); |
| 217 | + __cufile_handle_ = ::cuda::std::exchange(__other.__cufile_handle_, nullptr); |
| 218 | + } |
| 219 | + return *this; |
| 220 | + } |
| 221 | + |
| 222 | + //! @brief Destructor. Deregisters the cuFile file handle and closes the native handle. |
| 223 | + _CCCL_HOST_API ~cufile() |
| 224 | + { |
| 225 | + if (is_open()) |
| 226 | + { |
| 227 | + cufile_driver.deregister_native_handle(__cufile_handle_); |
| 228 | + [[maybe_unused]] const auto __ignore_close_retval = __close_file_no_throw(__native_handle_); |
| 229 | + } |
| 230 | + } |
| 231 | + |
| 232 | + //! @brief Queries whether the file is opened. |
| 233 | + //! |
| 234 | + //! @return True, if opened, false otherwise. |
| 235 | + [[nodiscard]] _CCCL_HOST_API bool is_open() const noexcept |
| 236 | + { |
| 237 | + return __native_handle_ != __invalid_native_handle; |
| 238 | + } |
| 239 | + |
| 240 | + //! @brief Queries the open mode the object was opened with. |
| 241 | + //! |
| 242 | + //! @return The \c cuda::cufile_open_mode value if opened, empty value otherwise. |
| 243 | + [[nodiscard]] _CCCL_HOST_API cufile_open_mode open_mode() const |
| 244 | + { |
| 245 | + return is_open() ? __open_mode(__native_handle_) : cufile_open_mode{}; |
| 246 | + } |
| 247 | + |
| 248 | + //! @brief Opens file @c __filename in mode @c __open_mode. |
| 249 | + //! |
| 250 | + //! @param __filename Path to the file. |
| 251 | + //! @param __open_mode Open mode to open the file with. |
| 252 | + //! |
| 253 | + //! @throws cuda::std::runtime_error if the file cannot be opened or if a file is already opened. |
| 254 | + //! @throws cuda::cuda_error if a CUDA driver error occurs. |
| 255 | + //! @throws cuda::cufile_error if a cuFile driver error occurs. |
| 256 | + _CCCL_HOST_API void open(const char* __filename, cufile_open_mode __open_mode) |
| 257 | + { |
| 258 | + if (is_open()) |
| 259 | + { |
| 260 | + ::cuda::std::__throw_runtime_error("File is already opened."); |
| 261 | + } |
| 262 | + |
| 263 | + __native_handle_ = __open_file(__filename, __make_oflags(__open_mode)); |
| 264 | + |
| 265 | + try |
| 266 | + { |
| 267 | + __cufile_handle_ = cufile_driver.register_native_handle(__native_handle_).get(); |
| 268 | + } |
| 269 | + catch (...) |
| 270 | + { |
| 271 | + __close_file(::cuda::std::exchange(__native_handle_, __invalid_native_handle)); |
| 272 | + throw; |
| 273 | + } |
| 274 | + } |
| 275 | + |
| 276 | + //! @brief Closes the currently opened file. If there is no opened file, no action is taken. |
| 277 | + //! |
| 278 | + //! @throws cuda::std::runtime_error if the file fails to close. |
| 279 | + //! @throws cuda::cuda_error if a CUDA driver error occurs. |
| 280 | + //! @throws cuda::cufile_error if a cuFile driver error occurs. |
| 281 | + _CCCL_HOST_API void close() |
| 282 | + { |
| 283 | + if (!is_open()) |
| 284 | + { |
| 285 | + return; |
| 286 | + } |
| 287 | + |
| 288 | + cufile_driver.deregister_native_handle(::cuda::std::exchange(__cufile_handle_, nullptr)); |
| 289 | + __close_file(::cuda::std::exchange(__native_handle_, __invalid_native_handle)); |
| 290 | + } |
| 291 | + |
| 292 | + //! @brief Gets the OS native handle. |
| 293 | + //! |
| 294 | + //! @return The native handle. |
| 295 | + [[nodiscard]] _CCCL_HOST_API native_handle_type native_handle() const noexcept |
| 296 | + { |
| 297 | + return __native_handle_; |
| 298 | + } |
| 299 | + |
| 300 | + //! @brief Deregisters the cuFile file handle and releases the native handle. The ownership of the native handle is |
| 301 | + //! transferred to the caller. |
| 302 | + //! |
| 303 | + //! @returns The native handle. |
| 304 | + [[nodiscard]] _CCCL_HOST_API native_handle_type release() noexcept |
| 305 | + { |
| 306 | + cufile_driver.deregister_native_handle(::cuda::std::exchange(__cufile_handle_, nullptr)); |
| 307 | + return ::cuda::std::exchange(__native_handle_, __invalid_native_handle); |
| 308 | + } |
| 309 | +}; |
| 310 | +} // namespace cuda::experimental |
0 commit comments