mirror of
https://github.com/matrix-construct/construct
synced 2024-11-25 08:12:37 +01:00
ircd::fs: Initial asynchronous file IO support for Linux platforms.
This commit is contained in:
parent
c4ea3cc3e3
commit
c698101d6d
5 changed files with 781 additions and 234 deletions
24
configure.ac
24
configure.ac
|
@ -511,14 +511,26 @@ AM_COND_IF(INTERIX,
|
||||||
],[])
|
],[])
|
||||||
|
|
||||||
|
|
||||||
|
dnl
|
||||||
|
dnl Linux AIO support
|
||||||
|
dnl
|
||||||
|
|
||||||
AM_COND_IF(LINUX,
|
AM_COND_IF(LINUX,
|
||||||
[
|
[
|
||||||
AC_ARG_ENABLE(aio,
|
AC_ARG_ENABLE(aio, AC_HELP_STRING([--disable-aio], [Disable kernel AIO support]),
|
||||||
AC_HELP_STRING([--enable-aio], [Enable AIO support on Linux platforms]),
|
|
||||||
[
|
[
|
||||||
IRCD_DEFINE(USE_AIO, [1], [Define to 1 if you want AIO support on linux])
|
aio=$enableval
|
||||||
],[])
|
],
|
||||||
],[])
|
[
|
||||||
|
aio=yes
|
||||||
|
])
|
||||||
|
], [])
|
||||||
|
|
||||||
|
if test "$aio" = "yes"; then
|
||||||
|
IRCD_DEFINE(USE_AIO, [1], [Linux AIO is supported and will be used])
|
||||||
|
else
|
||||||
|
IRCD_DEFINE(USE_AIO, [0], [Define to 1 if you want AIO support (linux only)])
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
dnl ***************************************************************************
|
dnl ***************************************************************************
|
||||||
|
@ -1364,6 +1376,7 @@ echo
|
||||||
echo "Configured ........................ $PACKAGE_NAME $PACKAGE_VERSION"
|
echo "Configured ........................ $PACKAGE_NAME $PACKAGE_VERSION"
|
||||||
echo "Version ........................... $RB_VERSION"
|
echo "Version ........................... $RB_VERSION"
|
||||||
echo "Configuration time ................ $RB_DATESTR"
|
echo "Configuration time ................ $RB_DATESTR"
|
||||||
|
echo "Host OS ........................... $host_os"
|
||||||
echo "Compiler .......................... $CXX"
|
echo "Compiler .......................... $CXX"
|
||||||
echo "Compiler flags (CXXFLAGS) ......... $CXXFLAGS"
|
echo "Compiler flags (CXXFLAGS) ......... $CXXFLAGS"
|
||||||
echo "Building boost .................... $with_included_boost"
|
echo "Building boost .................... $with_included_boost"
|
||||||
|
@ -1375,6 +1388,7 @@ echo "SSL support........................ $SSL_TYPE"
|
||||||
echo "Precompiled headers ............... $build_pch"
|
echo "Precompiled headers ............... $build_pch"
|
||||||
echo "Developer debug ................... $debug"
|
echo "Developer debug ................... $debug"
|
||||||
echo "IPv6 support ...................... $ipv6"
|
echo "IPv6 support ...................... $ipv6"
|
||||||
|
echo "Linux AIO support ................. $aio"
|
||||||
echo "Installing into ................... $prefix"
|
echo "Installing into ................... $prefix"
|
||||||
echo
|
echo
|
||||||
echo "* Ready to build $PACKAGE_NAME"
|
echo "* Ready to build $PACKAGE_NAME"
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
/*
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||||
* Copyright (C) 2016 Charybdis Development Team
|
// Copyright (C) 2016-2018 Jason Volk
|
||||||
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
|
//
|
||||||
*
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
// copyright notice and this permission notice is present in all copies.
|
||||||
* copyright notice and this permission notice is present in all copies.
|
//
|
||||||
*
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#define HAVE_IRCD_FS_H
|
#define HAVE_IRCD_FS_H
|
||||||
|
@ -96,19 +94,18 @@ namespace ircd::fs
|
||||||
bool mkdir(const std::string &path);
|
bool mkdir(const std::string &path);
|
||||||
|
|
||||||
// This suite of IO functions may yield your context.
|
// This suite of IO functions may yield your context.
|
||||||
std::string read(const std::string &name);
|
|
||||||
string_view read(const std::string &name, const mutable_raw_buffer &buf);
|
|
||||||
string_view read(const string_view &name, const mutable_raw_buffer &buf);
|
|
||||||
bool write(const std::string &name, const const_raw_buffer &buf);
|
bool write(const std::string &name, const const_raw_buffer &buf);
|
||||||
bool append(const std::string &name, const const_raw_buffer &buf);
|
bool append(const std::string &name, const const_raw_buffer &buf);
|
||||||
bool overwrite(const std::string &name, const const_raw_buffer &buf);
|
bool overwrite(const std::string &name, const const_raw_buffer &buf);
|
||||||
bool overwrite(const string_view &name, const const_raw_buffer &buf);
|
bool overwrite(const string_view &name, const const_raw_buffer &buf);
|
||||||
|
|
||||||
|
extern aio *aioctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "read.h"
|
||||||
|
|
||||||
struct ircd::fs::init
|
struct ircd::fs::init
|
||||||
{
|
{
|
||||||
std::unique_ptr<fs::aio> aio;
|
|
||||||
|
|
||||||
init();
|
init();
|
||||||
~init() noexcept;
|
~init() noexcept;
|
||||||
};
|
};
|
||||||
|
|
63
include/ircd/fs/read.h
Normal file
63
include/ircd/fs/read.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2018 Jason Volk
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice is present in all copies.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#define HAVE_IRCD_FS_READ_H
|
||||||
|
|
||||||
|
namespace ircd::fs
|
||||||
|
{
|
||||||
|
struct read_opts extern const read_opts_default;
|
||||||
|
using read_callback = std::function<void (std::exception_ptr, const string_view &)>;
|
||||||
|
|
||||||
|
// Asynchronous callback-based read into buffer; callback views read portion.
|
||||||
|
void read(const string_view &path, const mutable_raw_buffer &, const read_opts &, read_callback);
|
||||||
|
void read(const string_view &path, const mutable_raw_buffer &, read_callback);
|
||||||
|
|
||||||
|
// Yields ircd::ctx for read into buffer; returns view of read portion.
|
||||||
|
string_view read(const string_view &path, const mutable_raw_buffer &, const read_opts & = read_opts_default);
|
||||||
|
|
||||||
|
// Yields ircd::ctx for read into allocated string; returns that string
|
||||||
|
std::string read(const string_view &path, const read_opts & = read_opts_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for a read operation
|
||||||
|
struct ircd::fs::read_opts
|
||||||
|
{
|
||||||
|
read_opts() = default;
|
||||||
|
read_opts(const off_t &);
|
||||||
|
|
||||||
|
/// Offset in the file to start the read from.
|
||||||
|
off_t offset {0};
|
||||||
|
|
||||||
|
/// Request priority (this option may be improved, avoid for now)
|
||||||
|
int16_t priority {0};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::fs::read_opts::read_opts(const off_t &offset)
|
||||||
|
:offset{offset}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
ircd::fs::read(const string_view &path,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
read_callback callback)
|
||||||
|
{
|
||||||
|
return read(path, buf, read_opts_default, std::move(callback));
|
||||||
|
}
|
516
ircd/aio.h
Normal file
516
ircd/aio.h
Normal file
|
@ -0,0 +1,516 @@
|
||||||
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2018 Jason Volk
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice is present in all copies.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#define HAVE_AIO_H
|
||||||
|
|
||||||
|
#include <RB_INC_SYS_SYSCALL_H
|
||||||
|
#include <RB_INC_SYS_EVENTFD_H
|
||||||
|
#include <linux/aio_abi.h>
|
||||||
|
|
||||||
|
/// AIO context instance. Right now this is a singleton with an extern
|
||||||
|
/// instance at fs::aioctx.
|
||||||
|
struct ircd::fs::aio
|
||||||
|
{
|
||||||
|
struct request;
|
||||||
|
|
||||||
|
/// Maximum number of events we can submit to kernel
|
||||||
|
static constexpr const size_t &MAX_EVENTS
|
||||||
|
{
|
||||||
|
64
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The semaphore value for the eventfd which we keep here.
|
||||||
|
uint64_t semval{0};
|
||||||
|
|
||||||
|
/// An eventfd which will be notified by the kernel; we integrate this with
|
||||||
|
/// the ircd io_service core epoll() event loop. The EFD_SEMAPHORE flag is
|
||||||
|
/// not used to reduce the number of triggers. We can collect multiple AIO
|
||||||
|
/// completions after a single trigger to this fd. With EFD_SEMAPHORE, we
|
||||||
|
/// would collect all the completions on the first trigger and then
|
||||||
|
/// continue to get polled. Because EFD_SEMAPHORE is not set, the semval
|
||||||
|
/// which is kept above will reflect a hint for how many AIO's are done.
|
||||||
|
asio::posix::stream_descriptor resfd
|
||||||
|
{
|
||||||
|
*ircd::ios, int(syscall(::eventfd, semval, EFD_NONBLOCK))
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handler to the io context we submit requests to the kernel with
|
||||||
|
aio_context_t idp
|
||||||
|
{
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Callback stack invoked when the sigfd is notified of completed events.
|
||||||
|
void handle_event(const io_event &) noexcept;
|
||||||
|
void handle_events() noexcept;
|
||||||
|
void handle(const error_code &, const size_t) noexcept;
|
||||||
|
|
||||||
|
void set_handle();
|
||||||
|
|
||||||
|
aio()
|
||||||
|
{
|
||||||
|
syscall<SYS_io_setup>(MAX_EVENTS, &idp);
|
||||||
|
set_handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
~aio() noexcept
|
||||||
|
{
|
||||||
|
resfd.cancel();
|
||||||
|
syscall<SYS_io_destroy>(idp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Generic request control block.
|
||||||
|
struct ircd::fs::aio::request
|
||||||
|
:iocb
|
||||||
|
{
|
||||||
|
struct read;
|
||||||
|
struct write;
|
||||||
|
|
||||||
|
ssize_t retval {0};
|
||||||
|
ssize_t errcode {0};
|
||||||
|
ctx::ctx *waiter {nullptr};
|
||||||
|
bool close_fd {false};
|
||||||
|
bool free_req {false};
|
||||||
|
|
||||||
|
/// called if close_fd is true
|
||||||
|
void close_fildes() noexcept;
|
||||||
|
|
||||||
|
/// Overriden by types of requests
|
||||||
|
virtual void handle() = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Submit a request. ctx overload will properly yield
|
||||||
|
size_t operator()(ctx::ctx &waiter);
|
||||||
|
void operator()();
|
||||||
|
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
request(const int &fd);
|
||||||
|
virtual ~request() noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
ircd::fs::aio::request::request(const int &fd)
|
||||||
|
:iocb{0}
|
||||||
|
{
|
||||||
|
assert(aioctx);
|
||||||
|
aio_flags = IOCB_FLAG_RESFD;
|
||||||
|
aio_resfd = aioctx->resfd.native_handle();
|
||||||
|
aio_fildes = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// vtable base
|
||||||
|
ircd::fs::aio::request::~request()
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel a request. The handler callstack is invoked directly from here
|
||||||
|
/// which means any callback will be invoked or ctx will be notified if
|
||||||
|
/// appropriate.
|
||||||
|
void
|
||||||
|
ircd::fs::aio::request::cancel()
|
||||||
|
{
|
||||||
|
io_event result {0};
|
||||||
|
const auto &cb{static_cast<iocb *>(this)};
|
||||||
|
|
||||||
|
assert(aioctx);
|
||||||
|
syscall_nointr<SYS_io_cancel>(aioctx->idp, cb, &result);
|
||||||
|
aioctx->handle_event(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit a request and properly yield the ircd::ctx. The ctx argument
|
||||||
|
/// must be the currently running ctx (or ctx::current) for now; there is
|
||||||
|
/// no other usage. When this returns the result will be available or
|
||||||
|
/// exception will be thrown.
|
||||||
|
size_t
|
||||||
|
ircd::fs::aio::request::operator()(ctx::ctx &waiter)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Cooperation strategy is to set this->waiter to the ctx and wait for
|
||||||
|
// notification with this->waiter set to null to ignore spurious notes.
|
||||||
|
assert(&waiter == ctx::current);
|
||||||
|
this->waiter = &waiter;
|
||||||
|
operator()(); do
|
||||||
|
{
|
||||||
|
ctx::wait();
|
||||||
|
}
|
||||||
|
while(this->waiter);
|
||||||
|
|
||||||
|
if(retval == -1)
|
||||||
|
throw_system_error(errcode);
|
||||||
|
|
||||||
|
return size_t(retval);
|
||||||
|
}
|
||||||
|
catch(const ctx::interrupted &e)
|
||||||
|
{
|
||||||
|
// When the ctx is interrupted we're obligated to cancel the request.
|
||||||
|
// The handler callstack is invoked directly from here by cancel() for
|
||||||
|
// what it's worth but we rethrow the interrupt anyway.
|
||||||
|
this->waiter = nullptr;
|
||||||
|
cancel();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit a request.
|
||||||
|
void
|
||||||
|
ircd::fs::aio::request::operator()()
|
||||||
|
{
|
||||||
|
struct iocb *const cbs[]
|
||||||
|
{
|
||||||
|
static_cast<iocb *>(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(aioctx);
|
||||||
|
syscall<SYS_io_submit>(aioctx->idp, 1, &cbs);
|
||||||
|
/*
|
||||||
|
log::debug("AIO request(%p) fd:%u op:%d bytes:%lu off:%ld prio:%d ctx:%p submit",
|
||||||
|
this,
|
||||||
|
this->aio_fildes,
|
||||||
|
this->aio_lio_opcode,
|
||||||
|
this->aio_nbytes,
|
||||||
|
this->aio_offset,
|
||||||
|
this->aio_reqprio,
|
||||||
|
this->waiter);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::fs::aio::set_handle()
|
||||||
|
{
|
||||||
|
semval = 0;
|
||||||
|
const asio::mutable_buffers_1 bufs(&semval, sizeof(semval));
|
||||||
|
auto handler{std::bind(&aio::handle, this, ph::_1, ph::_2)};
|
||||||
|
asio::async_read(resfd, bufs, std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle notifications that requests are complete.
|
||||||
|
void
|
||||||
|
ircd::fs::aio::handle(const error_code &ec,
|
||||||
|
const size_t bytes)
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
assert((bytes == 8 && !ec && semval >= 1) || (bytes == 0 && ec));
|
||||||
|
assert(!ec || ec.category() == asio::error::get_system_category());
|
||||||
|
|
||||||
|
switch(ec.value())
|
||||||
|
{
|
||||||
|
case boost::system::errc::success:
|
||||||
|
handle_events();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case boost::system::errc::operation_canceled:
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw boost::system::system_error(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::fs::aio::handle_events()
|
||||||
|
noexcept try
|
||||||
|
{
|
||||||
|
std::array<io_event, MAX_EVENTS> event;
|
||||||
|
|
||||||
|
// The number of completed requests available in events[]. This syscall
|
||||||
|
// is restarted on EINTR. After restart, it may or may not find any ready
|
||||||
|
// events but it never blocks to do so.
|
||||||
|
const auto count
|
||||||
|
{
|
||||||
|
syscall_nointr<SYS_io_getevents>(idp, 0, event.size(), event.data(), nullptr)
|
||||||
|
};
|
||||||
|
|
||||||
|
// The count should be at least 1 event. The only reason to return 0 might
|
||||||
|
// be related to an INTR; this assert will find out and may be commented.
|
||||||
|
assert(count > 0);
|
||||||
|
|
||||||
|
for(ssize_t i(0); i < count; ++i)
|
||||||
|
handle_event(event[i]);
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error("AIO(%p) handle_events: %s",
|
||||||
|
this,
|
||||||
|
e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::fs::aio::handle_event(const io_event &event)
|
||||||
|
noexcept try
|
||||||
|
{
|
||||||
|
// Our extended control block is passed in event.data
|
||||||
|
auto *const request
|
||||||
|
{
|
||||||
|
reinterpret_cast<aio::request *>(event.data)
|
||||||
|
};
|
||||||
|
|
||||||
|
// The relevant iocb is repeated back to us in the result; we assert
|
||||||
|
// some basic sanity here about the layout of the request conglomerate.
|
||||||
|
assert(reinterpret_cast<iocb *>(event.obj) == static_cast<iocb *>(request));
|
||||||
|
|
||||||
|
// error conventions are like so
|
||||||
|
assert(event.res >= -1); // unix syscall return value semantic
|
||||||
|
assert(event.res2 >= 0); // errno code semantic
|
||||||
|
assert(event.res == -1 || event.res2 == 0);
|
||||||
|
|
||||||
|
// Set result indicators
|
||||||
|
request->retval = event.res;
|
||||||
|
request->errcode = event.res2;
|
||||||
|
/*
|
||||||
|
log::debug("AIO request(%p) fd:%d op:%d bytes:%lu off:%ld prio:%d ctx:%p result: bytes:%ld errno:%ld",
|
||||||
|
request,
|
||||||
|
request->aio_fildes,
|
||||||
|
request->aio_lio_opcode,
|
||||||
|
request->aio_nbytes,
|
||||||
|
request->aio_offset,
|
||||||
|
request->aio_reqprio,
|
||||||
|
request->waiter,
|
||||||
|
request->retval,
|
||||||
|
request->errcode);
|
||||||
|
*/
|
||||||
|
const unwind cleanup{[&request]
|
||||||
|
{
|
||||||
|
// The user might want us to cleanup their fd after this event
|
||||||
|
if(request->close_fd)
|
||||||
|
request->close_fildes();
|
||||||
|
|
||||||
|
// The user might want us to free a dynamically allocated request struct
|
||||||
|
// to simplify their callback sequence. The noexcept guarantees h
|
||||||
|
if(request->free_req)
|
||||||
|
delete request;
|
||||||
|
}};
|
||||||
|
|
||||||
|
// virtual dispatch based on the request type. Alternatively, we could
|
||||||
|
// switch() on the iocb lio_opcode and downcast... but that's what vtable
|
||||||
|
// does for us right here.
|
||||||
|
request->handle();
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::critical("Unhandled request(%lu) event(%p) error: %s",
|
||||||
|
event.data,
|
||||||
|
&event,
|
||||||
|
e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If requested, close the file descriptor. Errors here can be logged but
|
||||||
|
/// must be otherwise ignored.
|
||||||
|
void
|
||||||
|
ircd::fs::aio::request::close_fildes()
|
||||||
|
noexcept try
|
||||||
|
{
|
||||||
|
syscall(::close, aio_fildes);
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error("Failed to close request(%p) fd:%d: %s",
|
||||||
|
this,
|
||||||
|
aio_fildes);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// fs/read.h
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace ircd::fs
|
||||||
|
{
|
||||||
|
void read__aio(const string_view &path, const mutable_raw_buffer &, const read_opts &, read_callback);
|
||||||
|
string_view read__aio(const string_view &path, const mutable_raw_buffer &, const read_opts &);
|
||||||
|
std::string read__aio(const string_view &path, const read_opts &);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read request control block
|
||||||
|
struct ircd::fs::aio::request::read
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
read_callback callback;
|
||||||
|
|
||||||
|
virtual void handle() final override;
|
||||||
|
|
||||||
|
read(const int &fd, const mutable_raw_buffer &buf, const read_opts &opts, read_callback callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
ircd::fs::aio::request::read::read(const int &fd,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
const read_opts &opts,
|
||||||
|
read_callback callback)
|
||||||
|
:request{fd}
|
||||||
|
,callback{std::move(callback)}
|
||||||
|
{
|
||||||
|
aio_data = uintptr_t(this);
|
||||||
|
aio_reqprio = opts.priority;
|
||||||
|
aio_lio_opcode = IOCB_CMD_PREAD;
|
||||||
|
|
||||||
|
aio_buf = uintptr_t(buffer::data(buf));
|
||||||
|
aio_nbytes = buffer::size(buf);
|
||||||
|
aio_offset = opts.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::fs::aio::request::read::handle()
|
||||||
|
{
|
||||||
|
if(waiter)
|
||||||
|
{
|
||||||
|
ircd::ctx::notify(*waiter);
|
||||||
|
waiter = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(callback)
|
||||||
|
{
|
||||||
|
const size_t bytes
|
||||||
|
{
|
||||||
|
retval >= 0? size_t(retval) : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const string_view view
|
||||||
|
{
|
||||||
|
reinterpret_cast<const char *>(aio_buf), bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
std::exception_ptr eptr
|
||||||
|
{
|
||||||
|
errcode != 0? make_system_error(errcode) : std::exception_ptr{}
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(std::move(eptr), view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Interface
|
||||||
|
//
|
||||||
|
|
||||||
|
std::string
|
||||||
|
ircd::fs::read__aio(const string_view &path,
|
||||||
|
const read_opts &opts)
|
||||||
|
{
|
||||||
|
// Path to open(2) must be null terminated;
|
||||||
|
static thread_local char pathstr[2048];
|
||||||
|
strlcpy(pathstr, path, sizeof(pathstr));
|
||||||
|
|
||||||
|
const auto fd
|
||||||
|
{
|
||||||
|
syscall(::open, pathstr, O_CLOEXEC, O_RDONLY)
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwind cfd{[&fd]
|
||||||
|
{
|
||||||
|
syscall(::close, fd);
|
||||||
|
}};
|
||||||
|
|
||||||
|
// This fstat may be defeating; to be sure, don't use this overload
|
||||||
|
// and progressively buffer chunks into your application.
|
||||||
|
struct stat stat;
|
||||||
|
syscall(::fstat, fd, &stat);
|
||||||
|
const auto &size
|
||||||
|
{
|
||||||
|
stat.st_size
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string ret(size, char{});
|
||||||
|
const mutable_buffer buf
|
||||||
|
{
|
||||||
|
const_cast<char *>(ret.data()), ret.size()
|
||||||
|
};
|
||||||
|
|
||||||
|
aio::request::read request
|
||||||
|
{
|
||||||
|
int(fd), buf, opts, nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t bytes
|
||||||
|
{
|
||||||
|
request(ctx::cur())
|
||||||
|
};
|
||||||
|
|
||||||
|
ret.resize(bytes);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::string_view
|
||||||
|
ircd::fs::read__aio(const string_view &path,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
const read_opts &opts)
|
||||||
|
{
|
||||||
|
// Path to open(2) must be null terminated;
|
||||||
|
static thread_local char pathstr[2048];
|
||||||
|
strlcpy(pathstr, path, sizeof(pathstr));
|
||||||
|
|
||||||
|
const auto fd
|
||||||
|
{
|
||||||
|
syscall(::open, pathstr, O_CLOEXEC, O_RDONLY)
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwind cfd{[&fd]
|
||||||
|
{
|
||||||
|
syscall(::close, fd);
|
||||||
|
}};
|
||||||
|
|
||||||
|
aio::request::read request
|
||||||
|
{
|
||||||
|
int(fd), buf, opts, nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t bytes
|
||||||
|
{
|
||||||
|
request(ctx::cur())
|
||||||
|
};
|
||||||
|
|
||||||
|
const string_view view
|
||||||
|
{
|
||||||
|
reinterpret_cast<const char *>(data(buf)), bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::fs::read__aio(const string_view &path,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
const read_opts &opts,
|
||||||
|
read_callback callback)
|
||||||
|
{
|
||||||
|
static thread_local char pathstr[2048];
|
||||||
|
strlcpy(pathstr, path, sizeof(pathstr));
|
||||||
|
|
||||||
|
const auto fd
|
||||||
|
{
|
||||||
|
syscall(::open, pathstr, O_CLOEXEC, O_RDONLY)
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwind::exceptional cfd{[&fd]
|
||||||
|
{
|
||||||
|
syscall(::close, fd);
|
||||||
|
}};
|
||||||
|
|
||||||
|
auto request
|
||||||
|
{
|
||||||
|
std::make_unique<aio::request::read>(int(fd), buf, opts, std::move(callback))
|
||||||
|
};
|
||||||
|
|
||||||
|
request->close_fd = true;
|
||||||
|
request->free_req = true;
|
||||||
|
(*request)();
|
||||||
|
request.release();
|
||||||
|
}
|
365
ircd/fs.cc
365
ircd/fs.cc
|
@ -1,50 +1,43 @@
|
||||||
/*
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||||
* charybdis: A slightly useful ircd.
|
// Copyright (C) 2016-2018 Jason Volk
|
||||||
* ircd.c: Starts up and runs the ircd.
|
//
|
||||||
*
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
* Copyright (C) 1996-2002 Hybrid Development Team
|
// copyright notice and this permission notice is present in all copies.
|
||||||
* Copyright (C) 2002-2008 ircd-ratbox development team
|
//
|
||||||
* Copyright (C) 2005-2013 charybdis development team
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
*
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
* This program is free software; you can redistribute it and/or modify
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* it under the terms of the GNU General Public License as published by
|
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
* (at your option) any later version.
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
*
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
* This program is distributed in the hope that it will be useful,
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
* GNU General Public License for more details.
|
// POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
||||||
* USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#undef linux
|
|
||||||
#include <RB_INC_UNISTD_H
|
|
||||||
#include <RB_INC_SYS_SYSCALL_H
|
|
||||||
#include <RB_INC_SYS_EVENTFD_H
|
|
||||||
#include <RB_INC_LINUX_AIO_ABI_H
|
|
||||||
#include <RB_INC_BOOST_FILESYSTEM_HPP
|
#include <RB_INC_BOOST_FILESYSTEM_HPP
|
||||||
#include <ircd/asio.h>
|
#include <ircd/asio.h>
|
||||||
|
|
||||||
|
#ifdef IRCD_USE_AIO
|
||||||
|
#include "aio.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ircd::fs
|
namespace ircd::fs
|
||||||
{
|
{
|
||||||
using namespace boost::filesystem;
|
using namespace boost::filesystem;
|
||||||
|
|
||||||
enum
|
enum { NAME, PATH };
|
||||||
{
|
|
||||||
NAME = 0,
|
|
||||||
PATH = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
using ent = std::pair<std::string, std::string>;
|
using ent = std::pair<std::string, std::string>;
|
||||||
extern const std::array<ent, num_of<index>()> paths;
|
extern const std::array<ent, num_of<index>()> paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Non-null when aio is available for use
|
||||||
|
decltype(ircd::fs::aioctx)
|
||||||
|
ircd::fs::aioctx
|
||||||
|
{};
|
||||||
|
|
||||||
decltype(ircd::fs::paths)
|
decltype(ircd::fs::paths)
|
||||||
ircd::fs::paths
|
ircd::fs::paths
|
||||||
{{
|
{{
|
||||||
|
@ -60,15 +53,147 @@ ircd::fs::paths
|
||||||
}};
|
}};
|
||||||
|
|
||||||
ircd::fs::init::init()
|
ircd::fs::init::init()
|
||||||
:aio{std::make_unique<fs::aio>()}
|
|
||||||
{
|
{
|
||||||
|
#ifdef IRCD_USE_AIO
|
||||||
|
assert(!aioctx);
|
||||||
|
aioctx = new aio{};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ircd::fs::init::~init()
|
ircd::fs::init::~init()
|
||||||
noexcept
|
noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef IRCD_USE_AIO
|
||||||
|
delete aioctx;
|
||||||
|
aioctx = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assert(!aioctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// fs/read.h
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace ircd::fs
|
||||||
|
{
|
||||||
|
void read__std(const string_view &path, const mutable_raw_buffer &, const read_opts &, read_callback);
|
||||||
|
string_view read__std(const string_view &path, const mutable_raw_buffer &, const read_opts &);
|
||||||
|
std::string read__std(const string_view &path, const read_opts &);
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::fs::read_opts
|
||||||
|
const ircd::fs::read_opts_default
|
||||||
|
{};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ircd::fs interface linkage
|
||||||
|
//
|
||||||
|
|
||||||
|
std::string
|
||||||
|
ircd::fs::read(const string_view &path,
|
||||||
|
const read_opts &opts)
|
||||||
|
{
|
||||||
|
#ifdef IRCD_USE_AIO
|
||||||
|
if(likely(aioctx))
|
||||||
|
return read__aio(path, opts);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return read__std(path, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::string_view
|
||||||
|
ircd::fs::read(const string_view &path,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
const read_opts &opts)
|
||||||
|
{
|
||||||
|
#ifdef IRCD_USE_AIO
|
||||||
|
if(likely(aioctx))
|
||||||
|
return read__aio(path, buf, opts);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return read__std(path, buf, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::fs::read(const string_view &path,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
const read_opts &opts,
|
||||||
|
read_callback callback)
|
||||||
|
{
|
||||||
|
#ifdef IRCD_USE_AIO
|
||||||
|
if(likely(aioctx))
|
||||||
|
return read__aio(path, buf, opts, std::move(callback));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return read__std(path, buf, opts, std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// std read
|
||||||
|
//
|
||||||
|
|
||||||
|
std::string
|
||||||
|
ircd::fs::read__std(const string_view &path,
|
||||||
|
const read_opts &opts)
|
||||||
|
{
|
||||||
|
std::ifstream file{std::string{path}};
|
||||||
|
std::noskipws(file);
|
||||||
|
std::istream_iterator<char> b{file};
|
||||||
|
std::istream_iterator<char> e{};
|
||||||
|
return std::string{b, e};
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::string_view
|
||||||
|
ircd::fs::read__std(const string_view &path,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
const read_opts &opts)
|
||||||
|
{
|
||||||
|
string_view ret;
|
||||||
|
std::exception_ptr reptr;
|
||||||
|
read(path, buf, opts, [&reptr, &ret]
|
||||||
|
(std::exception_ptr eptr, const string_view &view)
|
||||||
|
{
|
||||||
|
reptr = std::move(eptr);
|
||||||
|
ret = view;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(reptr)
|
||||||
|
std::rethrow_exception(reptr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::fs::read__std(const string_view &path,
|
||||||
|
const mutable_raw_buffer &buf,
|
||||||
|
const read_opts &opts,
|
||||||
|
read_callback callback)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::ifstream file{std::string{path}};
|
||||||
|
file.exceptions(file.failbit | file.badbit);
|
||||||
|
file.seekg(opts.offset, file.beg);
|
||||||
|
file.read(reinterpret_cast<char *>(data(buf)), size(buf));
|
||||||
|
const string_view view
|
||||||
|
{
|
||||||
|
reinterpret_cast<const char *>(data(buf)), size_t(file.gcount())
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(std::exception_ptr{}, view);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
callback(std::make_exception_ptr(std::current_exception()), string_view{});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// fs/write.h
|
||||||
|
//
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ircd::fs::write(const std::string &path,
|
ircd::fs::write(const std::string &path,
|
||||||
const const_raw_buffer &buf)
|
const const_raw_buffer &buf)
|
||||||
|
@ -104,35 +229,6 @@ ircd::fs::append(const std::string &path,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
|
||||||
ircd::fs::read(const std::string &path)
|
|
||||||
{
|
|
||||||
std::ifstream file{path};
|
|
||||||
std::noskipws(file);
|
|
||||||
std::istream_iterator<char> b{file};
|
|
||||||
std::istream_iterator<char> e{};
|
|
||||||
return std::string{b, e};
|
|
||||||
}
|
|
||||||
|
|
||||||
ircd::string_view
|
|
||||||
ircd::fs::read(const string_view &path,
|
|
||||||
const mutable_raw_buffer &buf)
|
|
||||||
{
|
|
||||||
return ircd::fs::read(std::string{path}, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
ircd::string_view
|
|
||||||
ircd::fs::read(const std::string &path,
|
|
||||||
const mutable_raw_buffer &buf)
|
|
||||||
{
|
|
||||||
std::ifstream file{path};
|
|
||||||
if(!file.good())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
file.read(reinterpret_cast<char *>(data(buf)), size(buf));
|
|
||||||
return { reinterpret_cast<const char *>(data(buf)), size_t(file.gcount()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::fs::chdir(const std::string &path)
|
ircd::fs::chdir(const std::string &path)
|
||||||
try
|
try
|
||||||
|
@ -284,142 +380,3 @@ catch(const std::out_of_range &e)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// linux aio
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef IRCD_USE_AIO
|
|
||||||
|
|
||||||
struct ircd::fs::aio
|
|
||||||
{
|
|
||||||
aio() {}
|
|
||||||
~aio() noexcept {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
struct ircd::fs::aio
|
|
||||||
{
|
|
||||||
static constexpr const size_t &MAX_EVENTS
|
|
||||||
{
|
|
||||||
64
|
|
||||||
};
|
|
||||||
|
|
||||||
asio::posix::stream_descriptor resfd
|
|
||||||
{
|
|
||||||
*ircd::ios, int(syscall(::eventfd, 0, EFD_NONBLOCK))
|
|
||||||
};
|
|
||||||
|
|
||||||
aio_context_t idp
|
|
||||||
{
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
void handle_ready(const error_code &ec)
|
|
||||||
noexcept
|
|
||||||
{
|
|
||||||
std::array<io_event, MAX_EVENTS> events;
|
|
||||||
|
|
||||||
const auto nr
|
|
||||||
{
|
|
||||||
syscall<SYS_io_getevents>(idp, 0, events.size(), events.data(), nullptr)
|
|
||||||
};
|
|
||||||
|
|
||||||
for(ssize_t i(0); i < nr; ++i)
|
|
||||||
{
|
|
||||||
auto &event{events[i]};
|
|
||||||
auto &ctx{*reinterpret_cast<ctx::ctx *>(event.data)};
|
|
||||||
auto &cb{*reinterpret_cast<iocb *>(event.obj)};
|
|
||||||
cb.aio_data = event.res;
|
|
||||||
ircd::ctx::notify(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string read(const std::string &path);
|
|
||||||
|
|
||||||
aio();
|
|
||||||
~aio() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
ircd::fs::aio::aio()
|
|
||||||
{
|
|
||||||
syscall<SYS_io_setup>(MAX_EVENTS, &idp);
|
|
||||||
resfd.async_wait(resfd.wait_read, std::bind(&aio::handle_ready, this, ph::_1));
|
|
||||||
|
|
||||||
auto test{[this]
|
|
||||||
{
|
|
||||||
const auto got{this->read("/etc/passwd")};
|
|
||||||
std::cout << "got: " << got << std::endl;
|
|
||||||
}};
|
|
||||||
|
|
||||||
ircd::context
|
|
||||||
{
|
|
||||||
"aiotest", context::POST | context::DETACH, test
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ircd::fs::aio::~aio()
|
|
||||||
noexcept
|
|
||||||
{
|
|
||||||
syscall<SYS_io_destroy>(idp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the file at path into a string; yields your ircd::ctx.
|
|
||||||
///
|
|
||||||
std::string
|
|
||||||
ircd::fs::aio::read(const std::string &path)
|
|
||||||
{
|
|
||||||
const auto fd
|
|
||||||
{
|
|
||||||
syscall(::open, path.c_str(), O_CLOEXEC, O_RDONLY)
|
|
||||||
};
|
|
||||||
|
|
||||||
const unwind close{[&fd]
|
|
||||||
{
|
|
||||||
syscall(::close, fd);
|
|
||||||
}};
|
|
||||||
|
|
||||||
struct stat stat;
|
|
||||||
syscall(::fstat, fd, &stat);
|
|
||||||
const auto &size{stat.st_size};
|
|
||||||
|
|
||||||
std::string ret(size, char{});
|
|
||||||
const mutable_buffer buf
|
|
||||||
{
|
|
||||||
const_cast<char *>(ret.data()), ret.size()
|
|
||||||
};
|
|
||||||
|
|
||||||
struct iocb cb{0};
|
|
||||||
cb.aio_flags = IOCB_FLAG_RESFD;
|
|
||||||
cb.aio_reqprio = 0;
|
|
||||||
cb.aio_resfd = resfd.native_handle();
|
|
||||||
cb.aio_data = uintptr_t(ctx::current);
|
|
||||||
|
|
||||||
cb.aio_lio_opcode = IOCB_CMD_PREAD;
|
|
||||||
cb.aio_fildes = fd;
|
|
||||||
cb.aio_buf = uintptr_t(buffer::data(buf));
|
|
||||||
cb.aio_nbytes = buffer::size(buf);
|
|
||||||
cb.aio_offset = 0;
|
|
||||||
|
|
||||||
struct iocb *cbs[]
|
|
||||||
{
|
|
||||||
&cb
|
|
||||||
};
|
|
||||||
|
|
||||||
syscall<SYS_io_submit>(idp, 1, &cbs); // there
|
|
||||||
ctx::wait(); // back
|
|
||||||
|
|
||||||
const ssize_t &bytes
|
|
||||||
{
|
|
||||||
reinterpret_cast<const ssize_t &>(cb.aio_data)
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(bytes >= 0);
|
|
||||||
ret.resize(size_t(bytes));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Loading…
Reference in a new issue