mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-24 18:40:38 +00:00
Merge seastar upstream
This commit is contained in:
@@ -27,29 +27,62 @@
|
||||
#include <assert.h>
|
||||
#include <type_traits>
|
||||
|
||||
/// \addtogroup memory-module
|
||||
/// @{
|
||||
|
||||
/// Provides a mechanism for managing the lifetime of a buffer.
|
||||
///
|
||||
/// A \c deleter is an object that is used to inform the consumer
|
||||
/// of some buffer (not referenced by the deleter itself) how to
|
||||
/// delete the buffer. This can be by calling an arbitrary function
|
||||
/// or destroying an object carried by the deleter. Examples of
|
||||
/// a deleter's encapsulated actions are:
|
||||
///
|
||||
/// - calling \c std::free(p) on some captured pointer, p
|
||||
/// - calling \c delete \c p on some captured pointer, p
|
||||
/// - decrementing a reference count somewhere
|
||||
///
|
||||
/// A deleter performs its action from its destructor.
|
||||
class deleter final {
|
||||
public:
|
||||
/// \cond internal
|
||||
struct impl;
|
||||
struct raw_object_tag {};
|
||||
/// \endcond
|
||||
private:
|
||||
// if bit 0 set, point to object to be freed directly.
|
||||
impl* _impl = nullptr;
|
||||
public:
|
||||
/// Constructs an empty deleter that does nothing in its destructor.
|
||||
deleter() = default;
|
||||
deleter(const deleter&) = delete;
|
||||
/// Moves a deleter.
|
||||
deleter(deleter&& x) : _impl(x._impl) { x._impl = nullptr; }
|
||||
/// \cond internal
|
||||
explicit deleter(impl* i) : _impl(i) {}
|
||||
deleter(raw_object_tag tag, void* object)
|
||||
: _impl(from_raw_object(object)) {}
|
||||
/// \endcond
|
||||
/// Destroys the deleter and carries out the encapsulated action.
|
||||
~deleter();
|
||||
deleter& operator=(deleter&& x);
|
||||
deleter& operator=(deleter&) = delete;
|
||||
/// Performs a sharing operation. The encapsulated action will only
|
||||
/// be carried out after both the original deleter and the returned
|
||||
/// deleter are both destroyed.
|
||||
///
|
||||
/// \return a deleter with the same encapsulated action as this one.
|
||||
deleter share();
|
||||
/// Checks whether the deleter has an associated action.
|
||||
explicit operator bool() const { return bool(_impl); }
|
||||
/// \cond internal
|
||||
void reset(impl* i) {
|
||||
this->~deleter();
|
||||
new (this) deleter(i);
|
||||
}
|
||||
/// \endcond
|
||||
/// Appends another deleter to this deleter. When this deleter is
|
||||
/// destroyed, both encapsulated actions will be carried out.
|
||||
void append(deleter d);
|
||||
private:
|
||||
static bool is_raw_object(impl* i) {
|
||||
@@ -72,12 +105,14 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/// \cond internal
|
||||
struct deleter::impl {
|
||||
unsigned refs = 1;
|
||||
deleter next;
|
||||
impl(deleter next) : next(std::move(next)) {}
|
||||
virtual ~impl() {}
|
||||
};
|
||||
/// \endcond
|
||||
|
||||
inline
|
||||
deleter::~deleter() {
|
||||
@@ -99,6 +134,7 @@ deleter& deleter::operator=(deleter&& x) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \cond internal
|
||||
template <typename Deleter>
|
||||
struct lambda_deleter_impl final : deleter::impl {
|
||||
Deleter del;
|
||||
@@ -119,24 +155,39 @@ inline
|
||||
object_deleter_impl<Object>* make_object_deleter_impl(deleter next, Object obj) {
|
||||
return new object_deleter_impl<Object>(std::move(next), std::move(obj));
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
template <typename Deleter>
|
||||
/// Makes a \ref deleter that encapsulates the action of
|
||||
/// destroying an object, as well as running another deleter. The input
|
||||
/// object is moved to the deleter, and destroyed when the deleter is destroyed.
|
||||
///
|
||||
/// \param d deleter that will become part of the new deleter's encapsulated action
|
||||
/// \param o object whose destructor becomes part of the new deleter's encapsulated action
|
||||
/// \related deleter
|
||||
template <typename Object>
|
||||
deleter
|
||||
make_deleter(deleter next, Deleter d) {
|
||||
return deleter(new lambda_deleter_impl<Deleter>(std::move(next), std::move(d)));
|
||||
make_deleter(deleter next, Object o) {
|
||||
return deleter(new lambda_deleter_impl<Object>(std::move(next), std::move(o)));
|
||||
}
|
||||
|
||||
template <typename Deleter>
|
||||
/// Makes a \ref deleter that encapsulates the action of destroying an object. The input
|
||||
/// object is moved to the deleter, and destroyed when the deleter is destroyed.
|
||||
///
|
||||
/// \param o object whose destructor becomes the new deleter's encapsulated action
|
||||
/// \related deleter
|
||||
template <typename Object>
|
||||
deleter
|
||||
make_deleter(Deleter d) {
|
||||
return make_deleter(deleter(), std::move(d));
|
||||
make_deleter(Object o) {
|
||||
return make_deleter(deleter(), std::move(o));
|
||||
}
|
||||
|
||||
/// \cond internal
|
||||
struct free_deleter_impl final : deleter::impl {
|
||||
void* obj;
|
||||
free_deleter_impl(void* obj) : impl(deleter()), obj(obj) {}
|
||||
virtual ~free_deleter_impl() override { std::free(obj); }
|
||||
};
|
||||
/// \endcond
|
||||
|
||||
inline
|
||||
deleter
|
||||
@@ -176,6 +227,10 @@ void deleter::append(deleter d) {
|
||||
d._impl = nullptr;
|
||||
}
|
||||
|
||||
/// Makes a deleter that calls \c std::free() when it is destroyed.
|
||||
///
|
||||
/// \param obj object to free.
|
||||
/// \related deleter
|
||||
inline
|
||||
deleter
|
||||
make_free_deleter(void* obj) {
|
||||
@@ -185,12 +240,20 @@ make_free_deleter(void* obj) {
|
||||
return deleter(deleter::raw_object_tag(), obj);
|
||||
}
|
||||
|
||||
/// Makes a deleter that calls \c std::free() when it is destroyed, as well
|
||||
/// as invoking the encapsulated action of another deleter.
|
||||
///
|
||||
/// \param d deleter to invoke.
|
||||
/// \param obj object to free.
|
||||
/// \related deleter
|
||||
inline
|
||||
deleter
|
||||
make_free_deleter(deleter next, void* obj) {
|
||||
return make_deleter(std::move(next), [obj] () mutable { std::free(obj); });
|
||||
}
|
||||
|
||||
/// \see make_deleter(Object)
|
||||
/// \related deleter
|
||||
template <typename T>
|
||||
inline
|
||||
deleter
|
||||
@@ -198,6 +261,8 @@ make_object_deleter(T&& obj) {
|
||||
return deleter{make_object_deleter_impl(deleter(), std::move(obj))};
|
||||
}
|
||||
|
||||
/// \see make_deleter(deleter, Object)
|
||||
/// \related deleter
|
||||
template <typename T>
|
||||
inline
|
||||
deleter
|
||||
@@ -205,4 +270,6 @@ make_object_deleter(deleter d, T&& obj) {
|
||||
return deleter{make_object_deleter_impl(std::move(d), std::move(obj))};
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
#endif /* DELETER_HH_ */
|
||||
|
||||
@@ -225,6 +225,10 @@ struct future_state {
|
||||
}
|
||||
return std::move(_u.value);
|
||||
}
|
||||
using get0_return_type = std::tuple_element_t<0, std::tuple<T...>>;
|
||||
static get0_return_type get0(std::tuple<T...>&& x) {
|
||||
return std::get<0>(x);
|
||||
}
|
||||
void forward_to(promise<T...>& pr) noexcept {
|
||||
assert(_state != state::future);
|
||||
if (_state == state::exception) {
|
||||
@@ -308,6 +312,10 @@ struct future_state<> {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
using get0_return_type = void;
|
||||
static get0_return_type get0(std::tuple<>&&) {
|
||||
return;
|
||||
}
|
||||
std::exception_ptr get_exception() noexcept {
|
||||
assert(_u.st >= state::exception_min);
|
||||
// Move ex out so future::~future() knows we've handled it
|
||||
@@ -677,6 +685,18 @@ public:
|
||||
return state()->get();
|
||||
}
|
||||
|
||||
/// Gets the value returned by the computation.
|
||||
///
|
||||
/// Similar to \ref get(), but instead of returning a
|
||||
/// tuple, returns the first value of the tuple. This is
|
||||
/// useful for the common case of a \c future<T> with exactly
|
||||
/// one type parameter.
|
||||
///
|
||||
/// Equivalent to: \c std::get<0>(f.get()).
|
||||
typename future_state<T...>::get0_return_type get0() {
|
||||
return future_state<T...>::get0(get());
|
||||
}
|
||||
|
||||
/// \cond internal
|
||||
void wait() {
|
||||
auto thread = seastar::thread_impl::get();
|
||||
|
||||
@@ -26,8 +26,34 @@
|
||||
#include "util/eclipse.hh"
|
||||
#include <malloc.h>
|
||||
|
||||
// A temporary_buffer either points inside a larger buffer, or, if the requested size
|
||||
// is too large, or if the larger buffer is scattered, contains its own storage.
|
||||
/// \addtogroup memory-module
|
||||
/// @{
|
||||
|
||||
/// Temporary, self-managed byte buffer.
|
||||
///
|
||||
/// A \c temporary_buffer is similar to an \c std::string or a \c std::unique_ptr<char[]>,
|
||||
/// but provides more flexible memory management. A \c temporary_buffer can own the memory
|
||||
/// it points to, or it can be shared with another \c temporary_buffer, or point at a substring
|
||||
/// of a buffer. It uses a \ref deleter to manage its memory.
|
||||
///
|
||||
/// A \c temporary_buffer should not be held indefinitely. It can be held while a request
|
||||
/// is processed, or for a similar duration, but not longer, as it can tie up more memory
|
||||
/// that its size indicates.
|
||||
///
|
||||
/// A buffer can be shared: two \c temporary_buffer objects will point to the same data,
|
||||
/// or a subset of it. See the \ref temporary_buffer::share() method.
|
||||
///
|
||||
/// Unless you created a \c temporary_buffer yourself, do not modify its contents, as they
|
||||
/// may be shared with another user that does not expect the data to change.
|
||||
///
|
||||
/// Use cases for a \c temporary_buffer include:
|
||||
/// - passing a substring of a tcp packet for the user to consume (zero-copy
|
||||
/// tcp input)
|
||||
/// - passing a refcounted blob held in memory to tcp, ensuring that when the TCP ACK
|
||||
/// is received, the blob is released (by decrementing its reference count) (zero-copy
|
||||
/// tcp output)
|
||||
///
|
||||
/// \tparam CharType underlying character type (must be a variant of \c char).
|
||||
template <typename CharType>
|
||||
class temporary_buffer {
|
||||
static_assert(sizeof(CharType) == 1, "must buffer stream of bytes");
|
||||
@@ -35,6 +61,10 @@ class temporary_buffer {
|
||||
size_t _size;
|
||||
deleter _deleter;
|
||||
public:
|
||||
/// Creates a \c temporary_buffer of a specified size. The buffer is not shared
|
||||
/// with anyone, and is not initialized.
|
||||
///
|
||||
/// \param size buffer size, in bytes
|
||||
explicit temporary_buffer(size_t size)
|
||||
: _buffer(static_cast<CharType*>(malloc(size * sizeof(CharType)))), _size(size)
|
||||
, _deleter(make_free_deleter(_buffer)) {
|
||||
@@ -43,17 +73,26 @@ public:
|
||||
}
|
||||
}
|
||||
//explicit temporary_buffer(CharType* borrow, size_t size) : _buffer(borrow), _size(size) {}
|
||||
/// Creates an empty \c temporary_buffer that does not point at anything.
|
||||
temporary_buffer()
|
||||
: _buffer(nullptr)
|
||||
, _size(0) {}
|
||||
temporary_buffer(const temporary_buffer&) = delete;
|
||||
/// Moves a \c temporary_buffer.
|
||||
temporary_buffer(temporary_buffer&& x) : _buffer(x._buffer), _size(x._size), _deleter(std::move(x._deleter)) {
|
||||
x._buffer = nullptr;
|
||||
x._size = 0;
|
||||
}
|
||||
/// Creates a \c temporary_buffer with a specific deleter.
|
||||
///
|
||||
/// \param buf beginning of the buffer held by this \c temporary_buffer
|
||||
/// \param size size of the buffer
|
||||
/// \param d deleter controlling destruction of the buffer. The deleter
|
||||
/// will be destroyed when there are no longer any users for the buffer.
|
||||
temporary_buffer(CharType* buf, size_t size, deleter d)
|
||||
: _buffer(buf), _size(size), _deleter(std::move(d)) {}
|
||||
void operator=(const temporary_buffer&) = delete;
|
||||
/// Moves a \c temporary_buffer.
|
||||
temporary_buffer& operator=(temporary_buffer&& x) {
|
||||
if (this != &x) {
|
||||
_buffer = x._buffer;
|
||||
@@ -64,40 +103,88 @@ public:
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/// Gets a pointer to the beginning of the buffer.
|
||||
const CharType* get() const { return _buffer; }
|
||||
/// Gets a writable pointer to the beginning of the buffer. Use only
|
||||
/// when you are certain no user expects the buffer data not to change.
|
||||
CharType* get_write() { return _buffer; }
|
||||
/// Gets the buffer size.
|
||||
size_t size() const { return _size; }
|
||||
/// Gets a pointer to the beginning of the buffer.
|
||||
const CharType* begin() { return _buffer; }
|
||||
/// Gets a pointer to the end of the buffer.
|
||||
const CharType* end() { return _buffer + _size; }
|
||||
/// Returns the buffer, but with a reduced size. The original
|
||||
/// buffer is consumed by this call and can no longer be used.
|
||||
///
|
||||
/// \param size New size; must be smaller than current size.
|
||||
/// \return the same buffer, with a prefix removed.
|
||||
temporary_buffer prefix(size_t size) && {
|
||||
auto ret = std::move(*this);
|
||||
ret._size = size;
|
||||
return ret;
|
||||
}
|
||||
/// Reads a character from a specific position in the buffer.
|
||||
///
|
||||
/// \param pos position to read character from; must be less than size.
|
||||
CharType operator[](size_t pos) const {
|
||||
return _buffer[pos];
|
||||
}
|
||||
/// Checks whether the buffer is empty.
|
||||
bool empty() const { return !size(); }
|
||||
/// Checks whether the buffer is not empty.
|
||||
operator bool() { return size(); }
|
||||
/// Create a new \c temporary_buffer object referring to the same
|
||||
/// underlying data. The underlying \ref deleter will not be destroyed
|
||||
/// until both the original and the clone have been destroyed.
|
||||
///
|
||||
/// \return a clone of the buffer object.
|
||||
temporary_buffer share() {
|
||||
return temporary_buffer(_buffer, _size, _deleter.share());
|
||||
}
|
||||
/// Create a new \c temporary_buffer object referring to a substring of the
|
||||
/// same underlying data. The underlying \ref deleter will not be destroyed
|
||||
/// until both the original and the clone have been destroyed.
|
||||
///
|
||||
/// \param pos Position of the first character to share.
|
||||
/// \param len Length of substring to share.
|
||||
/// \return a clone of the buffer object, referring to a substring.
|
||||
temporary_buffer share(size_t pos, size_t len) {
|
||||
auto ret = share();
|
||||
ret._buffer += pos;
|
||||
ret._size = len;
|
||||
return ret;
|
||||
}
|
||||
/// Remove a prefix from the buffer. The underlying data
|
||||
/// is not modified.
|
||||
///
|
||||
/// \param pos Position of first character to retain.
|
||||
void trim_front(size_t pos) {
|
||||
_buffer += pos;
|
||||
_size -= pos;
|
||||
}
|
||||
/// Remove a suffix from the buffer. The underlying data
|
||||
/// is not modified.
|
||||
///
|
||||
/// \param pos Position of first character to drop.
|
||||
void trim(size_t pos) {
|
||||
_size = pos;
|
||||
}
|
||||
/// Stops automatic memory management. When the \c temporary_buffer
|
||||
/// object is destroyed, the underlying \ref deleter will not be called.
|
||||
/// Instead, it is the caller's responsibility to destroy the deleter object
|
||||
/// when the data is no longer needed.
|
||||
///
|
||||
/// \return \ref deleter object managing the data's lifetime.
|
||||
deleter release() {
|
||||
return std::move(_deleter);
|
||||
}
|
||||
/// Creates a \c temporary_buffer object with a specified size, with
|
||||
/// memory aligned to a specific boundary.
|
||||
///
|
||||
/// \param alignment Required alignment; must be a power of two.
|
||||
/// \param size Required size.
|
||||
/// \return a new \c temporary_buffer object.
|
||||
static temporary_buffer aligned(size_t alignment, size_t size) {
|
||||
void *ptr = nullptr;
|
||||
auto ret = ::posix_memalign(&ptr, alignment, size * sizeof(CharType));
|
||||
@@ -109,4 +196,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
#endif /* TEMPORARY_BUFFER_HH_ */
|
||||
|
||||
Reference in New Issue
Block a user