Skip to content

Commit

Permalink
Add llfs::IoRingStreamBuffer. (#139)
Browse files Browse the repository at this point in the history
* IoRingBufferView tests.

* Clean up IoRingStreamBuffer impl; need to write test plan.

* Clean up IoRingBufferPool; add IoRingBufferView::{split,data,size}.

* Wrote IoRingStreamBuffer tests; doing line/cond coverage testing now.

* Finished coverage testing of IoRingStreamBuffer (for now).  Ready for MR!

* Remove a spurious (formatting) diff.
  • Loading branch information
tonyastolfi authored Jan 4, 2024
1 parent 838b62f commit 16fa593
Show file tree
Hide file tree
Showing 14 changed files with 2,039 additions and 35 deletions.
43 changes: 27 additions & 16 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,44 @@
#+++++++++++-+-+--+----- --- -- - - - -

from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps
from conan.tools.files import copy

import os, sys, platform


#==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
# Import batt helper utilities module.
#
sys.path.append(os.path.join(os.path.dirname(__file__), 'script'))
import batt
import script.batt as batt
from script.batt import VISIBLE, OVERRIDE
#
#+++++++++++-+-+--+----- --- -- - - - -


class LlfsConan(ConanFile):
name = "llfs"

#----- --- -- - - - -
# version is set automatically from Git tags - DO NOT SET IT HERE
#----- --- -- - - - -

license = "Apache Public License 2.0"

author = "The MathWorks, Inc."

url = "https://github.com/mathworks/llfs"

description = "Low-Level File System Utilities (C++)"

settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False]}
default_options = {"shared": False}

options = {
"shared": [True, False],
}

default_options = {
"shared": False,
}

build_policy = "missing"

exports = [
Expand All @@ -56,9 +69,7 @@ def configure(self):


def requirements(self):
from batt import VISIBLE, OVERRIDE

self.requires("batteries/0.49.6", **VISIBLE)
self.requires("batteries/0.50.2", **VISIBLE)
self.requires("boost/1.83.0", **VISIBLE)
self.requires("cli11/2.3.2", **VISIBLE)
self.requires("glog/0.6.0", **VISIBLE)
Expand All @@ -75,12 +86,12 @@ def requirements(self):

#+++++++++++-+-+--+----- --- -- - - - -

from batt import set_version_from_git_tags as set_version
from batt import cmake_in_src_layout as layout
from batt import default_cmake_generate as generate
from batt import default_cmake_build as build
from batt import default_cmake_lib_package as package
from batt import default_lib_package_info as package_info
from batt import default_lib_package_id as package_id
from script.batt import set_version_from_git_tags as set_version
from script.batt import cmake_in_src_layout as layout
from script.batt import default_cmake_generate as generate
from script.batt import default_cmake_build as build
from script.batt import default_cmake_lib_package as package
from script.batt import default_lib_package_info as package_info
from script.batt import default_lib_package_id as package_id

#+++++++++++-+-+--+----- --- -- - - - -
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ endmacro()
LLFS_DefineLibrary(llfs ./llfs
batteries::batteries
liburing::liburing
OpenSSL::Crypto
)

#=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+---------------
Expand Down Expand Up @@ -219,6 +220,7 @@ target_link_libraries(llfs_fuse
Boost::context
Boost::stacktrace_backtrace
libbacktrace::libbacktrace
OpenSSL::Crypto
CLI11::CLI11
dl
stdc++fs)
44 changes: 42 additions & 2 deletions src/llfs/buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <batteries/buffer.hpp>

#include <memory>
#include <vector>

namespace llfs {
Expand All @@ -25,14 +26,18 @@ using batt::mutable_buffer_from_struct;
using batt::MutableBuffer;
using batt::resize_buffer;

// Returns the distance, in bytes, from `begin` to `end`. If `end` is less than `begin`, the result
// is negative.
//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
/** \brief Returns the distance, in bytes, from `begin` to `end`. If `end` is less than `begin`,
* the result is negative.
*/
inline isize byte_distance(const void* begin, const void* end)
{
return static_cast<const u8*>(end) - static_cast<const u8*>(begin);
}

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
/** \brief Returns an empty sequence of ConstBuffer objects.
*/
inline const std::vector<ConstBuffer>& no_buffers()
Expand All @@ -42,6 +47,41 @@ inline const std::vector<ConstBuffer>& no_buffers()
return no_buffers_;
}

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
/** \brief Returns the least-upper bound (exclusive) address of the passed buffer.
*/
inline const void* get_buffer_end(const batt::ConstBuffer& buffer) noexcept
{
return static_cast<const char*>(buffer.data()) + buffer.size();
}

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
/** \brief Returns the pointer that is offset from `ptr` by `delta` bytes.
*/
inline const void* advance_pointer(const void* ptr, isize delta)
{
return static_cast<const char*>(ptr) + delta;
}

} // namespace llfs

