/* * 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 CEPH_COMMON_DELETER_H #define CEPH_COMMON_DELETER_H #include /// \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) noexcept : _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(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(i); return reinterpret_cast(x & ~uintptr_t(1)); } void* to_raw_object() const { return to_raw_object(_impl); } impl* from_raw_object(void* object) { auto x = reinterpret_cast(object); return reinterpret_cast(x | 1); } }; /// \cond internal struct deleter::impl { std::atomic_uint refs; deleter next; impl(deleter next) : refs(1), 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 struct lambda_deleter_impl final : deleter::impl { Deleter del; lambda_deleter_impl(deleter next, Deleter&& del) : impl(std::move(next)), del(std::move(del)) {} ~lambda_deleter_impl() override { del(); } }; template 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 inline object_deleter_impl* make_object_deleter_impl(deleter next, Object obj) { return new object_deleter_impl(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 deleter make_deleter(deleter next, Object o) { return deleter(new lambda_deleter_impl(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 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) {} ~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) { if (next_impl == d._impl) return ; 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 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 inline deleter make_object_deleter(deleter d, T&& obj) { return deleter{make_object_deleter_impl(std::move(d), std::move(obj))}; } /// @} #endif /* CEPH_COMMON_DELETER_H */