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
25namespace lomiri
26{
27
28namespace util
29{
30
31namespace
32{
33
34// Simple helper class so we can adopt a lock without inconvenient syntax.
35
36template<typename T>
37class LockAdopter
38{
39public:
40 LockAdopter(T& mutex) noexcept
41 : m_(mutex, std::adopt_lock)
42 {
43 assert(!mutex.try_lock()); // Mutex must be locked to be adoptable.
44 }
45
46private:
47 std::unique_lock<T> m_;
48};
49
50} // namespace
51
116// TODO: Discuss throwing deleters and requirements (copy constructible, etc.) on deleter.
117
118template<typename R, typename D>
119class ResourcePtr final
120{
121public:
123 ResourcePtr(ResourcePtr const &) = delete;
130 typedef R element_type;
131
137 typedef D deleter_type;
138
139 ResourcePtr();
140 explicit ResourcePtr(D d);
141 ResourcePtr(R r, D d);
144 ~ResourcePtr() noexcept;
145
146 void swap(ResourcePtr& other);
147
148 void reset(R r);
149 R release();
150 void dealloc();
151
152 R get() const;
153 bool has_resource() const noexcept;
154 explicit operator bool() const noexcept;
155 D& get_deleter() noexcept;
156 D const& get_deleter() const noexcept;
157
158 bool operator==(ResourcePtr const& rhs) const;
159
160 bool operator!=(ResourcePtr const& rhs) const;
161
162 bool operator<(ResourcePtr const& rhs) const;
163
164 bool operator<=(ResourcePtr const& rhs) const;
165
166 bool operator>(ResourcePtr const& rhs) const;
167
168 bool operator>=(ResourcePtr const& rhs) const;
169
170private:
171 R resource_; // The managed resource.
172 D delete_; // The deleter to call.
173 bool initialized_; // True while we have a resource assigned.
174 mutable std::mutex m_; // Protects this instance.
175
176 typedef std::lock_guard<decltype(m_)> AutoLock;
177 typedef LockAdopter<decltype(m_)> AdoptLock;
178};
179
180template<typename R, typename D>
182 : initialized_(false)
183{
184 static_assert(!std::is_pointer<deleter_type>::value,
185 "constructed with null function pointer deleter");
186}
187
193template<typename R, typename D>
195 : delete_(d), initialized_(false)
196{
197}
198
236template<typename R, typename D>
238 : resource_(r), delete_(d), initialized_(true)
239{
240}
241
248// TODO: Mark as nothrow if the resource has a nothrow move constructor or nothrow copy constructor
249
250template<typename R, typename D>
252 : resource_(std::move(r.resource_)), delete_(r.delete_), initialized_(r.initialized_)
253{
254 r.initialized_ = false; // Stop r from deleting its resource, if it held any. No need to lock: r is a temporary.
255}
256
262// TODO: document exception safety behavior
263
264template<typename R, typename D>
266{
267 AutoLock lock(m_);
268
269 if (initialized_) // If we hold a resource, deallocate it first.
270 {
271 initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
272 delete_(resource_); // Delete our own resource.
273 }
274
275 // r is a temporary, so we don't need to lock it.
276
277 resource_ = std::move(r.resource_);
278 initialized_ = r.initialized_;
279 r.initialized_ = false; // Stop r from deleting its resource, if it held any.
280 delete_ = r.delete_;
281
282 return *this;
283}
284
289template<typename R, typename D>
291{
292 try
293 {
294 dealloc();
295 }
296 catch (...)
297 {
298 }
299}
300
308// TODO Split this into throw and no-throw versions depending on the underlying swap?
309
310template<typename R, typename D>
312{
313 if (this == &other) // This is necessary to avoid deadlock for self-swap
314 {
315 return;
316 }
317
318 std::lock(m_, other.m_);
319 AdoptLock left(m_);
320 AdoptLock right(other.m_);
321
322 using std::swap; // Enable ADL
323 swap(resource_, other.resource_);
324 swap(delete_, other.delete_);
325 swap(initialized_, other.initialized_);
326}
327
328// The non-member swap() must be in the same namespace as ResourcePtr, so it will work with ADL. And, once it is
329// defined here, there is no point in adding a specialization to namespace std any longer, because ADL
330// will find it here anyway.
331
339// TODO Split this into throw and no-throw versions depending on the underlying swap?
340
341template<typename R, typename D>
343{
344 lhs.swap(rhs);
345}
346
356template<typename R, typename D>
358{
359 AutoLock lock(m_);
360
361 bool has_old = initialized_;
362 R old_resource;
363
364 if (has_old)
365 {
366 old_resource = resource_;
367 }
368 resource_ = r;
369 initialized_ = true; // If the deleter throws, we still satisfy the postcondition: resource_ == r.
370 if (has_old)
371 {
372 delete_(old_resource);
373 }
374}
375
382template<typename R, typename D>
383inline
385{
386 AutoLock lock(m_);
387
388 if (!initialized_)
389 {
390 throw std::logic_error("release() called on ResourcePtr without resource");
391 }
392 initialized_ = false;
393 return resource_;
394}
395
403template<typename R, typename D>
405{
406 AutoLock lock(m_);
407
408 if (!initialized_)
409 {
410 return;
411 }
412 initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
413 delete_(resource_);
414}
415
425template<typename R, typename D>
426inline
428{
429 AutoLock lock(m_);
430
431 if (!initialized_)
432 {
433 throw std::logic_error("get() called on ResourcePtr without resource");
434 }
435 return resource_;
436}
437
442template<typename R, typename D>
443inline
445{
446 AutoLock lock(m_);
447 return initialized_;
448}
449
454template<typename R, typename D>
455inline
456ResourcePtr<R, D>::operator bool() const noexcept
457{
458 return has_resource();
459}
460
465template<typename R, typename D>
466inline
468{
469 AutoLock lock(m_);
470 return delete_;
471}
472
477template<typename R, typename D>
478inline
479D const& ResourcePtr<R, D>::get_deleter() const noexcept
480{
481 AutoLock lock(m_);
482 return delete_;
483}
484
496template<typename R, typename D>
498{
499 if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
500 {
501 return true;
502 }
503
504 std::lock(m_, rhs.m_);
505 AdoptLock left(m_);
506 AdoptLock right(rhs.m_);
507
508 if (!initialized_)
509 {
510 return !rhs.initialized_; // Equal if both are not initialized
511 }
512 else if (!rhs.initialized_)
513 {
514 return false; // Not equal if lhs initialized, but rhs not initialized
515 }
516 else
517 {
518 return resource_ == rhs.resource_;
519 }
520}
521
530template<typename R, typename D>
531inline
533{
534 return !(*this == rhs);
535}
536
547template<typename R, typename D>
549{
550 if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
551 {
552 return false;
553 }
554
555 std::lock(m_, rhs.m_);
556 AdoptLock left(m_);
557 AdoptLock right(rhs.m_);
558
559 if (!initialized_)
560 {
561 return rhs.initialized_; // Not initialized is less than initialized
562 }
563 else if (!rhs.initialized_) // Initialized is not less than not initialized
564 {
565 return false;
566 }
567 else
568 {
569 return resource_ < rhs.resource_;
570 }
571}
572
586template<typename R, typename D>
588{
589 if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
590 {
591 return true;
592 }
593
594 // We can't just write:
595 //
596 // return *this < rhs || *this == rhs;
597 //
598 // because that creates a race condition: the locks would be released and
599 // re-aquired in between the two comparisons.
600
601 std::lock(m_, rhs.m_);
602 AdoptLock left(m_);
603 AdoptLock right(rhs.m_);
604
605 return resource_ < rhs.resource_ || resource_ == rhs.resource_;
606}
607
620template<typename R, typename D>
621inline
623{
624 return !(*this <= rhs);
625}
626
638template<typename R, typename D>
639inline
641{
642 return !(*this < rhs);
643}
644
645} // namespace util
646
647} // namespace lomiri
648
649// Specializations in namespace std, so we play nicely with STL and metaprogramming.
650
651namespace std
652{
653
658template<typename R, typename D>
659struct equal_to<lomiri::util::ResourcePtr<R, D>>
660{
665 {
666 return lhs == rhs;
667 }
668};
669
674template<typename R, typename D>
675struct not_equal_to<lomiri::util::ResourcePtr<R, D>>
676{
681 {
682 return lhs != rhs;
683 }
684};
685
690template<typename R, typename D>
691struct less<lomiri::util::ResourcePtr<R, D>>
692{
697 {
698 return lhs < rhs;
699 }
700};
701
706template<typename R, typename D>
707struct less_equal<lomiri::util::ResourcePtr<R, D>>
708{
713 {
714 return lhs <= rhs;
715 }
716};
717
722template<typename R, typename D>
723struct greater<lomiri::util::ResourcePtr<R, D>>
724{
729 {
730 return lhs > rhs;
731 }
732};
733
738template<typename R, typename D>
739struct greater_equal<lomiri::util::ResourcePtr<R, D>>
740{
745 {
746 return lhs >= rhs;
747 }
748};
749
750// TODO: provide hash if std::hash<R> exists.
751
752} // namespace std
753
754#endif
Class to guarantee deallocation of arbitrary resources.
Definition: ResourcePtr.h:120
bool operator>=(ResourcePtr const &rhs) const
Returns true if this is greater than or equal to rhs.
Definition: ResourcePtr.h:640
bool operator<=(ResourcePtr const &rhs) const
Returns true if this is less than or equal to rhs.
Definition: ResourcePtr.h:587
void reset(R r)
Definition: ResourcePtr.h:357
D & get_deleter() noexcept
Definition: ResourcePtr.h:467
R release()
Definition: ResourcePtr.h:384
R element_type
Definition: ResourcePtr.h:130
bool operator>(ResourcePtr const &rhs) const
Returns true if this is greater than rhs.
Definition: ResourcePtr.h:622
void swap(ResourcePtr &other)
Definition: ResourcePtr.h:311
ResourcePtr(ResourcePtr const &)=delete
D deleter_type
Definition: ResourcePtr.h:137
void dealloc()
Definition: ResourcePtr.h:404
ResourcePtr & operator=(ResourcePtr const &)=delete
bool operator==(ResourcePtr const &rhs) const
Compares two instances for equality.
Definition: ResourcePtr.h:497
bool has_resource() const noexcept
Definition: ResourcePtr.h:444
~ResourcePtr() noexcept
Definition: ResourcePtr.h:290
R get() const
Definition: ResourcePtr.h:427
bool operator!=(ResourcePtr const &rhs) const
Compares two instances for inequality.
Definition: ResourcePtr.h:532
bool operator<(ResourcePtr const &rhs) const
Returns true if this is less than rhs.
Definition: ResourcePtr.h:548
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:664
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:728
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:744
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:696
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:712
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:680