Use io_uring for async file I/O
This commit is contained in:
parent
8115721913
commit
334bb1faa9
|
@ -90,7 +90,7 @@ set(BOOST_ARGS "REQUIRED;COMPONENTS;system;filesystem;iostreams")
|
||||||
use_package(TARGET_NAME Boost::system PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}")
|
use_package(TARGET_NAME Boost::system PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}")
|
||||||
use_package(TARGET_NAME Boost::filesystem PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}")
|
use_package(TARGET_NAME Boost::filesystem PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}")
|
||||||
use_package(TARGET_NAME Boost::iostreams PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}")
|
use_package(TARGET_NAME Boost::iostreams PACKAGE_NAME Boost PACKAGE_ARGS "${BOOST_ARGS}")
|
||||||
option(BOOST_ASIO_IO_URING OFF "enable use of io_uring")
|
option(BOOST_ASIO_IO_URING ON "enable use of io_uring")
|
||||||
if (BOOST_ASIO_IO_URING)
|
if (BOOST_ASIO_IO_URING)
|
||||||
list(APPEND META_PUBLIC_COMPILE_DEFINITIONS BOOST_ASIO_HAS_IO_URING BOOST_ASIO_DISABLE_EPOLL)
|
list(APPEND META_PUBLIC_COMPILE_DEFINITIONS BOOST_ASIO_HAS_IO_URING BOOST_ASIO_DISABLE_EPOLL)
|
||||||
use_pkg_config_module(PKG_CONFIG_MODULES "liburing" VISIBILITY PUBLIC)
|
use_pkg_config_module(PKG_CONFIG_MODULES "liburing" VISIBILITY PUBLIC)
|
||||||
|
|
|
@ -24,34 +24,45 @@ void BuildProcessSession::DataForWebSession::streamFile(
|
||||||
{
|
{
|
||||||
error = false;
|
error = false;
|
||||||
|
|
||||||
boost::beast::error_code error;
|
#ifdef BOOST_ASIO_HAS_FILE
|
||||||
m_file.open(filePath.data(), boost::beast::file_mode::scan, error);
|
auto ec = boost::system::error_code();
|
||||||
if (error) {
|
m_fileStream.open(filePath, boost::asio::stream_file::read_only, ec);
|
||||||
cerr << Phrases::WarningMessage << "Unable to open \"" << filePath << "\": " << error.message() << Phrases::EndFlush;
|
#else
|
||||||
|
auto error = boost::beast::error_code();
|
||||||
|
m_file.open(filePath.data(), boost::beast::file_mode::scan, ec);
|
||||||
|
#endif
|
||||||
|
if (ec) {
|
||||||
|
cerr << Phrases::WarningMessage << "Unable to open \"" << filePath << "\": " << ec.message() << Phrases::EndFlush;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto fileSize = m_file.size(error);
|
#ifdef BOOST_ASIO_HAS_FILE
|
||||||
|
const auto fileSize = m_fileStream.size(ec);
|
||||||
|
#else
|
||||||
|
const auto fileSize = m_file.size(ec);
|
||||||
|
#endif
|
||||||
m_bytesToSendFromFile.store(fileSize);
|
m_bytesToSendFromFile.store(fileSize);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
if (error) {
|
if (ec) {
|
||||||
cerr << Phrases::WarningMessage << "Unable to determine size of \"" << filePath << "\": " << error.message() << Phrases::EndFlush;
|
cerr << Phrases::WarningMessage << "Unable to determine size of \"" << filePath << "\": " << ec.message() << Phrases::EndFlush;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_descriptor.assign(m_file.native_handle(), error);
|
#ifndef BOOST_ASIO_HAS_FILE
|
||||||
if (error) {
|
m_fileStream.assign(m_file.native_handle(), ec);
|
||||||
|
if (ec) {
|
||||||
m_bytesToSendFromFile.store(0);
|
m_bytesToSendFromFile.store(0);
|
||||||
cerr << Phrases::WarningMessage << "Unable to assign descriptor for \"" << filePath << "\": " << error.message() << Phrases::EndFlush;
|
cerr << Phrases::WarningMessage << "Unable to assign descriptor for \"" << filePath << "\": " << ec.message() << Phrases::EndFlush;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_descriptor.non_blocking(true, error);
|
m_fileStream.non_blocking(true, ec);
|
||||||
if (error) {
|
if (ec) {
|
||||||
m_bytesToSendFromFile.store(0);
|
m_bytesToSendFromFile.store(0);
|
||||||
cerr << Phrases::WarningMessage << "Unable to set descriptor for \"" << filePath << "\" to non-blocking mode: " << error.message()
|
cerr << Phrases::WarningMessage << "Unable to set descriptor for \"" << filePath << "\" to non-blocking mode: " << ec.message()
|
||||||
<< Phrases::EndFlush;
|
<< Phrases::EndFlush;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
m_fileBuffer = m_session.m_bufferPool.newBuffer();
|
m_fileBuffer = m_session.m_bufferPool.newBuffer();
|
||||||
m_descriptor.async_read_some(boost::asio::buffer(*m_fileBuffer, sizeof(std::min(fileSize, m_session.m_bufferPool.bufferSize()))),
|
m_fileStream.async_read_some(boost::asio::buffer(*m_fileBuffer, sizeof(std::min(fileSize, m_session.m_bufferPool.bufferSize()))),
|
||||||
std::bind(&DataForWebSession::writeFileData, this, std::ref(filePath), std::move(session), std::placeholders::_1, std::placeholders::_2));
|
std::bind(&DataForWebSession::writeFileData, this, std::ref(filePath), std::move(session), std::placeholders::_1, std::placeholders::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +76,7 @@ void BuildProcessSession::DataForWebSession::writeFileData(
|
||||||
return;
|
return;
|
||||||
} else if (eof) {
|
} else if (eof) {
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
m_descriptor.close(ec);
|
m_fileStream.close(ec);
|
||||||
}
|
}
|
||||||
// send file data to web client
|
// send file data to web client
|
||||||
if (bytesTransferred > m_bytesToSendFromFile) {
|
if (bytesTransferred > m_bytesToSendFromFile) {
|
||||||
|
@ -95,7 +106,7 @@ void BuildProcessSession::DataForWebSession::writeFileData(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// continue reading if there's more data
|
// continue reading if there's more data
|
||||||
m_descriptor.async_read_some(boost::asio::buffer(*m_fileBuffer, sizeof(std::min(bytesLeftToRead, m_session.m_bufferPool.bufferSize()))),
|
m_fileStream.async_read_some(boost::asio::buffer(*m_fileBuffer, sizeof(std::min(bytesLeftToRead, m_session.m_bufferPool.bufferSize()))),
|
||||||
std::bind(
|
std::bind(
|
||||||
&DataForWebSession::writeFileData, this, std::ref(filePath), std::move(session), std::placeholders::_1, std::placeholders::_2));
|
&DataForWebSession::writeFileData, this, std::ref(filePath), std::move(session), std::placeholders::_1, std::placeholders::_2));
|
||||||
});
|
});
|
||||||
|
@ -148,6 +159,16 @@ void BuildProcessSession::prepareLogFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// open logfile and a "file descriptor" for writing in a non-blocking way
|
// open logfile and a "file descriptor" for writing in a non-blocking way
|
||||||
|
#ifdef BOOST_ASIO_HAS_FILE
|
||||||
|
auto ec = boost::system::error_code();
|
||||||
|
m_logFileStream.open(
|
||||||
|
m_logFilePath, boost::asio::stream_file::write_only | boost::asio::stream_file::create | boost::asio::stream_file::truncate, ec);
|
||||||
|
if (ec) {
|
||||||
|
result.errorCode = std::error_code(ec.value(), ec.category());
|
||||||
|
result.error = CppUtilities::argsToString("unable to open \"", m_logFilePath, ": ", ec.message());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
auto ec = boost::beast::error_code();
|
auto ec = boost::beast::error_code();
|
||||||
m_logFile.open(m_logFilePath.data(), boost::beast::file_mode::write, ec);
|
m_logFile.open(m_logFilePath.data(), boost::beast::file_mode::write, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
|
@ -164,6 +185,7 @@ void BuildProcessSession::prepareLogFile()
|
||||||
result.error = CppUtilities::argsToString("unable to prepare descriptor for \"", m_logFilePath, ": ", e.what());
|
result.error = CppUtilities::argsToString("unable to prepare descriptor for \"", m_logFilePath, ": ", e.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildProcessSession::readMoreFromPipe()
|
void BuildProcessSession::readMoreFromPipe()
|
||||||
|
@ -201,7 +223,7 @@ void BuildProcessSession::writeCurrentBuffer(std::size_t bytesTransferred)
|
||||||
if (!m_logFileBuffers.error) {
|
if (!m_logFileBuffers.error) {
|
||||||
if (m_logFileBuffers.currentlySentBuffers.empty()) {
|
if (m_logFileBuffers.currentlySentBuffers.empty()) {
|
||||||
m_logFileBuffers.currentlySentBuffers.emplace_back(std::pair(m_buffer, bytesTransferred));
|
m_logFileBuffers.currentlySentBuffers.emplace_back(std::pair(m_buffer, bytesTransferred));
|
||||||
boost::asio::async_write(m_logFileDescriptor, boost::asio::buffer(m_buffer.get(), bytesTransferred),
|
boost::asio::async_write(m_logFileStream, boost::asio::buffer(m_buffer.get(), bytesTransferred),
|
||||||
std::bind(&BuildProcessSession::writeNextBufferToLogFile, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
std::bind(&BuildProcessSession::writeNextBufferToLogFile, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
||||||
} else {
|
} else {
|
||||||
m_logFileBuffers.outstandingBuffersToSend.emplace_back(std::pair(m_buffer, bytesTransferred));
|
m_logFileBuffers.outstandingBuffersToSend.emplace_back(std::pair(m_buffer, bytesTransferred));
|
||||||
|
@ -252,11 +274,7 @@ void BuildProcessSession::writeNextBufferToLogFile(const boost::system::error_co
|
||||||
m_logFileBuffers.currentlySentBuffers.clear();
|
m_logFileBuffers.currentlySentBuffers.clear();
|
||||||
// close the logfile when the process exited and we've written all the output
|
// close the logfile when the process exited and we've written all the output
|
||||||
if (m_logFileBuffers.outstandingBuffersToSend.empty() && m_exited.load()) {
|
if (m_logFileBuffers.outstandingBuffersToSend.empty() && m_exited.load()) {
|
||||||
auto closeError = boost::system::error_code();
|
closeLogFile();
|
||||||
m_logFile.close(closeError);
|
|
||||||
if (closeError) {
|
|
||||||
cerr << Phrases::WarningMessage << "Error closing \"" << m_logFilePath << "\": " << closeError.message() << Phrases::EndFlush;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_logFileBuffers.currentlySentBuffers.swap(m_logFileBuffers.outstandingBuffersToSend);
|
m_logFileBuffers.currentlySentBuffers.swap(m_logFileBuffers.outstandingBuffersToSend);
|
||||||
|
@ -265,7 +283,7 @@ void BuildProcessSession::writeNextBufferToLogFile(const boost::system::error_co
|
||||||
m_logFileBuffers.currentlySentBufferRefs.emplace_back(boost::asio::buffer(buffer.first.get(), buffer.second));
|
m_logFileBuffers.currentlySentBufferRefs.emplace_back(boost::asio::buffer(buffer.first.get(), buffer.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boost::asio::async_write(m_logFileDescriptor, m_logFileBuffers.currentlySentBufferRefs,
|
boost::asio::async_write(m_logFileStream, m_logFileBuffers.currentlySentBufferRefs,
|
||||||
std::bind(&BuildProcessSession::writeNextBufferToLogFile, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
std::bind(&BuildProcessSession::writeNextBufferToLogFile, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,15 +322,24 @@ void BuildProcessSession::writeNextBufferToWebSession(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BuildProcessSession::closeLogFile()
|
||||||
|
{
|
||||||
|
auto ec = boost::system::error_code();
|
||||||
|
#ifdef BOOST_ASIO_HAS_FILE
|
||||||
|
m_logFileStream.close(ec);
|
||||||
|
#else
|
||||||
|
m_logFile.close(ec);
|
||||||
|
#endif
|
||||||
|
if (ec) {
|
||||||
|
cerr << Phrases::WarningMessage << "Error closing \"" << m_logFilePath << "\": " << ec.message() << Phrases::EndFlush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BuildProcessSession::close()
|
void BuildProcessSession::close()
|
||||||
{
|
{
|
||||||
auto lock = std::lock_guard<std::mutex>(m_mutex);
|
auto lock = std::lock_guard<std::mutex>(m_mutex);
|
||||||
if (m_logFileBuffers.outstandingBuffersToSend.empty()) {
|
if (m_logFileBuffers.outstandingBuffersToSend.empty()) {
|
||||||
auto error = boost::system::error_code();
|
closeLogFile();
|
||||||
m_logFile.close(error);
|
|
||||||
if (error) {
|
|
||||||
cerr << Phrases::WarningMessage << "Error closing \"" << m_logFilePath << "\": " << error.message() << Phrases::EndFlush;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (auto &[session, sessionInfo] : m_registeredWebSessions) {
|
for (auto &[session, sessionInfo] : m_registeredWebSessions) {
|
||||||
if (!sessionInfo->outstandingBuffersToSend.empty()) {
|
if (!sessionInfo->outstandingBuffersToSend.empty()) {
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/stream_file.hpp>
|
||||||
|
#ifndef BOOST_ASIO_HAS_FILE
|
||||||
#include <boost/asio/posix/stream_descriptor.hpp>
|
#include <boost/asio/posix/stream_descriptor.hpp>
|
||||||
|
#endif
|
||||||
#include <boost/beast/core/file.hpp>
|
#include <boost/beast/core/file.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/process/extend.hpp>
|
#include <boost/process/extend.hpp>
|
||||||
|
@ -112,6 +115,12 @@ template <typename StorageType> inline std::size_t BufferPool<StorageType>::stor
|
||||||
return m_buffers.size();
|
return m_buffers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BOOST_ASIO_HAS_FILE
|
||||||
|
using AsioFileStream = boost::asio::stream_file;
|
||||||
|
#else
|
||||||
|
using AsioFileStream = boost::asio::posix::stream_descriptor;
|
||||||
|
#endif
|
||||||
|
|
||||||
/// \brief The BuildProcessSession class spawns a process associated with a build action.
|
/// \brief The BuildProcessSession class spawns a process associated with a build action.
|
||||||
/// The process output is make available as a logfile of the build action allowing live-steaming.
|
/// The process output is make available as a logfile of the build action allowing live-steaming.
|
||||||
class LIBREPOMGR_EXPORT BuildProcessSession : public std::enable_shared_from_this<BuildProcessSession>, public BaseProcessSession {
|
class LIBREPOMGR_EXPORT BuildProcessSession : public std::enable_shared_from_this<BuildProcessSession>, public BaseProcessSession {
|
||||||
|
@ -154,9 +163,11 @@ private:
|
||||||
|
|
||||||
BuildProcessSession &m_session;
|
BuildProcessSession &m_session;
|
||||||
std::atomic<std::size_t> m_bytesToSendFromFile = 0;
|
std::atomic<std::size_t> m_bytesToSendFromFile = 0;
|
||||||
|
#ifndef BOOST_ASIO_HAS_FILE
|
||||||
boost::beast::file m_file;
|
boost::beast::file m_file;
|
||||||
|
#endif
|
||||||
BufferType m_fileBuffer;
|
BufferType m_fileBuffer;
|
||||||
boost::asio::posix::stream_descriptor m_descriptor;
|
AsioFileStream m_fileStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
void readMoreFromPipe();
|
void readMoreFromPipe();
|
||||||
|
@ -165,6 +176,7 @@ private:
|
||||||
void writeNextBufferToLogFile(const boost::system::error_code &error, std::size_t bytesTransferred);
|
void writeNextBufferToLogFile(const boost::system::error_code &error, std::size_t bytesTransferred);
|
||||||
void writeNextBufferToWebSession(
|
void writeNextBufferToWebSession(
|
||||||
const boost::system::error_code &error, std::size_t bytesTransferred, WebAPI::Session &session, BuffersToWrite &sessionInfo);
|
const boost::system::error_code &error, std::size_t bytesTransferred, WebAPI::Session &session, BuffersToWrite &sessionInfo);
|
||||||
|
void closeLogFile();
|
||||||
void close();
|
void close();
|
||||||
void conclude();
|
void conclude();
|
||||||
|
|
||||||
|
@ -174,8 +186,10 @@ private:
|
||||||
BufferType m_buffer;
|
BufferType m_buffer;
|
||||||
std::string m_displayName;
|
std::string m_displayName;
|
||||||
std::string m_logFilePath;
|
std::string m_logFilePath;
|
||||||
|
#ifndef BOOST_ASIO_HAS_FILE
|
||||||
boost::beast::file m_logFile;
|
boost::beast::file m_logFile;
|
||||||
boost::asio::posix::stream_descriptor m_logFileDescriptor;
|
#endif
|
||||||
|
AsioFileStream m_logFileStream;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
BuffersToWrite m_logFileBuffers;
|
BuffersToWrite m_logFileBuffers;
|
||||||
std::unordered_map<std::shared_ptr<WebAPI::Session>, std::unique_ptr<DataForWebSession>> m_registeredWebSessions;
|
std::unordered_map<std::shared_ptr<WebAPI::Session>, std::unique_ptr<DataForWebSession>> m_registeredWebSessions;
|
||||||
|
@ -186,7 +200,7 @@ private:
|
||||||
|
|
||||||
inline BuildProcessSession::DataForWebSession::DataForWebSession(BuildProcessSession &session)
|
inline BuildProcessSession::DataForWebSession::DataForWebSession(BuildProcessSession &session)
|
||||||
: m_session(session)
|
: m_session(session)
|
||||||
, m_descriptor(session.m_ioContext)
|
, m_fileStream(session.m_ioContext)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +217,7 @@ inline BuildProcessSession::BuildProcessSession(BuildAction *buildAction, boost:
|
||||||
, m_bufferPool(bufferSize)
|
, m_bufferPool(bufferSize)
|
||||||
, m_displayName(std::move(displayName))
|
, m_displayName(std::move(displayName))
|
||||||
, m_logFilePath(std::move(logFilePath))
|
, m_logFilePath(std::move(logFilePath))
|
||||||
, m_logFileDescriptor(ioContext)
|
, m_logFileStream(ioContext)
|
||||||
, m_locks(std::move(locks))
|
, m_locks(std::move(locks))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue