276 lines
7.9 KiB
C++
276 lines
7.9 KiB
C++
/*
|
|
* This file is open source software, licensed to you under the terms
|
|
* of the Apache License, Version 2.0 (the "License"). See the NOTICE file
|
|
* distributed with this work for additional information regarding copyright
|
|
* ownership. You may not use this file except in compliance with the License.
|
|
*
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
/*
|
|
* Copyright (C) 2014 Cloudius Systems, Ltd.
|
|
*/
|
|
|
|
#ifndef DELETER_HH_
|
|
#define DELETER_HH_
|
|
|
|
#include <memory>
|
|
#include <cstdlib>
|
|
#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) {
|
|
auto x = reinterpret_cast<uintptr_t>(i);
|
|
return x & 1;
|
|
}
|
|
bool is_raw_object() const {
|
|
return is_raw_object(_impl);
|
|
}
|
|
static void* to_raw_object(impl* i) {
|
|
auto x = reinterpret_cast<uintptr_t>(i);
|
|
return reinterpret_cast<void*>(x & ~uintptr_t(1));
|
|
}
|
|
void* to_raw_object() const {
|
|
return to_raw_object(_impl);
|
|
}
|
|
impl* from_raw_object(void* object) {
|
|
auto x = reinterpret_cast<uintptr_t>(object);
|
|
return reinterpret_cast<impl*>(x | 1);
|
|
}
|
|
};
|
|
|
|
/// \cond internal
|
|
struct deleter::impl {
|
|
unsigned refs = 1;
|
|
deleter next;
|
|
impl(deleter next) : next(std::move(next)) {}
|
|
virtual ~impl() {}
|
|
};
|
|
/// \endcond
|
|
|
|
inline
|
|
deleter::~deleter() {
|
|
if (is_raw_object()) {
|
|
std::free(to_raw_object());
|
|
return;
|
|
}
|
|
if (_impl && --_impl->refs == 0) {
|
|
delete _impl;
|
|
}
|
|
}
|
|
|
|
inline
|
|
deleter& deleter::operator=(deleter&& x) {
|
|
if (this != &x) {
|
|
this->~deleter();
|
|
new (this) deleter(std::move(x));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// \cond internal
|
|
template <typename Deleter>
|
|
struct lambda_deleter_impl final : deleter::impl {
|
|
Deleter del;
|
|
lambda_deleter_impl(deleter next, Deleter&& del)
|
|
: impl(std::move(next)), del(std::move(del)) {}
|
|
virtual ~lambda_deleter_impl() override { del(); }
|
|
};
|
|
|
|
template <typename Object>
|
|
struct object_deleter_impl final : deleter::impl {
|
|
Object obj;
|
|
object_deleter_impl(deleter next, Object&& obj)
|
|
: impl(std::move(next)), obj(std::move(obj)) {}
|
|
};
|
|
|
|
template <typename Object>
|
|
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
|
|
|
|
/// 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, Object o) {
|
|
return deleter(new lambda_deleter_impl<Object>(std::move(next), std::move(o)));
|
|
}
|
|
|
|
/// 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(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
|
|
deleter::share() {
|
|
if (!_impl) {
|
|
return deleter();
|
|
}
|
|
if (is_raw_object()) {
|
|
_impl = new free_deleter_impl(to_raw_object());
|
|
}
|
|
++_impl->refs;
|
|
return deleter(_impl);
|
|
}
|
|
|
|
// Appends 'd' to the chain of deleters. Avoids allocation if possible. For
|
|
// performance reasons the current chain should be shorter and 'd' should be
|
|
// longer.
|
|
inline
|
|
void deleter::append(deleter d) {
|
|
if (!d._impl) {
|
|
return;
|
|
}
|
|
impl* next_impl = _impl;
|
|
deleter* next_d = this;
|
|
while (next_impl) {
|
|
assert(next_impl != d._impl);
|
|
if (is_raw_object(next_impl)) {
|
|
next_d->_impl = next_impl = new free_deleter_impl(to_raw_object(next_impl));
|
|
}
|
|
if (next_impl->refs != 1) {
|
|
next_d->_impl = next_impl = make_object_deleter_impl(std::move(next_impl->next), deleter(next_impl));
|
|
}
|
|
next_d = &next_impl->next;
|
|
next_impl = next_d->_impl;
|
|
}
|
|
next_d->_impl = d._impl;
|
|
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) {
|
|
if (!obj) {
|
|
return deleter();
|
|
}
|
|
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
|
|
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
|
|
make_object_deleter(deleter d, T&& obj) {
|
|
return deleter{make_object_deleter_impl(std::move(d), std::move(obj))};
|
|
}
|
|
|
|
/// @}
|
|
|
|
#endif /* DELETER_HH_ */
|