Merge seastar upstream

This commit is contained in:
Avi Kivity
2015-06-15 11:45:00 +03:00
3 changed files with 184 additions and 8 deletions

View File

@@ -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_ */

View File

@@ -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();

View File

@@ -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_ */