//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++

namespace std {

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
/** \brief Resizes the given buffer storage object and returns a MutableBuffer of the same size.
*/
inline llfs::MutableBuffer resize_buffer_storage(std::unique_ptr<llfs::u8[]>& p_storage,
llfs::usize size)
{
p_storage.reset(new llfs::u8[size]);
return llfs::MutableBuffer{p_storage.get(), size};
}

} //namespace std

#endif // LLFS_BUFFER_HPP
30 changes: 30 additions & 0 deletions src/llfs/buffer.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++
//
// Part of the LLFS Project, under Apache License v2.0.
// See https://www.apache.org/licenses/LICENSE-2.0 for license information.
// SPDX short identifier: Apache-2.0
//
//+++++++++++-+-+--+----- --- -- - - - -

#include <llfs/buffer.hpp>
//
#include <llfs/buffer.hpp>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

namespace {

using namespace llfs::int_types;

TEST(BufferTest, ResizeBufferStorage)
{
std::unique_ptr<u8[]> storage;
llfs::MutableBuffer buffer = resize_buffer_storage(storage, 1977);

EXPECT_NE(buffer.data(), nullptr);
EXPECT_EQ((void*)buffer.data(), (void*)storage.get());
EXPECT_EQ(buffer.size(), 1977u);
}

} // namespace
10 changes: 10 additions & 0 deletions src/llfs/ioring_buffer_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,16 @@ IoRingBufferPool::Buffer::Buffer() noexcept : allocated_{nullptr}
/*explicit*/ IoRingBufferPool::Buffer::Buffer(batt::SharedPtr<const Allocated>&& allocated) noexcept
: allocated_{std::move(allocated)}
{
LLFS_VLOG_IF(1, this->allocated_)
<< BATT_INSPECT(this->allocated_->use_count()) << "->" << (this->allocated_->use_count() + 1);
}

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
IoRingBufferPool::Buffer::~Buffer() noexcept
{
LLFS_VLOG_IF(1, this->allocated_)
<< BATT_INSPECT(this->allocated_->use_count()) << "->" << (this->allocated_->use_count() - 1);
}

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
Expand Down
43 changes: 26 additions & 17 deletions src/llfs/ioring_buffer_pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <llfs/api_types.hpp>
#include <llfs/constants.hpp>
#include <llfs/ioring.hpp>
#include <llfs/status.hpp>

#include <batteries/async/mutex.hpp>
#include <batteries/async/watch.hpp>
Expand Down Expand Up @@ -160,6 +161,13 @@ class IoRingBufferPool
*/
Buffer(const Buffer&) = default;

/** \brief Destroys this Buffer object, decrementing the ref count on the underlying memory by
* one.
*/
~Buffer() noexcept;

//----- --- -- - - - -

/** This class is copyable; all copies share the same underlying buffer memory. When the last
* copy is destroyed, the buffer is returned to the pool.
*/
Expand Down Expand Up @@ -229,12 +237,12 @@ class IoRingBufferPool

using BufferVec = batt::SmallVec<Buffer, 2>;

using AbstractHandler = batt::BasicAbstractHandler<HandlerBase, batt::StatusOr<BufferVec>&&>;
using AbstractHandler = batt::BasicAbstractHandler<HandlerBase, StatusOr<BufferVec>&&>;

template <typename Fn>
using HandlerImpl = batt::BasicHandlerImpl<Fn, HandlerBase, batt::StatusOr<BufferVec>&&>;
using HandlerImpl = batt::BasicHandlerImpl<Fn, HandlerBase, StatusOr<BufferVec>&&>;

using HandlerList = batt::BasicHandlerList<HandlerBase, batt::StatusOr<BufferVec>&&>;
using HandlerList = batt::BasicHandlerList<HandlerBase, StatusOr<BufferVec>&&>;

using BufferFreePoolList = boost::intrusive::slist<Deallocated, //
boost::intrusive::cache_last<true>, //
Expand All @@ -246,9 +254,10 @@ class IoRingBufferPool
*
* Automatically registers the buffers in the pool with the passed IoRing context.
*/
static batt::StatusOr<std::unique_ptr<IoRingBufferPool>> make_new(
const IoRing& io_ring, BufferCount count,
BufferSize size = BufferSize{Self::kMemoryUnitSize}) noexcept;
static StatusOr<std::unique_ptr<IoRingBufferPool>> make_new(const IoRing& io_ring,
BufferCount count,
BufferSize size = BufferSize{
Self::kMemoryUnitSize}) noexcept;

//+++++++++++-+-+--+----- --- -- - - - -

