mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-22 17:40:34 +00:00
Merge branch 'deleter'
"Deleter objects are relatively heavyweight since they need to remember which destructor to call. However, raw memory needs no destructor, and we can exploit this fact."
This commit is contained in:
@@ -6,17 +6,23 @@
|
||||
#define DELETER_HH_
|
||||
|
||||
#include <memory>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
class deleter {
|
||||
public:
|
||||
struct impl;
|
||||
struct raw_object_tag {};
|
||||
private:
|
||||
// if bit 0 set, point to object to be freed directly.
|
||||
impl* _impl = nullptr;
|
||||
public:
|
||||
deleter() = default;
|
||||
deleter(const deleter&) = delete;
|
||||
deleter(deleter&& x) : _impl(x._impl) { x._impl = nullptr; }
|
||||
explicit deleter(impl* i) : _impl(i) {}
|
||||
deleter(raw_object_tag tag, void* object)
|
||||
: _impl(from_raw_object(object)) {}
|
||||
~deleter();
|
||||
deleter& operator=(deleter&& x);
|
||||
deleter& operator=(deleter&) = delete;
|
||||
@@ -30,6 +36,19 @@ public:
|
||||
this->~deleter();
|
||||
new (this) deleter(i);
|
||||
}
|
||||
private:
|
||||
bool is_raw_object() const {
|
||||
auto x = reinterpret_cast<uintptr_t>(_impl);
|
||||
return x & 1;
|
||||
}
|
||||
void* to_raw_object() const {
|
||||
auto x = reinterpret_cast<uintptr_t>(_impl);
|
||||
return reinterpret_cast<void*>(x & ~uintptr_t(1));
|
||||
}
|
||||
impl* from_raw_object(void* object) {
|
||||
auto x = reinterpret_cast<uintptr_t>(object);
|
||||
return reinterpret_cast<impl*>(x | 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct deleter::impl {
|
||||
@@ -41,6 +60,10 @@ struct deleter::impl {
|
||||
|
||||
inline
|
||||
deleter::~deleter() {
|
||||
if (is_raw_object()) {
|
||||
std::free(to_raw_object());
|
||||
return;
|
||||
}
|
||||
if (_impl && --_impl->refs == 0) {
|
||||
delete _impl;
|
||||
}
|
||||
@@ -69,14 +92,29 @@ make_deleter(deleter next, Deleter d) {
|
||||
return deleter(new lambda_deleter_impl<Deleter>(std::move(next), std::move(d)));
|
||||
}
|
||||
|
||||
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); }
|
||||
};
|
||||
|
||||
inline
|
||||
deleter
|
||||
deleter::share() {
|
||||
if (!_impl) {
|
||||
return deleter();
|
||||
}
|
||||
if (is_raw_object()) {
|
||||
_impl = new free_deleter_impl(to_raw_object());
|
||||
}
|
||||
++_impl->refs;
|
||||
return deleter(_impl);
|
||||
}
|
||||
|
||||
inline
|
||||
deleter
|
||||
make_free_deleter(void* obj) {
|
||||
return deleter(deleter::raw_object_tag(), obj);
|
||||
}
|
||||
|
||||
#endif /* DELETER_HH_ */
|
||||
|
||||
@@ -18,8 +18,8 @@ class temporary_buffer {
|
||||
deleter _deleter;
|
||||
public:
|
||||
explicit temporary_buffer(size_t size)
|
||||
: _buffer(new CharType[size]), _size(size)
|
||||
, _deleter(make_deleter(deleter(), [b = _buffer] { delete[] b; })) {}
|
||||
: _buffer(static_cast<CharType*>(malloc(size * sizeof(CharType)))), _size(size)
|
||||
, _deleter(make_free_deleter(_buffer)) {}
|
||||
//explicit temporary_buffer(CharType* borrow, size_t size) : _buffer(borrow), _size(size) {}
|
||||
temporary_buffer() = delete;
|
||||
temporary_buffer(const temporary_buffer&) = delete;
|
||||
|
||||
@@ -175,6 +175,9 @@ public:
|
||||
// build packet with iterator
|
||||
template <typename Iterator, typename Deleter>
|
||||
packet(Iterator begin, Iterator end, Deleter del);
|
||||
// build packet with iterator
|
||||
template <typename Iterator>
|
||||
packet(Iterator begin, Iterator end, deleter del);
|
||||
// append fragment (copying new fragment)
|
||||
packet(packet&& x, fragment frag);
|
||||
// prepend fragment (copying new fragment, with header optimization)
|
||||
@@ -317,6 +320,18 @@ packet::packet(Iterator begin, Iterator end, Deleter del) {
|
||||
std::copy(begin, end, _impl->_frags);
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
inline
|
||||
packet::packet(Iterator begin, Iterator end, deleter del) {
|
||||
unsigned nr_frags = 0, len = 0;
|
||||
nr_frags = std::distance(begin, end);
|
||||
std::for_each(begin, end, [&] (fragment& frag) { len += frag.size; });
|
||||
_impl = impl::allocate(nr_frags);
|
||||
_impl->_deleter = std::move(del);
|
||||
_impl->_len = len;
|
||||
_impl->_nr_frags = nr_frags;
|
||||
std::copy(begin, end, _impl->_frags);
|
||||
}
|
||||
|
||||
inline
|
||||
packet::packet(packet&& x, fragment frag)
|
||||
|
||||
@@ -376,7 +376,7 @@ class virtio_net_device : public net::device {
|
||||
vring _ring;
|
||||
unsigned _remaining_buffers = 0;
|
||||
std::vector<fragment> _fragments;
|
||||
std::vector<std::unique_ptr<char[]>> _deleters;
|
||||
std::vector<std::unique_ptr<char[], free_deleter>> _deleters;
|
||||
public:
|
||||
rxq(virtio_net_device& _if,
|
||||
vring::config config, readable_eventfd notified, writeable_eventfd kicked);
|
||||
@@ -506,7 +506,7 @@ virtio_net_device::rxq::prepare_buffers() {
|
||||
struct single_buffer_and_comletion : std::array<vring::buffer, 1> {
|
||||
promise<size_t> completed;
|
||||
} bc;
|
||||
std::unique_ptr<char[]> buf(new char[4096]);
|
||||
std::unique_ptr<char[], free_deleter> buf(reinterpret_cast<char*>(malloc(4096)));
|
||||
vring::buffer& b = bc[0];
|
||||
b.addr = virt_to_phys(buf.get());
|
||||
b.len = 4096;
|
||||
@@ -528,12 +528,19 @@ virtio_net_device::rxq::prepare_buffers() {
|
||||
|
||||
// Append current buffer
|
||||
_fragments.emplace_back(fragment{frag_buf, frag_len});
|
||||
_deleters.emplace_back(buf.release());
|
||||
_deleters.push_back(std::move(buf));
|
||||
_remaining_buffers--;
|
||||
|
||||
// Last buffer
|
||||
if (_remaining_buffers == 0) {
|
||||
packet p(_fragments.begin(), _fragments.end(), [deleters = std::move(_deleters)] () mutable { deleters.clear(); });
|
||||
deleter del;
|
||||
if (_deleters.size() == 1) {
|
||||
del = make_free_deleter(_deleters[0].release());
|
||||
_deleters.clear();
|
||||
} else {
|
||||
del = make_deleter(deleter(), [deleters = std::move(_deleters)] {});
|
||||
}
|
||||
packet p(_fragments.begin(), _fragments.end(), std::move(del));
|
||||
_dev._rx_ready = _dev._rx_ready.then([this, p = std::move(p)] () mutable {
|
||||
return _dev.queue_rx_packet(std::move(p));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user