My Project
 All Classes Namespaces Files Functions Typedefs Enumerations Enumerator Properties Macros
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 <unity/util/NonCopyable.h>
23 
24 #include <mutex>
25 
26 namespace unity
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 : private NonCopyable
121 {
122 public:
127  typedef R element_type;
128 
134  typedef D deleter_type;
135 
136  explicit ResourcePtr(D d);
137  ResourcePtr(R r, D d);
140  ~ResourcePtr() noexcept;
141 
142  void swap(ResourcePtr& other);
143 
144  void reset(R r);
145  R release();
146  void dealloc();
147 
148  R get() const;
149  bool has_resource() const noexcept;
150  explicit operator bool() const noexcept;
151  D& get_deleter() noexcept;
152  D const& get_deleter() const noexcept;
153 
154  bool operator==(ResourcePtr const& rhs) const;
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 private:
167  R resource_; // The managed resource.
168  D delete_; // The deleter to call.
169  bool initialized_; // True while we have a resource assigned.
170  mutable std::mutex m_; // Protects this instance.
171 
172  typedef std::lock_guard<decltype(m_)> AutoLock;
173  typedef LockAdopter<decltype(m_)> AdoptLock;
174 };
175 
181 template<typename R, typename D>
183  : delete_(d), initialized_(false)
184 {
185 }
186 
224 template<typename R, typename D>
226  : resource_(r), delete_(d), initialized_(true)
227 {
228 }
229 
236 // TODO: Mark as nothrow if the resource has a nothrow move constructor or nothrow copy constructor
237 
238 template<typename R, typename D>
240  : resource_(std::move(r.resource_)), delete_(r.delete_), initialized_(r.initialized_)
241 {
242  r.initialized_ = false; // Stop r from deleting its resource, if it held any. No need to lock: r is a temporary.
243 }
244 
250 // TODO: document exception safety behavior
251 
252 template<typename R, typename D>
254 {
255  AutoLock lock(m_);
256 
257  if (initialized_) // If we hold a resource, deallocate it first.
258  {
259  initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
260  delete_(resource_); // Delete our own resource.
261  }
262 
263  // r is a temporary, so we don't need to lock it.
264 
265  resource_ = std::move(r.resource_);
266  initialized_ = r.initialized_;
267  r.initialized_ = false; // Stop r from deleting its resource, if it held any.
268  delete_ = r.delete_;
269 
270  return *this;
271 }
272 
277 template<typename R, typename D>
279 {
280  try
281  {
282  dealloc();
283  }
284  catch (...)
285  {
286  }
287 }
288 
296 // TODO Split this into throw and no-throw versions depending on the underlying swap?
297 
298 template<typename R, typename D>
300 {
301  if (this == &other) // This is necessary to avoid deadlock for self-swap
302  {
303  return;
304  }
305 
306  std::lock(m_, other.m_);
307  AdoptLock left(m_);
308  AdoptLock right(other.m_);
309 
310  using std::swap; // Enable ADL
311  swap(resource_, other.resource_);
312  swap(delete_, other.delete_);
313  swap(initialized_, other.initialized_);
314 }
315 
316 // The non-member swap() must be in the same namespace as ResourcePtr, so it will work with ADL. And, once it is
317 // defined here, there is no point in adding a specialization to namespace std any longer, because ADL
318 // will find it here anyway.
319 
327 // TODO Split this into throw and no-throw versions depending on the underlying swap?
328 
329 template<typename R, typename D>
331 {
332  lhs.swap(rhs);
333 }
334 
344 template<typename R, typename D>
346 {
347  AutoLock lock(m_);
348 
349  bool has_old = initialized_;
350  R old_resource;
351 
352  if (has_old)
353  {
354  old_resource = resource_;
355  }
356  resource_ = r;
357  initialized_ = true; // If the deleter throws, we still satisfy the postcondition: resource_ == r.
358  if (has_old)
359  {
360  delete_(old_resource);
361  }
362 }
363 
370 template<typename R, typename D>
371 inline
373 {
374  AutoLock lock(m_);
375 
376  if (!initialized_)
377  {
378  throw std::logic_error("release() called on ResourcePtr without resource");
379  }
380  initialized_ = false;
381  return resource_;
382 }
383 
391 template<typename R, typename D>
393 {
394  AutoLock lock(m_);
395 
396  if (!initialized_)
397  {
398  return;
399  }
400  initialized_ = false; // If the deleter throws, we will not try it again for the same resource.
401  delete_(resource_);
402 }
403 
413 template<typename R, typename D>
414 inline
416 {
417  AutoLock lock(m_);
418 
419  if (!initialized_)
420  {
421  throw std::logic_error("get() called on ResourcePtr without resource");
422  }
423  return resource_;
424 }
425 
430 template<typename R, typename D>
431 inline
432 bool ResourcePtr<R, D>::has_resource() const noexcept
433 {
434  AutoLock lock(m_);
435  return initialized_;
436 }
437 
442 template<typename R, typename D>
443 inline
444 ResourcePtr<R, D>::operator bool() const noexcept
445 {
446  return has_resource();
447 }
448 
453 template<typename R, typename D>
454 inline
456 {
457  AutoLock lock(m_);
458  return delete_;
459 }
460 
465 template<typename R, typename D>
466 inline
467 D const& ResourcePtr<R, D>::get_deleter() const noexcept
468 {
469  AutoLock lock(m_);
470  return delete_;
471 }
472 
484 template<typename R, typename D>
486 {
487  if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
488  {
489  return true;
490  }
491 
492  std::lock(m_, rhs.m_);
493  AdoptLock left(m_);
494  AdoptLock right(rhs.m_);
495 
496  if (!initialized_)
497  {
498  return !rhs.initialized_; // Equal if both are not initialized
499  }
500  else if (!rhs.initialized_)
501  {
502  return false; // Not equal if lhs initialized, but rhs not initialized
503  }
504  else
505  {
506  return resource_ == rhs.resource_;
507  }
508 }
509 
518 template<typename R, typename D>
519 inline
521 {
522  return !(*this == rhs);
523 }
524 
536 template<typename R, typename D>
538 {
539  if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
540  {
541  return false;
542  }
543 
544  std::lock(m_, rhs.m_);
545  AdoptLock left(m_);
546  AdoptLock right(rhs.m_);
547 
548  if (!initialized_)
549  {
550  return rhs.initialized_; // Not initialized is less than initialized
551  }
552  else if (!rhs.initialized_) // Initialized is not less than not initialized
553  {
554  return false;
555  }
556  else
557  {
558  return resource_ < rhs.resource_;
559  }
560 }
561 
574 template<typename R, typename D>
576 {
577  if (this == &rhs) // This is necessary to avoid deadlock for self-comparison
578  {
579  return true;
580  }
581 
582  // We can't just write:
583  //
584  // return *this < rhs || *this == rhs;
585  //
586  // because that creates a race condition: the locks would be released and
587  // re-aquired in between the two comparisons.
588 
589  std::lock(m_, rhs.m_);
590  AdoptLock left(m_);
591  AdoptLock right(rhs.m_);
592 
593  return resource_ < rhs.resource_ || resource_ == rhs.resource_;
594 }
595 
607 template<typename R, typename D>
608 inline
610 {
611  return !(*this <= rhs);
612 }
613 
626 template<typename R, typename D>
627 inline
629 {
630  return !(*this < rhs);
631 }
632 
633 } // namespace util
634 
635 } // namespace unity
636 
637 // Specializations in namespace std, so we play nicely with STL and metaprogramming.
638 
639 namespace std
640 {
641 
646 template<typename R, typename D>
647 struct equal_to<unity::util::ResourcePtr<R, D>>
648 {
653  {
654  return lhs == rhs;
655  }
656 };
657 
662 template<typename R, typename D>
663 struct not_equal_to<unity::util::ResourcePtr<R, D>>
664 {
669  {
670  return lhs != rhs;
671  }
672 };
673 
678 template<typename R, typename D>
679 struct less<unity::util::ResourcePtr<R, D>>
680 {
685  {
686  return lhs < rhs;
687  }
688 };
689 
694 template<typename R, typename D>
695 struct less_equal<unity::util::ResourcePtr<R, D>>
696 {
701  {
702  return lhs <= rhs;
703  }
704 };
705 
710 template<typename R, typename D>
711 struct greater<unity::util::ResourcePtr<R, D>>
712 {
717  {
718  return lhs > rhs;
719  }
720 };
721 
726 template<typename R, typename D>
727 struct greater_equal<unity::util::ResourcePtr<R, D>>
728 {
733  {
734  return lhs >= rhs;
735  }
736 };
737 
738 // TODO: provide hash if std::hash<R> exists.
739 
740 } // namespace std
741 
742 #endif