Project
Loading...
Searching...
No Matches
XRootDByteSource.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include "XRootDByteSource.h"
13#include "AODNative/Errors.h"
14#include <XrdCl/XrdClFile.hh>
15#include <XrdCl/XrdClXRootDResponses.hh>
16#include <condition_variable>
17#include <mutex>
18#include <vector>
19
20namespace o2::aod::native
21{
22
23namespace
24{
25// XrdCl's defaults for kXR_readv: chunks of at most 2097136 bytes, at most 1024
26// of them per request. Larger ranges are read with plain reads, in pieces small
27// enough for the uint32_t size of XrdCl::File::Read.
28constexpr size_t kMaxVectorChunk = 2097136;
29constexpr size_t kMaxVectorChunks = 1024;
30constexpr size_t kMaxPlainRead = 64 * 1024 * 1024;
31
34class Completion
35{
36 public:
37 explicit Completion(size_t pending) : mPending(pending) {}
38
39 void done(std::string error)
40 {
41 std::lock_guard lock{mMutex};
42 if (!error.empty() && mError.empty()) {
43 mError = std::move(error);
44 }
45 if (--mPending == 0) {
46 mDone.notify_all();
47 }
48 }
49
52 std::string wait()
53 {
54 std::unique_lock lock{mMutex};
55 mDone.wait(lock, [this] { return mPending == 0; });
56 return mError;
57 }
58
59 private:
60 std::mutex mMutex;
61 std::condition_variable mDone;
62 size_t mPending;
63 std::string mError;
64};
65
67class Reply : public XrdCl::ResponseHandler
68{
69 public:
70 Reply(Completion& completion, size_t expected) : mCompletion(completion), mExpected(expected) {}
71
72 void HandleResponse(XrdCl::XRootDStatus* status, XrdCl::AnyObject* response) override
73 {
74 std::string error;
75 if (!status->IsOK()) {
76 error = status->ToString();
77 } else if (auto got = received(response); got != mExpected) {
78 error = "short read: " + std::to_string(got) + " of " + std::to_string(mExpected) + " bytes";
79 }
80 delete status;
81 delete response;
82 mCompletion.done(std::move(error));
83 delete this;
84 }
85
86 private:
87 static size_t received(XrdCl::AnyObject* response)
88 {
89 if (!response) {
90 return 0;
91 }
92 XrdCl::ChunkInfo* chunk = nullptr;
93 response->Get(chunk);
94 if (chunk) {
95 return chunk->length;
96 }
97 XrdCl::VectorReadInfo* vector = nullptr;
98 response->Get(vector);
99 return vector ? vector->GetSize() : 0;
100 }
101
102 Completion& mCompletion;
103 size_t mExpected;
104};
105
106struct PlainRead {
107 uint64_t offset;
108 uint32_t length;
109 void* dst;
110};
111
112struct VectorRead {
113 XrdCl::ChunkList chunks;
114 size_t bytes = 0;
115};
116} // namespace
117
119 : mName(url.substr(0, url.find('?'))), mFile(std::make_unique<XrdCl::File>())
120{
121 auto status = mFile->Open(url, XrdCl::OpenFlags::Read);
122 if (!status.IsOK()) {
123 throw IoError(mName, status.ToString());
124 }
125 XrdCl::StatInfo* info = nullptr;
126 status = mFile->Stat(false, info);
127 if (!status.IsOK() || !info) {
128 delete info;
129 throw IoError(mName, "stat failed: " + status.ToString());
130 }
131 mSize = info->GetSize();
132 delete info;
133}
134
136{
137 if (mFile->IsOpen()) {
138 [[maybe_unused]] auto status = mFile->Close();
139 }
140}
141
142void XRootDByteSource::checkRange(uint64_t off, size_t len) const
143{
144 if (off > mSize || len > mSize - off) {
145 throw CorruptData(mName, off, "read of " + std::to_string(len) + " bytes past the end of a " + std::to_string(mSize) + " byte file");
146 }
147}
148
149void XRootDByteSource::readInto(uint64_t off, size_t len, void* dst)
150{
151 ReadRequest request{off, len, dst};
152 readvInto({&request, 1});
153}
154
155void XRootDByteSource::readvInto(std::span<ReadRequest> requests)
156{
157 std::vector<PlainRead> plain;
158 std::vector<VectorRead> vectors;
159 size_t total = 0;
160 for (auto const& r : requests) {
161 checkRange(r.offset, r.length);
162 total += r.length;
163 if (r.length > kMaxVectorChunk) {
164 for (size_t done = 0; done < r.length; done += kMaxPlainRead) {
165 auto n = std::min(kMaxPlainRead, r.length - done);
166 plain.push_back({r.offset + done, (uint32_t)n, (uint8_t*)r.dst + done});
167 }
168 continue;
169 }
170 if (r.length == 0) {
171 continue;
172 }
173 if (vectors.empty() || vectors.back().chunks.size() == kMaxVectorChunks) {
174 vectors.emplace_back();
175 }
176 vectors.back().chunks.emplace_back(r.offset, (uint32_t)r.length, r.dst);
177 vectors.back().bytes += r.length;
178 }
179 // A vector of one chunk is a plain read.
180 for (auto& v : vectors) {
181 if (v.chunks.size() == 1) {
182 auto& c = v.chunks.front();
183 plain.push_back({c.offset, c.length, c.buffer});
184 v.chunks.clear();
185 }
186 }
187 std::erase_if(vectors, [](VectorRead const& v) { return v.chunks.empty(); });
188 if (plain.empty() && vectors.empty()) {
189 return;
190 }
191
192 Completion completion{plain.size() + vectors.size()};
193 for (auto const& p : plain) {
194 auto* reply = new Reply(completion, p.length);
195 auto status = mFile->Read(p.offset, p.length, p.dst, reply);
196 if (!status.IsOK()) {
197 delete reply;
198 completion.done(status.ToString());
199 }
200 }
201 for (auto const& v : vectors) {
202 auto* reply = new Reply(completion, v.bytes);
203 auto status = mFile->VectorRead(v.chunks, nullptr, reply);
204 if (!status.IsOK()) {
205 delete reply;
206 completion.done(status.ToString());
207 }
208 }
209 auto error = completion.wait();
210 if (!error.empty()) {
211 throw IoError(mName, error);
212 }
213 mReadCalls.fetch_add(plain.size() + vectors.size(), std::memory_order_relaxed);
214 mBytesRead.fetch_add(total, std::memory_order_relaxed);
215}
216
217} // namespace o2::aod::native
std::string url
bool done
char error[CURL_ERROR_SIZE]
size_t received
uint32_t c
Definition RawData.h:2
XrdCl::ChunkList chunks
size_t bytes
std::string url() const override
Without the query, which may hold an access token.
void readvInto(std::span< ReadRequest > requests) override
XRootDByteSource(std::string url, Options const &options)
void readInto(uint64_t off, size_t len, void *dst) override
Throws IoError, or CorruptData when the range is past the end of the file.
GLdouble n
Definition glcorearb.h:1982
const GLdouble * v
Definition glcorearb.h:832
GLenum GLenum dst
Definition glcorearb.h:1767
GLintptr offset
Definition glcorearb.h:660
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLboolean r
Definition glcorearb.h:1233
GLenum GLenum GLsizei len
Definition glcorearb.h:4232
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
Failure of the underlying transport.
Definition Errors.h:69
std::map< std::string, ID > expected