Skip to content

Commit 4f0b244

Browse files
committed
Implement cudax::cufile and cudax::cufile_ref
1 parent fba9f83 commit 4f0b244

16 files changed

Lines changed: 1068 additions & 15 deletions

CMakePresets.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"cudax_ENABLE_DIALECT_CPP17": true,
9191
"cudax_ENABLE_DIALECT_CPP20": true,
9292
"cudax_ENABLE_CUFILE": false,
93+
"cudax_ENABLE_CUFILE_TESTS_RUN": false,
9394
"CCCL_C_Parallel_ENABLE_TESTING": true,
9495
"CCCL_C_Parallel_ENABLE_HEADER_TESTING": true,
9596
"CCCL_C_EXPERIMENTAL_STF_ENABLE_TESTING": true
@@ -251,7 +252,8 @@
251252
"cudax_ENABLE_CUDASTF_MATHLIBS": false,
252253
"cudax_ENABLE_DIALECT_CPP17": false,
253254
"cudax_ENABLE_DIALECT_CPP20": false,
254-
"cudax_ENABLE_CUFILE": false
255+
"cudax_ENABLE_CUFILE": false,
256+
"cudax_ENABLE_CUFILE_TESTS_RUN": false
255257
}
256258
},
257259
{

ci/build_cudax.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ if [[ -z "${cudax_ENABLE_CUFILE:-}" ]]; then
1818
fi
1919
fi
2020

21+
# todo: enable running tests in the CI
22+
if [[ -z "${cudax_ENABLE_CUFILE_TESTS_RUN:-}" ]]; then
23+
cudax_ENABLE_CUFILE_TESTS_RUN="false"
24+
fi
25+
2126
PRESET="cudax-cpp$CXX_STANDARD"
2227

23-
CMAKE_OPTIONS="-Dcudax_ENABLE_CUFILE=${cudax_ENABLE_CUFILE}"
28+
CMAKE_OPTIONS="-Dcudax_ENABLE_CUFILE=${cudax_ENABLE_CUFILE} -Dcudax_ENABLE_CUFILE_TESTS_RUN=${cudax_ENABLE_CUFILE_TESTS_RUN}"
2429

2530
configure_and_build_preset "CUDA Experimental" "$PRESET" "$CMAKE_OPTIONS"
2631

cudax/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@ option(
4040
"Enable STF tests/examples that use cublas/cusolver."
4141
OFF
4242
)
43-
option(cudax_ENABLE_CUFILE "Enable cuFile in CUDA Experimental" OFF)
43+
option(cudax_ENABLE_CUFILE "Enable cuFile in CUDA Experimental" ON)
44+
option(
45+
cudax_ENABLE_CUFILE_TESTS_RUN
46+
"Enable cuFile tests run on top of compilation"
47+
ON
48+
)
4449

4550
if (cudax_ENABLE_CUFILE)
4651
if (WIN32)
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
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

Comments
 (0)