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 
26 namespace lomiri
27 {
28 
29 namespace util
30 {
31 
32 namespace
33 {
34 
35 // Simple helper class so we can adopt a lock without inconvenient syntax.
36 
37 template<typename T>
38 class LockAdopter
39 {
40 public:
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 
47 private:
48  std::unique_lock<T> m_;
49 };
50 
51 } // namespace
52 
117 // TODO: Discuss throwing deleters and requirements (copy constructible, etc.) on deleter.
118 
119 template<typename R, typename D>
120 class ResourcePtr final
121 {
122 public:
124  ResourcePtr(ResourcePtr const &) = delete;
126  ResourcePtr& operator=(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 
171 private:
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 
181 template<typename R, typename D>
182 ResourcePtr<R, D>::ResourcePtr()
183  : initialized_(false)
184 {
185  static_assert(!std::is_pointer<deleter_type>::value,
186  "constructed with null function pointer deleter");
187 }
188 
194 template<typename R, typename D>
196  : delete_(d), initialized_(false)
197 {
198 }
199 
237 template<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 
251 template<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 
265 template<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 
290 template<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 
311 template<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 
342 template<typename R, typename D>
344 {
345  lhs.swap(rhs);
346 }
347 
357 template<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 
383 template<typename R, typename D>
384 inline
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 
404 template<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 
426 template<typename R, typename D>
427 inline
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 
443 template<typename R, typename D>
444 inline
445 bool ResourcePtr<R, D>::has_resource() const noexcept
446 {
447  AutoLock lock(m_);
448  return initialized_;
449 }
450 
455 template<typename R, typename D>
456 inline
457 ResourcePtr<R, D>::operator bool() const noexcept
458 {
459  return has_resource();
460 }
461 
466 template<typename R, typename D>
467 inline
469 {
470  AutoLock lock(m_);
471  return delete_;
472 }
473 
478 template<typename R, typename D>
479 inline
480 D const& ResourcePtr<R, D>::get_deleter() const noexcept
481 {
482  AutoLock lock(m_);
483  return delete_;
484 }
485 
497 template<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 
531 template<typename R, typename D>
532 inline
534 {
535  return !(*this == rhs);
536 }
537 
548 template<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 
587 template<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 
621 template<typename R, typename D>
622 inline
624 {
625  return !(*this <= rhs);
626 }
627 
639 template<typename R, typename D>
640 inline
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 
652 namespace std
653 {
654 
659 template<typename R, typename D>
660 struct equal_to<lomiri::util::ResourcePtr<R, D>>
661 {
666  {
667  return lhs == rhs;
668  }
669 };
670 
675 template<typename R, typename D>
676 struct not_equal_to<lomiri::util::ResourcePtr<R, D>>
677 {
682  {
683  return lhs != rhs;
684  }
685 };
686 
691 template<typename R, typename D>
692 struct less<lomiri::util::ResourcePtr<R, D>>
693 {
698  {
699  return lhs < rhs;
700  }
701 };
702 
707 template<typename R, typename D>
708 struct less_equal<lomiri::util::ResourcePtr<R, D>>
709 {
714  {
715  return lhs <= rhs;
716  }
717 };
718 
723 template<typename R, typename D>
724 struct greater<lomiri::util::ResourcePtr<R, D>>
725 {
730  {
731  return lhs > rhs;
732  }
733 };
734 
739 template<typename R, typename D>
740 struct 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
lomiri::util::ResourcePtr::dealloc
void dealloc()
Definition: ResourcePtr.h:405
lomiri::util::ResourcePtr::deleter_type
D deleter_type
Definition: ResourcePtr.h:138
lomiri::util::ResourcePtr::~ResourcePtr
~ResourcePtr() noexcept
Definition: ResourcePtr.h:291
lomiri::util::ResourcePtr::operator<
bool operator<(ResourcePtr const &rhs) const
Returns true if this is less than rhs.
Definition: ResourcePtr.h:549
lomiri::util::ResourcePtr::operator<=
bool operator<=(ResourcePtr const &rhs) const
Returns true if this is less than or equal to rhs.
Definition: ResourcePtr.h:588
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:697
lomiri::util::ResourcePtr::operator!=
bool operator!=(ResourcePtr const &rhs) const
Compares two instances for inequality.
Definition: ResourcePtr.h:533
lomiri::util::ResourcePtr::operator>=
bool operator>=(ResourcePtr const &rhs) const
Returns true if this is greater than or equal to rhs.
Definition: ResourcePtr.h:641
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:745
lomiri::util::ResourcePtr::has_resource
bool has_resource() const noexcept
Definition: ResourcePtr.h:445
lomiri::util::ResourcePtr::element_type
R element_type
Definition: ResourcePtr.h:131
lomiri::util::ResourcePtr::reset
void reset(R r)
Definition: ResourcePtr.h:358
lomiri::util::ResourcePtr::swap
void swap(ResourcePtr &other)
Definition: ResourcePtr.h:312
lomiri::util::ResourcePtr::operator==
bool operator==(ResourcePtr const &rhs) const
Compares two instances for equality.
Definition: ResourcePtr.h:498
lomiri::util::ResourcePtr::get_deleter
D & get_deleter() noexcept
Definition: ResourcePtr.h:468
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:681
lomiri::util::ResourcePtr::operator>
bool operator>(ResourcePtr const &rhs) const
Returns true if this is greater than rhs.
Definition: ResourcePtr.h:623
lomiri::util::ResourcePtr::get
R get() const
Definition: ResourcePtr.h:428
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:713
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:729
lomiri::util::ResourcePtr
Class to guarantee deallocation of arbitrary resources.
Definition: ResourcePtr.h:120
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:665
lomiri::util::ResourcePtr::operator=
ResourcePtr & operator=(ResourcePtr const &)=delete
lomiri::util::ResourcePtr::release
R release()
Definition: ResourcePtr.h:385