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 
25 namespace lomiri
26 {
27 
28 namespace util
29 {
30 
31 namespace
32 {
33 
34 // Simple helper class so we can adopt a lock without inconvenient syntax.
35 
36 template<typename T>
37 class LockAdopter
38 {
39 public:
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 
46 private:
47  std::unique_lock<T> m_;
48 };
49 
50 } // namespace
51 
116 // TODO: Discuss throwing deleters and requirements (copy constructible, etc.) on deleter.
117 
118 template<typename R, typename D>
119 class ResourcePtr final
120 {
121 public:
123  ResourcePtr(ResourcePtr const &) = delete;
125  ResourcePtr& operator=(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 
170 private:
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 
180 template<typename R, typename D>
181 ResourcePtr<R, D>::ResourcePtr()
182  : initialized_(false)
183 {
184  static_assert(!std::is_pointer<deleter_type>::value,
185  "constructed with null function pointer deleter");
186 }
187 
193 template<typename R, typename D>
195  : delete_(d), initialized_(false)
196 {
197 }
198 
236 template<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 
250 template<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 
264 template<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 
289 template<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 
310 template<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 
341 template<typename R, typename D>
343 {
344  lhs.swap(rhs);
345 }
346 
356 template<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 
382 template<typename R, typename D>
383 inline
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 
403 template<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 
425 template<typename R, typename D>
426 inline
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 
442 template<typename R, typename D>
443 inline
444 bool ResourcePtr<R, D>::has_resource() const noexcept
445 {
446  AutoLock lock(m_);
447  return initialized_;
448 }
449 
454 template<typename R, typename D>
455 inline
456 ResourcePtr<R, D>::operator bool() const noexcept
457 {
458  return has_resource();
459 }
460 
465 template<typename R, typename D>
466 inline
468 {
469  AutoLock lock(m_);
470  return delete_;
471 }
472 
477 template<typename R, typename D>
478 inline
479 D const& ResourcePtr<R, D>::get_deleter() const noexcept
480 {
481  AutoLock lock(m_);
482  return delete_;
483 }
484 
496 template<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 
530 template<typename R, typename D>
531 inline
533 {
534  return !(*this == rhs);
535 }
536 
547 template<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 
586 template<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 
620 template<typename R, typename D>
621 inline
623 {
624  return !(*this <= rhs);
625 }
626 
638 template<typename R, typename D>
639 inline
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 
651 namespace std
652 {
653 
658 template<typename R, typename D>
659 struct equal_to<lomiri::util::ResourcePtr<R, D>>
660 {
665  {
666  return lhs == rhs;
667  }
668 };
669 
674 template<typename R, typename D>
675 struct not_equal_to<lomiri::util::ResourcePtr<R, D>>
676 {
681  {
682  return lhs != rhs;
683  }
684 };
685 
690 template<typename R, typename D>
691 struct less<lomiri::util::ResourcePtr<R, D>>
692 {
697  {
698  return lhs < rhs;
699  }
700 };
701 
706 template<typename R, typename D>
707 struct less_equal<lomiri::util::ResourcePtr<R, D>>
708 {
713  {
714  return lhs <= rhs;
715  }
716 };
717 
722 template<typename R, typename D>
723 struct greater<lomiri::util::ResourcePtr<R, D>>
724 {
729  {
730  return lhs > rhs;
731  }
732 };
733 
738 template<typename R, typename D>
739 struct 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
lomiri::util::ResourcePtr::dealloc
void dealloc()
Definition: ResourcePtr.h:404
lomiri::util::ResourcePtr::deleter_type
D deleter_type
Definition: ResourcePtr.h:137
lomiri::util::ResourcePtr::~ResourcePtr
~ResourcePtr() noexcept
Definition: ResourcePtr.h:290
lomiri::util::ResourcePtr::operator<
bool operator<(ResourcePtr const &rhs) const
Returns true if this is less than rhs.
Definition: ResourcePtr.h:548
lomiri::util::ResourcePtr::operator<=
bool operator<=(ResourcePtr const &rhs) const
Returns true if this is less than or equal to rhs.
Definition: ResourcePtr.h:587
std::less< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:696
lomiri::util::ResourcePtr::operator!=
bool operator!=(ResourcePtr const &rhs) const
Compares two instances for inequality.
Definition: ResourcePtr.h:532
lomiri::util::ResourcePtr::operator>=
bool operator>=(ResourcePtr const &rhs) const
Returns true if this is greater than or equal to rhs.
Definition: ResourcePtr.h:640
std::greater_equal< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:744
lomiri::util::ResourcePtr::has_resource
bool has_resource() const noexcept
Definition: ResourcePtr.h:444
lomiri::util::ResourcePtr::element_type
R element_type
Definition: ResourcePtr.h:130
lomiri::util::ResourcePtr::reset
void reset(R r)
Definition: ResourcePtr.h:357
lomiri::util::ResourcePtr::swap
void swap(ResourcePtr &other)
Definition: ResourcePtr.h:311
lomiri::util::ResourcePtr::operator==
bool operator==(ResourcePtr const &rhs) const
Compares two instances for equality.
Definition: ResourcePtr.h:497
lomiri::util::ResourcePtr::get_deleter
D & get_deleter() noexcept
Definition: ResourcePtr.h:467
std::not_equal_to< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:680
lomiri::util::ResourcePtr::operator>
bool operator>(ResourcePtr const &rhs) const
Returns true if this is greater than rhs.
Definition: ResourcePtr.h:622
lomiri::util::ResourcePtr::get
R get() const
Definition: ResourcePtr.h:427
std::less_equal< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:712
std::greater< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:728
lomiri::util::ResourcePtr
Class to guarantee deallocation of arbitrary resources.
Definition: ResourcePtr.h:119
lomiri
Top-level namespace for all things Lomiri-related.
Definition: Version.h:37
std::equal_to< lomiri::util::ResourcePtr< R, D > >::operator()
bool operator()(lomiri::util::ResourcePtr< R, D > const &lhs, lomiri::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:664
lomiri::util::ResourcePtr::operator=
ResourcePtr & operator=(ResourcePtr const &)=delete
lomiri::util::ResourcePtr::release
R release()
Definition: ResourcePtr.h:384