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:
Avi Kivity
2014-10-21 15:50:53 +03:00
4 changed files with 66 additions and 6 deletions

View File

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

View File

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

View File

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

View File

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