Expand Down Expand Up @@ -278,30 +287,30 @@ class IoRingBufferPool
/** \brief Asynchronously allocate a new buffer. Waits until a buffer becomes available and then
* invokes the passed handler.
*
* The signature of the handler is: `void (batt::StatusOr<llfs::IoRingBufferPool::Buffer>)`.
* The signature of the handler is: `void (StatusOr<llfs::IoRingBufferPool::Buffer>)`.
*/
template <typename Handler = void(batt::StatusOr<Buffer>&&)>
template <typename Handler = void(StatusOr<Buffer>&&)>
void async_allocate(Handler&& handler)
{
this->async_allocate(
BufferCount{1},
batt::bind_handler(
BATT_FORWARD(handler), [](Handler&& handler, batt::StatusOr<BufferVec>&& buffers) {
batt::bind_handler( //
BATT_FORWARD(handler), [](Handler&& handler, StatusOr<BufferVec>&& buffers) {
if (!buffers.ok()) {
BATT_FORWARD(handler)(batt::StatusOr<Buffer>{buffers.status()});
BATT_FORWARD(handler)(StatusOr<Buffer>{buffers.status()});
return;
}
BATT_CHECK_EQ(buffers->size(), 1u);
BATT_FORWARD(handler)(batt::StatusOr<Buffer>{std::move(buffers->front())});
BATT_FORWARD(handler)(StatusOr<Buffer>{std::move(buffers->front())});
}));
}

/** \brief Asynchronously allocate the specified number of buffers. Waits until enough buffers
* become available and then invokes the passed handler.
*
* The signature of the handler is: `void (batt::StatusOr<llfs::IoRingBufferPool::Buffer>)`.
* The signature of the handler is: `void (StatusOr<llfs::IoRingBufferPool::Buffer>)`.
*/
template <typename Fn = void(batt::StatusOr<BufferVec>&&)>
template <typename Fn = void(StatusOr<BufferVec>&&)>
void async_allocate(BufferCount count, Fn&& fn)
{
AbstractHandler* handler = HandlerImpl<Fn>::make_new(BATT_FORWARD(fn));
Expand All @@ -316,17 +325,17 @@ class IoRingBufferPool
/** \brief Blocks the current Task until a buffer becomes available, then allocates and returns
* it.
*/
auto await_allocate() -> batt::StatusOr<Buffer>;
auto await_allocate() -> StatusOr<Buffer>;

/** \brief Blocks the current Task until a buffer becomes available, then allocates and returns
* it.
*/
auto await_allocate(BufferCount count) -> batt::StatusOr<BufferVec>;
auto await_allocate(BufferCount count) -> StatusOr<BufferVec>;

/** \brief Attempts to allocate a buffer without blocking; if successful, returns the buffer; else
* returns batt::StatusCode::kResourceExhausted.
*/
auto try_allocate() -> batt::StatusOr<Buffer>;
auto try_allocate() -> StatusOr<Buffer>;

/** \brief Returns the number of buffers in the pool which are currently in use (allocated).
*/
Expand Down
61 changes: 61 additions & 0 deletions src/llfs/ioring_buffer_view.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//#=##=##=#==#=#==#===#+==#+==========+==+=+=+=+=+=++=+++=+++++=-++++=-+++++++++++
//
// Part of the LLFS Project, under Apache License v2.0.
// See https://www.apache.org/licenses/LICENSE-2.0 for license information.
// SPDX short identifier: Apache-2.0
//
//+++++++++++-+-+--+----- --- -- - - - -

#include <llfs/ioring_buffer_view.hpp>
//

namespace llfs {

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
template <typename SliceT>
auto BasicIoRingBufferView<SliceT>::split(usize byte_offset) noexcept -> Self
{
byte_offset = std::min(byte_offset, this->slice.size());

Self prefix{
this->buffer,
SliceT{this->slice.data(), byte_offset},
};

this->slice += byte_offset;

return prefix;
}

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
template <typename SliceT>
bool BasicIoRingBufferView<SliceT>::can_merge_with(const Self& other) const noexcept
{
return this->buffer == other.buffer //
&& get_buffer_end(this->slice) == other.slice.data();
}

//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
//
template <typename SliceT>
bool BasicIoRingBufferView<SliceT>::merge_with(const Self& other) noexcept
{
if (!this->can_merge_with(other)) {
return false;
}
this->slice = SliceT{
this->slice.data(),
this->slice.size() + other.slice.size(),
};
return true;
}

//=#=#==#==#===============+=+=+=+=++=++++++++++++++-++-+--+-+----+---------------
// Explicitly specialize for MutableBuffer and ConstBuffer only.

template class BasicIoRingBufferView<ConstBuffer>;
template class BasicIoRingBufferView<MutableBuffer>;

} //namespace llfs
Loading

0 comments on commit 16fa593

Please sign in to comment.