My Project
ResourcePtr.h
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Michi Henning <michi.henning@canonical.com>
17 */
18
19#ifndef LOMIRI_UTIL_RESOURCEPTR_H
20#define LOMIRI_UTIL_RESOURCEPTR_H
21
22#include <mutex>
23#include <type_traits>
24#include <stdexcept>
25
26namespace lomiri
27{
28
29namespace util
30{
31
32namespace
33{
34
35// Simple helper class so we can adopt a lock without inconvenient syntax.
36
37template<typename T>
38class LockAdopter
39{
40public:
41 LockAdopter(T& mutex) noexcept
42 : m_(mutex, std::adopt_lock)
43 {
44 assert(!mutex.try_lock()); // Mutex must be locked to be adoptable.
45 }
46
47private:
48 std::unique_lock<T> m_;
49};
50
51} // namespace
52
117// TODO: Discuss throwing deleters and requirements (copy constructible, etc.) on deleter.
118
119template<typename R, typename D>
120class ResourcePtr final
121{
122public:
124 ResourcePtr(ResourcePtr const &) = delete;
131 typedef R element_type;
132
138 typedef D deleter_type;
139
140 ResourcePtr();
141 explicit ResourcePtr(D d);
142 ResourcePtr(R r, D d);
145 ~ResourcePtr() noexcept;
146
147 void swap(ResourcePtr& other);
148
149 void reset(R r);
150 R release();
151 void dealloc();
152
153 R get() const;
154 bool has_resource() const noexcept;
155 explicit operator bool() const noexcept;
156 D& get_deleter() noexcept;
157 D const& get_deleter() const noexcept;
158
159 bool operator==(ResourcePtr const& rhs) const;
160
161 bool operator!=(ResourcePtr const& rhs) const;
162
163 bool operator<(ResourcePtr const& rhs) const;
164
165 bool operator<=(ResourcePtr const& rhs) const;
166
167 bool operator>(ResourcePtr const& rhs) const;
168
169 bool operator>=(ResourcePtr const& rhs) const;
170
171private:
172 R resource_; // The managed resource.
173 D delete_; // The deleter to call.
174 bool initialized_; // True while we have a resource assigned.
175 mutable std::mutex m_; // Protects this instance.
176
177 typedef std::lock_guard<decltype(m_)> AutoLock;
178 typedef LockAdopter<decltype(m_)> AdoptLock;
179};
180
181template<typename R, typename D>
183 : initialized_(false)
184{
185 static_assert(!std::is_pointer<deleter_type>::value,
186 "constructed with null function pointer deleter");
187}
188
194template<typename R, typename D>
196 : delete_(d), initialized_(false)
197{
198}
199
237template<typename R, typename D>
239 : resource_(r), delete_(d), initialized_(true)
240{
241}
242
249// TODO: Mark as nothrow if the resource has a nothrow move constructor or nothrow copy constructor
250
251template<typename R, typename D>
253 : resource_(std::move(r.resource_)), delete_(r.delete_), initialized_(r.initialized_)
254{
255 r.initialized_ = false; // Stop r from deleting its resource, if it held any. No need to lock: r is a temporary.
256}
257
263// TODO: document exception safety behavior
264
265template<typename R, typename D>
267{
268 AutoLock lock(m_);
269
270 if (initialized_) // If we hold a resource, deallocate it first.
271 {
272 initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
273 delete_(resource_); // Delete our own resource.
274 }
275
276 // r is a temporary, so we don't need to lock it.
277
278 resource_ = std::move(r.resource_);
279 initialized_ = r.initialized_;
280 r.initialized_ = false; // Stop r from deleting its resource, if it held any.
281 delete_ = r.delete_;
282
283 return *this;
284}
285
290template<typename R, typename D>
292{
293 try
294 {
295 dealloc();
296 }
297 catch (...)
298 {
299 }
300}
301
309// TODO Split this into throw and no-throw versions depending on the underlying swap?
310
311template<typename R, typename D>
313{
314 if (this == &other) // This is necessary to avoid deadlock for self-swap
315 {
316 return;
317 }
318
319 std::lock(m_, other.m_);
320 AdoptLock left(m_);
321 AdoptLock right(other.m_);
322
323 using std::swap; // Enable ADL
324 swap(resource_, other.resource_);
325 swap(delete_, other.delete_);
326 swap(initialized_, other.initialized_);
327}
328
329// The non-member swap() must be in the same namespace as ResourcePtr, so it will work with ADL. And, once it is
330// defined here, there is no point in adding a specialization to namespace std any longer, because ADL
331// will find it here anyway.
332
340// TODO Split this into throw and no-throw versions depending on the underlying swap?
341
342template<typename R, typename D>
344{
345 lhs.swap(rhs);
346}
347
357template<typename R, typename D>
359{
360 AutoLock lock(m_);
361
362 bool has_old = initialized_;
363 R old_resource;
364
365 if (has_old)
366 {
367 old_resource = resource_;
368 }
369 resource_ = r;
370 initialized_ = true; // If the deleter throws, we still satisfy the postcondition: resource_ == r.
371 if (has_old)
372 {
373 delete_(old_resource);
374 }
375}
376
383template<typename R, typename D>
384inline
386{
387 AutoLock lock(m_);
388
389 if (!initialized_)
390 {
391 throw std::logic_error("release() called on ResourcePtr without resource");
392 }
393 initialized_ = false;
394 return resource_;
395}
396
404template<typename R, typename D>
406{
407 AutoLock lock(m_);
408
409 if (!initialized_)
410 {
411 return;
412 }
413 initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
414 delete_(resource_);
415}
416
426template<typename R, typename D>
427inline
429{
430 AutoLock lock(m_);
431
432 if (!initialized_)
433 {
434 throw std::logic_error("get() called on ResourcePtr without resource");
435 }
436 return resource_;
437}
438
443template<typename R, typename D>
444inline
446{
447 AutoLock lock(m_);
448 return initialized_;
449}
450
455template<typename R, typename D>
456inline
457ResourcePtr<R, D>::operator bool() const noexcept
458{
459 return has_resource();
460}
461
466template<typename R, typename D>
467inline
469{
470 AutoLock lock(m_);
471 return delete_;
472}
473
478template<typename R, typename D>
479inline
480D const& ResourcePtr<R, D>::get_deleter() const noexcept
481{
482 AutoLock lock(m_);
483 return delete_;
484}
485
497template<typename R, typename D>
499{
500 if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
501 {
502 return true;
503 }
504
505 std::lock(m_, rhs.m_);
506 AdoptLock left(m_);
507 AdoptLock right(rhs.m_);
508
509 if (!initialized_)
510 {
511 return !rhs.initialized_; // Equal if both are not initialized
512 }
513 else if (!rhs.initialized_)
514 {
515 return false; // Not equal if lhs initialized, but rhs not initialized
516 }
517 else
518 {
519 return resource_ == rhs.resource_;
520 }
521}
522
531template<typename R, typename D>
532inline
534{
535 return !(*this == rhs);
536}
537
548template<typename R, typename D>
550{
551 if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
552 {
553 return false;
554 }
555
556 std::lock(m_, rhs.m_);
557 AdoptLock left(m_);
558 AdoptLock right(rhs.m_);
559
560 if (!initialized_)
561 {
562 return rhs.initialized_; // Not initialized is less than initialized
563 }
564 else if (!rhs.initialized_) // Initialized is not less than not initialized
565 {
566 return false;
567 }
568 else
569 {
570 return resource_ < rhs.resource_;
571 }
572}
573
587template<typename R, typename D>
589{
590 if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
591 {
592 return true;
593 }
594
595 // We can't just write:
596 //
597 // return *this < rhs || *this == rhs;
598 //
599 // because that creates a race condition: the locks would be released and
600 // re-aquired in between the two comparisons.
601
602 std::lock(m_, rhs.m_);
603 AdoptLock left(m_);
604 AdoptLock right(rhs.m_);
605
606 return resource_ < rhs.resource_ || resource_ == rhs.resource_;
607}
608
621template<typename R, typename D>
622inline
624{
625 return !(*this <= rhs);
626}
627
639template<typename R, typename D>
640inline
642{
643 return !(*this < rhs);
644}
645
646} // namespace util
647
648} // namespace lomiri
649
650// Specializations in namespace std, so we play nicely with STL and metaprogramming.
651
652namespace std
653{
654
659template<typename R, typename D>
660struct equal_to<lomiri::util::ResourcePtr<R, D>>
661{
666 {
667 return lhs == rhs;
668 }
669};
670
675template<typename R, typename D>
676struct not_equal_to<lomiri::util::ResourcePtr<R, D>>
677{
682 {
683 return lhs != rhs;
684 }
685};
686
691template<typename R, typename D>
692struct less<lomiri::util::ResourcePtr<R, D>>
693{
698 {
699 return lhs < rhs;
700 }
701};
702
707template<typename R, typename D>
708struct less_equal<lomiri::util::ResourcePtr<R, D>>
709{
714 {
715 return lhs <= rhs;
716 }
717};
718
723template<typename R, typename D>
724struct greater<lomiri::util::ResourcePtr<R, D>>
725{
730 {
731 return lhs > rhs;
732 }
733};
734
739template<typename R, typename D>
740struct greater_equal<lomiri::util::ResourcePtr<R, D>>
741{
746 {
747 return lhs >= rhs;
748 }
749};
750
751// TODO: provide hash if std::hash<R> exists.
752
753} // namespace std
754
755#endif
Class to guarantee deallocation of arbitrary resources.
Definition: ResourcePtr.h:121
bool operator>=(ResourcePtr const &rhs) const
Returns true if this is greater than or equal to rhs.
Definition: ResourcePtr.h:641
bool operator<=(ResourcePtr const &rhs) const
Returns true if this is less than or equal to rhs.
Definition: ResourcePtr.h:588
void reset(R r)
Definition: ResourcePtr.h:358
D & get_deleter() noexcept
Definition: ResourcePtr.h:468
R release()
Definition: ResourcePtr.h:385
R element_type
Definition: ResourcePtr.h:131
bool operator>(ResourcePtr const &rhs) const
Returns true if this is greater than rhs.
Definition: ResourcePtr.h:623
void swap(ResourcePtr &other)
Definition: ResourcePtr.h:312
ResourcePtr(ResourcePtr const &)=delete
D deleter_type
Definition: ResourcePtr.h:138
void dealloc()
Definition: ResourcePtr.h:405
ResourcePtr & operator=(ResourcePtr const &)=delete
bool operator==(ResourcePtr const &rhs) const
Compares two instances for equality.
Definition: ResourcePtr.h:498
bool has_resource() const noexcept
Definition: ResourcePtr.h:445
~ResourcePtr() noexcept
Definition: ResourcePtr.h:291
R get() const
Definition: ResourcePtr.h:428
bool operator!=(ResourcePtr const &rhs) const
Compares two instances for inequality.
Definition: ResourcePtr.h:533
bool operator<(ResourcePtr const &rhs) const
Returns true if this is less than rhs.
Definition: ResourcePtr.h:549
Top-level namespace for all things Lomiri-related.
Definition: Version.h:38
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:665
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:729
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:745
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:697
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:713
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:681