/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
FWStorage/XrdAdaptor/src/XrdFile.cc
523 строки
17 KB
Chris Jones
Moved Utilities/XrdAdaptor to FWStorage/XrdAdaptor
10 дек 2025, 18:22
10 дек 2025, 18:22
d455443
Код
Авторство
О чём код?
#include "FWStorage/XrdAdaptor/src/XrdFile.h" #include "FWStorage/XrdAdaptor/src/XrdRequestManager.h" #include "FWCore/Utilities/interface/EDMException.h" #include "FWCore/MessageLogger/interface/MessageLogger.h" #include "FWCore/Utilities/interface/Likely.h" #include <vector> #include <sstream> #include <iostream> #include <cassert> #include <chrono> using namespace XrdAdaptor; // To be re-enabled when the monitoring interface is back. //static const char *kCrabJobIdEnv = "CRAB_UNIQUE_JOB_ID"; static constexpr int XRD_CL_MAX_CHUNK = 512 * 1024; static constexpr int XRD_CL_MAX_SIZE = 1024; static constexpr int XRD_CL_MAX_READ_SIZE = (8 * 1024 * 1024); using namespace edm::storage; XrdFile::XrdFile() : m_offset(0), m_size(-1), m_close(false), m_name(), m_op_count(0) {} XrdFile::XrdFile(const char *name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) : m_offset(0), m_size(-1), m_close(false), m_name(), m_op_count(0) { open(name, flags, perms); } XrdFile::XrdFile(const std::string &name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) : m_offset(0), m_size(-1), m_close(false), m_name(), m_op_count(0) { open(name.c_str(), flags, perms); } XrdFile::~XrdFile() { if (m_close) edm::LogError("XrdFileError") << "Destructor called on XROOTD file '" << m_name << "' but the file is still open"; } ////////////////////////////////////////////////////////////////////// void XrdFile::create(const char *name, bool exclusive /* = false */, int perms /* = 066 */) { open(name, (IOFlags::OpenCreate | IOFlags::OpenWrite | IOFlags::OpenTruncate | (exclusive ? IOFlags::OpenExclusive : 0)), perms); } void XrdFile::create(const std::string &name, bool exclusive /* = false */, int perms /* = 066 */) { open(name.c_str(), (IOFlags::OpenCreate | IOFlags::OpenWrite | IOFlags::OpenTruncate | (exclusive ? IOFlags::OpenExclusive : 0)), perms); } void XrdFile::open(const std::string &name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) { open(name.c_str(), flags, perms); } void XrdFile::open(const char *name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) { // Actual open if ((name == nullptr) || (*name == 0)) { edm::Exception ex(edm::errors::FileOpenError); ex << "Cannot open a file without a name"; ex.addContext("Calling XrdFile::open()"); throw ex; } if ((flags & (IOFlags::OpenRead | IOFlags::OpenWrite)) == 0) { edm::Exception ex(edm::errors::FileOpenError); ex << "Must open file '" << name << "' at least for read or write"; ex.addContext("Calling XrdFile::open()"); throw ex; } // Translate our flags to system flags XrdCl::OpenFlags::Flags openflags = XrdCl::OpenFlags::None; if (flags & IOFlags::OpenWrite) openflags |= XrdCl::OpenFlags::Update; else if (flags & IOFlags::OpenRead) openflags |= XrdCl::OpenFlags::Read; if (flags & IOFlags::OpenAppend) { edm::Exception ex(edm::errors::FileOpenError); ex << "Opening file '" << name << "' in append mode not supported"; ex.addContext("Calling XrdFile::open()"); throw ex; } if (flags & IOFlags::OpenCreate) { if (!(flags & IOFlags::OpenExclusive)) openflags |= XrdCl::OpenFlags::Delete; openflags |= XrdCl::OpenFlags::New; openflags |= XrdCl::OpenFlags::MakePath; } if ((flags & IOFlags::OpenTruncate) && (flags & IOFlags::OpenWrite)) openflags |= XrdCl::OpenFlags::Delete; // Translate mode flags XrdCl::Access::Mode modeflags = XrdCl::Access::None; modeflags |= (perms & S_IRUSR) ? XrdCl::Access::UR : XrdCl::Access::None; modeflags |= (perms & S_IWUSR) ? XrdCl::Access::UW : XrdCl::Access::None; modeflags |= (perms & S_IXUSR) ? XrdCl::Access::UX : XrdCl::Access::None; modeflags |= (perms & S_IRGRP) ? XrdCl::Access::GR : XrdCl::Access::None; modeflags |= (perms & S_IWGRP) ? XrdCl::Access::GW : XrdCl::Access::None; modeflags |= (perms & S_IXGRP) ? XrdCl::Access::GX : XrdCl::Access::None; modeflags |= (perms & S_IROTH) ? XrdCl::Access::GR : XrdCl::Access::None; modeflags |= (perms & S_IWOTH) ? XrdCl::Access::GW : XrdCl::Access::None; modeflags |= (perms & S_IXOTH) ? XrdCl::Access::GX : XrdCl::Access::None; m_requestmanager = RequestManager::getInstance(name, openflags, modeflags); m_name = name; // Stat the file so we can keep track of the offset better. auto file = getActiveFile(); XrdCl::XRootDStatus status; XrdCl::StatInfo *statInfo = nullptr; if (!(status = file->Stat(false, statInfo)).IsOK()) { edm::Exception ex(edm::errors::FileOpenError); ex << "XrdCl::File::Stat(name='" << name << ") => error '" << status.ToStr() << "' (errno=" << status.errNo << ", code=" << status.code << ")"; ex.addContext("Calling XrdFile::open()"); addConnection(ex); throw ex; } assert(statInfo); m_size = statInfo->GetSize(); delete (statInfo); m_offset = 0; m_close = true; // Send the monitoring info, if available. // Note: std::getenv is not reentrant. // Commenting out until this is available in the new client. /* char * crabJobId = std::getenv(kCrabJobIdEnv); if (crabJobId) { kXR_unt32 dictId; m_file->SendMonitoringInfo(crabJobId, &dictId); edm::LogInfo("XrdFileInfo") << "Set monitoring ID to " << crabJobId << " with resulting dictId " << dictId << "."; } */ edm::LogInfo("XrdFileInfo") << "Opened " << m_name; std::vector<std::string> sources; m_requestmanager->getActiveSourceNames(sources); std::stringstream ss; ss << "Active sources: "; for (auto const &it : sources) ss << it << ", "; edm::LogInfo("XrdFileInfo") << ss.str(); } void XrdFile::close() { if (!m_requestmanager.get()) { edm::LogError("XrdFileError") << "XrdFile::close(name='" << m_name << "') called but the file is not open"; m_close = false; return; } m_requestmanager = nullptr; // propagate_const<T> has no reset() function m_close = false; m_offset = 0; m_size = -1; edm::LogInfo("XrdFileInfo") << "Closed " << m_name; } void XrdFile::abort() { m_requestmanager = nullptr; // propagate_const<T> has no reset() function m_close = false; m_offset = 0; m_size = -1; } ////////////////////////////////////////////////////////////////////// IOSize XrdFile::read(void *into, IOSize n) { if (n > 0x7fffffff) { edm::Exception ex(edm::errors::FileReadError); ex << "XrdFile::read(name='" << m_name << "', n=" << n << ") too many bytes, limit is 0x7fffffff"; ex.addContext("Calling XrdFile::read()"); addConnection(ex); throw ex; } uint32_t bytesRead = m_requestmanager->handle(into, n, m_offset).get(); m_offset += bytesRead; return bytesRead; } IOSize XrdFile::read(void *into, IOSize n, IOOffset pos) { if (n > 0x7fffffff) { edm::Exception ex(edm::errors::FileReadError); ex << "XrdFile::read(name='" << m_name << "', n=" << n << ") exceeds read size limit 0x7fffffff"; ex.addContext("Calling XrdFile::read()"); addConnection(ex); throw ex; } if (n == 0) { return 0; } // In some cases, the IO layers above us (particularly, if lazy-download is // enabled) will emit very large reads. We break this up into multiple // reads in order to avoid hitting timeouts. std::future<IOSize> prev_future, cur_future; IOSize bytesRead = 0, prev_future_expected = 0, cur_future_expected = 0; bool readReturnedShort = false; // Check the status of a read operation; updates bytesRead and // readReturnedShort. auto check_read = [&](std::future<IOSize> &future, IOSize expected) { if (!future.valid()) { return; } IOSize result = future.get(); if (readReturnedShort && (result != 0)) { edm::Exception ex(edm::errors::FileReadError); ex << "XrdFile::read(name='" << m_name << "', n=" << n << ") remote server returned non-zero length read after EOF."; ex.addContext("Calling XrdFile::read()"); addConnection(ex); throw ex; } else if (result != expected) { readReturnedShort = true; } bytesRead += result; }; while (n) { IOSize chunk = std::min(n, static_cast<IOSize>(XRD_CL_MAX_READ_SIZE)); // Save prior read state; issue new read. prev_future = std::move(cur_future); prev_future_expected = cur_future_expected; cur_future = m_requestmanager->handle(into, chunk, pos); cur_future_expected = chunk; // Wait for the prior read; update bytesRead. check_read(prev_future, prev_future_expected); // Update counters. into = static_cast<char *>(into) + chunk; n -= chunk; pos += chunk; } // Wait for the last read to finish. check_read(cur_future, cur_future_expected); return bytesRead; } // This method is rarely used by CMS; hence, it is a small wrapper and not efficient. IOSize XrdFile::readv(IOBuffer *into, IOSize n) { std::vector<IOPosBuffer> new_buf; new_buf.reserve(n); IOOffset off = 0; for (IOSize i = 0; i < n; i++) { IOSize size = into[i].size(); new_buf[i] = IOPosBuffer(off, into[i].data(), size); off += size; } return readv(&(new_buf[0]), n); } /* * A vectored scatter-gather read. * Returns the total number of bytes successfully read. */ IOSize XrdFile::readv(IOPosBuffer *into, IOSize n) { // A trivial vector read - unlikely, considering ROOT data format. if (UNLIKELY(n == 0)) { return 0; } if (UNLIKELY(n == 1)) { return read(into[0].data(), into[0].size(), into[0].offset()); } auto cl = std::make_shared<std::vector<IOPosBuffer>>(); // CMSSW may issue large readv's; Xrootd is only able to handle // 1024. Further, the splitting algorithm may slightly increase // the number of buffers. IOSize adjust = XRD_CL_MAX_SIZE - 2; cl->reserve(n > adjust ? adjust : n); IOSize idx = 0, last_idx = 0; IOSize final_result = 0; std::vector<std::pair<std::future<IOSize>, IOSize>> readv_futures; while (idx < n) { cl->clear(); IOSize size = 0; while (idx < n) { unsigned rollback_count = 1; IOSize current_size = size; IOOffset offset = into[idx].offset(); IOSize length = into[idx].size(); size += length; char *buffer = static_cast<char *>(into[idx].data()); while (length > XRD_CL_MAX_CHUNK) { IOPosBuffer ci; ci.set_size(XRD_CL_MAX_CHUNK); length -= XRD_CL_MAX_CHUNK; ci.set_offset(offset); offset += XRD_CL_MAX_CHUNK; ci.set_data(buffer); buffer += XRD_CL_MAX_CHUNK; cl->emplace_back(ci); rollback_count++; } IOPosBuffer ci; ci.set_size(length); ci.set_offset(offset); ci.set_data(buffer); cl->emplace_back(ci); if (cl->size() > adjust) { while (rollback_count--) cl->pop_back(); size = current_size; break; } else { idx++; } } try { readv_futures.emplace_back(m_requestmanager->handle(cl), size); } catch (edm::Exception &ex) { ex.addContext("Calling XrdFile::readv()"); throw; } // Assure that we have made some progress. assert(last_idx < idx); last_idx = idx; } std::chrono::time_point<std::chrono::steady_clock> start, end; start = std::chrono::steady_clock::now(); // If there are multiple readv calls, wait until all return until looking // at the results of any. This guarantees that all readv's have finished // by time we call .get() for the first time (in case one of the readv's // result in an exception). // // We cannot have outstanding readv's on function exit as the XrdCl may // write into the corresponding buffer at the same time as ROOT. if (readv_futures.size() > 1) { for (auto &readv_result : readv_futures) { if (readv_result.first.valid()) { readv_result.first.wait(); } } } for (auto &readv_result : readv_futures) { IOSize result = 0; try { const int retry_count = 5; for (int retries = 0; retries < retry_count; retries++) { try { if (readv_result.first.valid()) { result = readv_result.first.get(); } } catch (XrootdException &ex) { if ((retries != retry_count - 1) && (ex.getCode() == XrdCl::errInvalidResponse)) { edm::LogWarning("XrdAdaptorInternal") << "Got an invalid response from Xrootd server; retrying" << std::endl; result = m_requestmanager->handle(cl).get(); } else { throw; } } assert(result == readv_result.second); } } catch (edm::Exception &ex) { ex.addContext("Calling XrdFile::readv()"); throw; } catch (std::exception &ex) { edm::Exception newex(edm::errors::StdException); newex << "A std::exception was thrown when processing an xrootd request: " << ex.what(); newex.addContext("Calling XrdFile::readv()"); throw newex; } final_result += result; } end = std::chrono::steady_clock::now(); edm::LogVerbatim("XrdAdaptorInternal") << "[" << m_op_count.fetch_add(1) << "] Time for readv: " << static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()) << " (sub-readv requests: " << readv_futures.size() << ")" << std::endl; return final_result; } IOSize XrdFile::write(const void *from, IOSize n) { if (n > 0x7fffffff) { edm::Exception ex(edm::errors::FileWriteError); ex << "XrdFile::write(name='" << m_name << "', n=" << n << ") too many bytes, limit is 0x7fffffff"; ex.addContext("Calling XrdFile::write()"); addConnection(ex); throw ex; } auto file = getActiveFile(); XrdCl::XRootDStatus s = file->Write(m_offset, n, from); if (!s.IsOK()) { edm::Exception ex(edm::errors::FileWriteError); ex << "XrdFile::write(name='" << m_name << "', n=" << n << ") failed with error '" << s.ToStr() << "' (errno=" << s.errNo << ", code=" << s.code << ")"; ex.addContext("Calling XrdFile::write()"); addConnection(ex); throw ex; } m_offset += n; assert(m_size != -1); if (m_offset > m_size) m_size = m_offset; return n; } IOSize XrdFile::write(const void *from, IOSize n, IOOffset pos) { if (n > 0x7fffffff) { edm::Exception ex(edm::errors::FileWriteError); ex << "XrdFile::write(name='" << m_name << "', n=" << n << ") too many bytes, limit is 0x7fffffff"; ex.addContext("Calling XrdFile::write()"); addConnection(ex); throw ex; } auto file = getActiveFile(); XrdCl::XRootDStatus s = file->Write(pos, n, from); if (!s.IsOK()) { edm::Exception ex(edm::errors::FileWriteError); ex << "XrdFile::write(name='" << m_name << "', n=" << n << ") failed with error '" << s.ToStr() << "' (errno=" << s.errNo << ", code=" << s.code << ")"; ex.addContext("Calling XrdFile::write()"); addConnection(ex); throw ex; } assert(m_size != -1); if (static_cast<IOOffset>(pos + n) > m_size) m_size = pos + n; return n; } bool XrdFile::prefetch(const IOPosBuffer *what, IOSize n) { // The new Xrootd client does not contain any internal buffers. // Hence, prefetching is disabled completely. return false; } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// IOOffset XrdFile::position(IOOffset offset, Relative whence /* = SET */) { if (!m_requestmanager.get()) { cms::Exception ex("FilePositionError"); ex << "XrdFile::position() called on a closed file"; ex.addContext("Calling XrdFile::position()"); addConnection(ex); throw ex; } switch (whence) { case SET: m_offset = offset; break; case CURRENT: m_offset += offset; break; // TODO: None of this works with concurrent writers to the file. case END: assert(m_size != -1); m_offset = m_size + offset; break; default: cms::Exception ex("FilePositionError"); ex << "XrdFile::position() called with incorrect 'whence' parameter"; ex.addContext("Calling XrdFile::position()"); addConnection(ex); throw ex; } if (m_offset < 0) m_offset = 0; assert(m_size != -1); if (m_offset > m_size) m_size = m_offset; return m_offset; } void XrdFile::resize(IOOffset /* size */) { cms::Exception ex("FileResizeError"); ex << "XrdFile::resize(name='" << m_name << "') not implemented"; ex.addContext("Calling XrdFile::resize()"); addConnection(ex); throw ex; } std::shared_ptr<XrdCl::File> XrdFile::getActiveFile(void) { if (!m_requestmanager.get()) { cms::Exception ex("XrdFileLogicError"); ex << "Xrd::getActiveFile(name='" << m_name << "') no active request manager"; ex.addContext("Calling XrdFile::getActiveFile()"); m_requestmanager->addConnections(ex); m_close = false; throw ex; } return m_requestmanager->getActiveFile(); } void XrdFile::addConnection(cms::Exception &ex) { if (m_requestmanager.get()) { m_requestmanager->addConnections(ex); } }