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 UNITY_UTIL_RESOURCEPTR_H
20 #define UNITY_UTIL_RESOURCEPTR_H
21 
22 #include <mutex>
23 
24 namespace unity
25 {
26 
27 namespace util
28 {
29 
30 namespace
31 {
32 
33 // Simple helper class so we can adopt a lock without inconvenient syntax.
34 
35 template<typename T>
36 class LockAdopter
37 {
38 public:
39  LockAdopter(T& mutex) noexcept
40  : m_(mutex, std::adopt_lock)
41  {
42  assert(!mutex.try_lock()); // Mutex must be locked to be adoptable.
43  }
44 
45 private:
46  std::unique_lock<T> m_;
47 };
48 
49 } // namespace
50 
115 // TODO: Discuss throwing deleters and requirements (copy constructible, etc.) on deleter.
116 
117 template<typename R, typename D>
118 class ResourcePtr final
119 {
120 public:
122  ResourcePtr(ResourcePtr const &) = delete;
124  ResourcePtr& operator=(ResourcePtr const&) = delete;
129  typedef R element_type;
130 
136  typedef D deleter_type;
137 
138  explicit ResourcePtr(D d);
139  ResourcePtr(R r, D d);
142  ~ResourcePtr() noexcept;
143 
144  void swap(ResourcePtr& other);
145 
146  void reset(R r);
147  R release();
148  void dealloc();
149 
150  R get() const;
151  bool has_resource() const noexcept;
152  explicit operator bool() const noexcept;
153  D& get_deleter() noexcept;
154  D const& get_deleter() const noexcept;
155 
156  bool operator==(ResourcePtr const& rhs) const;
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 private:
169  R resource_; // The managed resource.
170  D delete_; // The deleter to call.
171  bool initialized_; // True while we have a resource assigned.
172  mutable std::mutex m_; // Protects this instance.
173 
174  typedef std::lock_guard<decltype(m_)> AutoLock;
175  typedef LockAdopter<decltype(m_)> AdoptLock;
176 };
177 
183 template<typename R, typename D>
185  : delete_(d), initialized_(false)
186 {
187 }
188 
226 template<typename R, typename D>
228  : resource_(r), delete_(d), initialized_(true)
229 {
230 }
231 
238 // TODO: Mark as nothrow if the resource has a nothrow move constructor or nothrow copy constructor
239 
240 template<typename R, typename D>
242  : resource_(std::move(r.resource_)), delete_(r.delete_), initialized_(r.initialized_)
243 {
244  r.initialized_ = false; // Stop r from deleting its resource, if it held any. No need to lock: r is a temporary.
245 }
246 
252 // TODO: document exception safety behavior
253 
254 template<typename R, typename D>
256 {
257  AutoLock lock(m_);
258 
259  if (initialized_) // If we hold a resource, deallocate it first.
260  {
261  initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
262  delete_(resource_); // Delete our own resource.
263  }
264 
265  // r is a temporary, so we don't need to lock it.
266 
267  resource_ = std::move(r.resource_);
268  initialized_ = r.initialized_;
269  r.initialized_ = false; // Stop r from deleting its resource, if it held any.
270  delete_ = r.delete_;
271 
272  return *this;
273 }
274 
279 template<typename R, typename D>
281 {
282  try
283  {
284  dealloc();
285  }
286  catch (...)
287  {
288  }
289 }
290 
298 // TODO Split this into throw and no-throw versions depending on the underlying swap?
299 
300 template<typename R, typename D>
302 {
303  if (this == &other) // This is necessary to avoid deadlock for self-swap
304  {
305  return;
306  }
307 
308  std::lock(m_, other.m_);
309  AdoptLock left(m_);
310  AdoptLock right(other.m_);
311 
312  using std::swap; // Enable ADL
313  swap(resource_, other.resource_);
314  swap(delete_, other.delete_);
315  swap(initialized_, other.initialized_);
316 }
317 
318 // The non-member swap() must be in the same namespace as ResourcePtr, so it will work with ADL. And, once it is
319 // defined here, there is no point in adding a specialization to namespace std any longer, because ADL
320 // will find it here anyway.
321 
329 // TODO Split this into throw and no-throw versions depending on the underlying swap?
330 
331 template<typename R, typename D>
333 {
334  lhs.swap(rhs);
335 }
336 
346 template<typename R, typename D>
348 {
349  AutoLock lock(m_);
350 
351  bool has_old = initialized_;
352  R old_resource;
353 
354  if (has_old)
355  {
356  old_resource = resource_;
357  }
358  resource_ = r;
359  initialized_ = true; // If the deleter throws, we still satisfy the postcondition: resource_ == r.
360  if (has_old)
361  {
362  delete_(old_resource);
363  }
364 }
365 
372 template<typename R, typename D>
373 inline
375 {
376  AutoLock lock(m_);
377 
378  if (!initialized_)
379  {
380  throw std::logic_error("release() called on ResourcePtr without resource");
381  }
382  initialized_ = false;
383  return resource_;
384 }
385 
393 template<typename R, typename D>
395 {
396  AutoLock lock(m_);
397 
398  if (!initialized_)
399  {
400  return;
401  }
402  initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
403  delete_(resource_);
404 }
405 
415 template<typename R, typename D>
416 inline
418 {
419  AutoLock lock(m_);
420 
421  if (!initialized_)
422  {
423  throw std::logic_error("get() called on ResourcePtr without resource");
424  }
425  return resource_;
426 }
427 
432 template<typename R, typename D>
433 inline
434 bool ResourcePtr<R, D>::has_resource() const noexcept
435 {
436  AutoLock lock(m_);
437  return initialized_;
438 }
439 
444 template<typename R, typename D>
445 inline
446 ResourcePtr<R, D>::operator bool() const noexcept
447 {
448  return has_resource();
449 }
450 
455 template<typename R, typename D>
456 inline
458 {
459  AutoLock lock(m_);
460  return delete_;
461 }
462 
467 template<typename R, typename D>
468 inline
469 D const& ResourcePtr<R, D>::get_deleter() const noexcept
470 {
471  AutoLock lock(m_);
472  return delete_;
473 }
474 
486 template<typename R, typename D>
488 {
489  if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
490  {
491  return true;
492  }
493 
494  std::lock(m_, rhs.m_);
495  AdoptLock left(m_);
496  AdoptLock right(rhs.m_);
497 
498  if (!initialized_)
499  {
500  return !rhs.initialized_; // Equal if both are not initialized
501  }
502  else if (!rhs.initialized_)
503  {
504  return false; // Not equal if lhs initialized, but rhs not initialized
505  }
506  else
507  {
508  return resource_ == rhs.resource_;
509  }
510 }
511 
520 template<typename R, typename D>
521 inline
523 {
524  return !(*this == rhs);
525 }
526 
537 template<typename R, typename D>
539 {
540  if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
541  {
542  return false;
543  }
544 
545  std::lock(m_, rhs.m_);
546  AdoptLock left(m_);
547  AdoptLock right(rhs.m_);
548 
549  if (!initialized_)
550  {
551  return rhs.initialized_; // Not initialized is less than initialized
552  }
553  else if (!rhs.initialized_) // Initialized is not less than not initialized
554  {
555  return false;
556  }
557  else
558  {
559  return resource_ < rhs.resource_;
560  }
561 }
562 
576 template<typename R, typename D>
578 {
579  if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
580  {
581  return true;
582  }
583 
584  // We can't just write:
585  //
586  // return *this < rhs || *this == rhs;
587  //
588  // because that creates a race condition: the locks would be released and
589  // re-aquired in between the two comparisons.
590 
591  std::lock(m_, rhs.m_);
592  AdoptLock left(m_);
593  AdoptLock right(rhs.m_);
594 
595  return resource_ < rhs.resource_ || resource_ == rhs.resource_;
596 }
597 
610 template<typename R, typename D>
611 inline
613 {
614  return !(*this <= rhs);
615 }
616 
628 template<typename R, typename D>
629 inline
631 {
632  return !(*this < rhs);
633 }
634 
635 } // namespace util
636 
637 } // namespace unity
638 
639 // Specializations in namespace std, so we play nicely with STL and metaprogramming.
640 
641 namespace std
642 {
643 
648 template<typename R, typename D>
649 struct equal_to<unity::util::ResourcePtr<R, D>>
650 {
655  {
656  return lhs == rhs;
657  }
658 };
659 
664 template<typename R, typename D>
665 struct not_equal_to<unity::util::ResourcePtr<R, D>>
666 {
671  {
672  return lhs != rhs;
673  }
674 };
675 
680 template<typename R, typename D>
681 struct less<unity::util::ResourcePtr<R, D>>
682 {
687  {
688  return lhs < rhs;
689  }
690 };
691 
696 template<typename R, typename D>
697 struct less_equal<unity::util::ResourcePtr<R, D>>
698 {
703  {
704  return lhs <= rhs;
705  }
706 };
707 
712 template<typename R, typename D>
713 struct greater<unity::util::ResourcePtr<R, D>>
714 {
719  {
720  return lhs > rhs;
721  }
722 };
723 
728 template<typename R, typename D>
729 struct greater_equal<unity::util::ResourcePtr<R, D>>
730 {
735  {
736  return lhs >= rhs;
737  }
738 };
739 
740 // TODO: provide hash if std::hash<R> exists.
741 
742 } // namespace std
743 
744 #endif
bool operator!=(ResourcePtr const &rhs) const
Compares two instances for inequality.
Definition: ResourcePtr.h:522
bool operator<(ResourcePtr const &rhs) const
Returns true if this is less than rhs.
Definition: ResourcePtr.h:538
Class to guarantee deallocation of arbitrary resources.
Definition: ResourcePtr.h:118
bool operator()(unity::util::ResourcePtr< R, D > const &lhs, unity::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:702
bool operator()(unity::util::ResourcePtr< R, D > const &lhs, unity::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:686
R element_type
Definition: ResourcePtr.h:129
bool operator>(ResourcePtr const &rhs) const
Returns true if this is greater than rhs.
Definition: ResourcePtr.h:612
Definition: ResourcePtr.h:641
bool operator()(unity::util::ResourcePtr< R, D > const &lhs, unity::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:654
ResourcePtr & operator=(ResourcePtr const &)=delete
bool operator>=(ResourcePtr const &rhs) const
Returns true if this is greater than or equal to rhs.
Definition: ResourcePtr.h:630
R release()
Definition: ResourcePtr.h:374
bool operator()(unity::util::ResourcePtr< R, D > const &lhs, unity::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:670
bool operator()(unity::util::ResourcePtr< R, D > const &lhs, unity::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:734
Top-level namespace for all things Unity-related.
Definition: Version.h:37
bool operator==(ResourcePtr const &rhs) const
Compares two instances for equality.
Definition: ResourcePtr.h:487
ResourcePtr(ResourcePtr const &)=delete
bool operator()(unity::util::ResourcePtr< R, D > const &lhs, unity::util::ResourcePtr< R, D > const &rhs) const
Definition: ResourcePtr.h:718
void swap(ResourcePtr &other)
Definition: ResourcePtr.h:301
D deleter_type
Definition: ResourcePtr.h:136
R get() const
Definition: ResourcePtr.h:417
bool has_resource() const noexcept
Definition: ResourcePtr.h:434
void reset(R r)
Definition: ResourcePtr.h:347
bool operator<=(ResourcePtr const &rhs) const
Returns true if this is less than or equal to rhs.
Definition: ResourcePtr.h:577
void dealloc()
Definition: ResourcePtr.h:394
~ResourcePtr() noexcept
Definition: ResourcePtr.h:280
D & get_deleter() noexcept
Definition: ResourcePtr.h:457