PStack  2.0
Stack trace printer for MSVC and GCC binaries
shared_handle.hpp
Go to the documentation of this file.
1 // ===-- include/psystem/framework/shared_handle.hpp ------ -*- C++ -*- --=== //
2 // Copyright (c) 2014 Matt Bisson. All rights reserved.
3 
33 #pragma once
34 #ifndef PSYSTEM_FRAMEWORK_SHARED_HANDLE_HPP
35 #define PSYSTEM_FRAMEWORK_SHARED_HANDLE_HPP
36 
37 #include "platform.hpp"
38 
39 #include <atomic>
40 
41 #include "default_close_handle.hpp"
42 #include "stack_allocated.hpp"
43 #include "unique_handle.hpp" // Needed?
44 
45 namespace psystem {
46 
97 template <typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
99 {
103 private:
111  {
114  : m_usage_count(1),
115  m_weak_count(1)
116  { }
117 
120 
136  virtual void delete_handle(T hndl) noexcept
137  {
139  }
140 
145  std::atomic<size_t> m_usage_count;
146 
153  std::atomic<size_t> m_weak_count;
154  };
155 
162  template <typename D>
164  : public shared_handle_data
165  {
167  using deleter_type = D;
168 
175  noexcept
176  : shared_handle_data(),
177  m_deleter(deleter)
178  { }
179 
186  : shared_handle_data(),
187  m_deleter(std::move(deleter))
188  { }
189 
194  virtual void delete_handle(T hndl) noexcept override
195  {
196  m_deleter(hndl);
197  }
198 
201  };
202 
206 public:
208  using handle_type = T;
209 
212  using safe_address_container =
215 
219 public:
221 #ifndef _MSC_VER
222  static constexpr T invalid_handle_value = kInvalidHandle;
223 #endif
224 
228 public:
239  explicit shared_handle(handle_type hndl = kInvalidHandle) noexcept
240  : m_handle(hndl),
242  (kInvalidHandle != hndl)
243  ? new shared_handle_data
244  : nullptr)
245  { }
246 
270  template <typename D>
271  shared_handle(handle_type hndl, D&& deleter) noexcept
272  : m_handle(hndl),
274  (kInvalidHandle != hndl)
275  ? new shared_handle_data_with_delete<D>(std::forward<D>(deleter))
276  : nullptr)
277  { }
278 
293  : m_handle(other.m_handle),
294  m_control_block(other.m_control_block)
295  {
296  if (kInvalidHandle != m_handle) increment();
297  }
298 
313  : m_handle(other.m_handle),
315  {
316  other.m_handle = kInvalidHandle;
317  other.m_control_block = nullptr;
318  }
319 
344  template <typename D>
346  noexcept
347  : m_handle(other.release()),
349  (kInvalidHandle != m_handle)
350  ? new shared_handle_data_with_delete<D>(
351  std::move(other.get_deleter()))
352  : nullptr)
353  { }
354 
370  explicit shared_handle(
372  T, default_close_handle<T>, kInvalidHandle>&& other) noexcept
373  : m_handle(other.release()),
375  (kInvalidHandle != m_handle)
376  ? new shared_handle_data
377  : nullptr)
378  { }
379 
395  {
396  if (kInvalidHandle != m_handle) decrement();
397  }
398 
402 public:
426  {
427  if (&o == this) return *this;
428 
429  shared_handle tmp = o; // Strong exception guarantee
430 
431  std::swap(tmp.m_handle, m_handle);
432  std::swap(tmp.m_control_block, m_control_block);
433 
434  return *this;
435  }
436 
460  {
461  if (&o == this) return *this;
462 
463  shared_handle tmp = std::move(o); // Strong exception guarantee
464 
465  std::swap(tmp.m_handle, m_handle);
466  std::swap(tmp.m_control_block, m_control_block);
467 
468  return *this;
469  }
470 
502  template <typename D>
505  {
506  if (&o == this) return *this;
507 
508  shared_handle tmp = std::move(o); // Strong exception guarantee
509 
510  std::swap(tmp.m_handle, m_handle);
511  std::swap(tmp.m_control_block, m_control_block);
512 
513  return *this;
514  }
515 
541  psystem::unique_handle<T, default_close_handle<T>, kInvalidHandle>&& o)
542  noexcept
543  {
544  if (&o == this) return *this;
545 
546  shared_handle tmp = std::move(o); // Strong exception guarantee
547 
548  std::swap(tmp.m_handle, m_handle);
549  std::swap(tmp.m_control_block, m_control_block);
550 
551  return *this;
552  }
553 
564  {
565  return safe_address_container(*this);
566  }
567 
579  explicit operator bool() const noexcept
580  {
581  return (kInvalidHandle != m_handle);
582  }
583 
587 public:
595  handle_type get() const noexcept
596  {
597  return m_handle;
598  }
599 
613  void reset(handle_type hndl = kInvalidHandle) noexcept
614  {
615  if (kInvalidHandle != m_handle) decrement();
616  m_handle = hndl;
617  }
618 
650  template <typename D>
651  void reset(handle_type hndl, D&& deleter) noexcept
652  {
653  // Create a temporary copy to see that the parameters are valid before
654  // even touching *this.
655  shared_handle tmp(hndl, std::forward<D>(deleter));
656 
657  // Now that we succeeded, we can alter *this.
658  if (kInvalidHandle != m_handle) decrement();
659  std::swap(tmp.m_handle, m_handle);
660  std::swap(tmp.m_control_block, m_control_block);
661  }
662 
671  {
672  if (&o == this) return;
673 
674  std::swap(o.m_handle, m_handle);
675  std::swap(o.m_control_block, m_control_block);
676  }
677 
684  bool unique() const noexcept
685  {
686  return ((m_control_block) && (m_control_block->m_usage_count == 1));
687  }
688 
694  size_t use_count() const noexcept
695  {
697  }
698 
702 private:
712  {
714 
715  auto const usage_count =
716  m_control_block->m_usage_count.fetch_sub(
717  1, std::memory_order_relaxed);
718  ASSERT(0 < usage_count);
719 
720  if (1 == usage_count)
721  {
723  m_handle = kInvalidHandle;
724 
725  auto const weak_count =
726  m_control_block->m_weak_count.fetch_sub(1);
727  ASSERT(0 < weak_count);
728 
729  if (1 == weak_count)
730  {
731  delete m_control_block;
732  m_control_block = nullptr;
733  }
734  }
735  }
736 
739  {
741 
742  // Use relaxed memory ordering for incrementing because nothing really
743  // destructive happens as a result of incrementing the ref count...
744  auto const usage_count = m_control_block->m_usage_count.fetch_add(
745  1, std::memory_order_relaxed);
746 
747  // We just blew through the max number of shared_handles!
748  ASSERT_USING_VAR(usage_count, SIZE_MAX > usage_count);
749  }
750 
754 private:
757 
773  shared_handle_data *m_control_block;
774 };
775 
776 } // namespace psystem
777 
778 // -----------------------------------------------------------------------------
779 // Template specializations injected into the std namespace
780 // -----------------------------------------------------------------------------
781 
782 namespace std {
783 
796 template <typename T, T kInvalidHandle>
797 void
798 swap(
801  noexcept
802 {
803  o1.swap(o2);
804 }
805 
815 template <typename T, T kInvalidHandle>
816 struct hash< psystem::shared_handle<T, kInvalidHandle> >
817 {
820 
824  size_t operator()(shared_handle_type const& value) const
825  {
826  return std::hash<typename shared_handle_type::handle_type>()(
827  value.get());
828  }
829 };
830 
831 } // namespace std
832 
833 // -----------------------------------------------------------------------------
834 // Free functions related to shared_handle
835 // -----------------------------------------------------------------------------
836 
850 template <typename T, T kInvalidHandle>
851 inline bool operator==(
854 {
855  return (h1.get() == h2.get());
856 }
857 
873 template <typename T, T kInvalidHandle>
874 inline bool operator!=(
877 {
878  return !(h1 == h2);
879 }
880 
896 template <typename T, T kInvalidHandle>
897 inline bool operator<(
900 {
901  return std::less<
903  h1.get(), h2.get());
904 }
905 
921 template <typename T, T kInvalidHandle>
922 inline bool operator<=(
925 {
926  return !(h2 < h1);
927 }
928 
944 template <typename T, T kInvalidHandle>
945 inline bool operator>(
948 {
949  return (h2 < h1);
950 }
951 
967 template <typename T, T kInvalidHandle>
968 inline bool operator>=(
971 {
972  return !(h1 < h2);
973 }
974 
975 #endif // PSYSTEM_FRAMEWORK_SHARED_HANDLE_HPP
handle_type get() const noexcept
Access the handle contained in this object.
Definition: shared_handle.hpp:595
A simple functor to close handles (for use with RAII handle containers).
Definition: default_close_handle.hpp:49
shared_handle(shared_handle &&other)
Transfer ownership of a handle from another shared_handle.
Definition: shared_handle.hpp:312
Master header file for Platform-wide declarations.
shared_handle & operator=(shared_handle const &o) noexcept
Share ownership of a handle managed by a shared_handle instance.
Definition: shared_handle.hpp:425
Uniquely manage a single "handle".
Definition: unique_handle.hpp:249
psystem::internal::managed_handle_proxy< handle_type, shared_handle< handle_type, kInvalidHandle > > safe_address_container
A safe container for altering the handle by the address-of operator.
Definition: shared_handle.hpp:214
handle_type m_handle
The handle to manage within this class.
Definition: shared_handle.hpp:756
void reset(handle_type hndl=kInvalidHandle) noexcept
Assign a new handle to be managed by this instance.
Definition: shared_handle.hpp:613
shared_handle(shared_handle const &other) noexcept
Share ownership of a handle with another shared_handle.
Definition: shared_handle.hpp:292
void swap(shared_handle &o) noexcept
Exchange ownership of two shared_handle instances.
Definition: shared_handle.hpp:670
shared_handle(handle_type hndl, D &&deleter) noexcept
Construct an instance to manage a handle with a custom "deleter.".
Definition: shared_handle.hpp:271
~shared_handle() noexcept
Release ownership of the handle by destroying this instance.
Definition: shared_handle.hpp:394
deleter_type m_deleter
A user-provided handle deletion utility.
Definition: shared_handle.hpp:200
virtual void delete_handle(T hndl) noexcept
Remove resources associated with a handle.
Definition: shared_handle.hpp:136
void increment() noexcept
Increment the managed handle's reference count.
Definition: shared_handle.hpp:738
#define ASSERT_USING_VAR(var, cond)
Abort the process if the condition is not satisfied, declaring the a variable is used by the assertio...
Definition: platform.hpp:147
Definition: shared_handle.hpp:782
shared_handle & operator=(psystem::unique_handle< T, D, kInvalidHandle > &&o) noexcept
Move an unique_handle instance into a shared_handle.
Definition: shared_handle.hpp:503
Disable heap allocation and deallocation.
Definition: stack_allocated.hpp:42
#define noexcept
Replace keyword with something useful.
Definition: platform.hpp:71
shared_handle(psystem::unique_handle< T, D, kInvalidHandle > &&other) noexcept
Transfer ownership of a handle from an unique_handle.
Definition: shared_handle.hpp:345
shared_handle_data * m_control_block
The shared "control block" for all instances managing the handle.
Definition: shared_handle.hpp:773
shared_handle & operator=(psystem::unique_handle< T, default_close_handle< T >, kInvalidHandle > &&o) noexcept
Transfer ownership of a handle from a default unique_handle type.
Definition: shared_handle.hpp:540
Allows safe modification of a managed handle container when accessed by the handle's address...
Definition: managed_handle_proxy.hpp:51
#define ASSERT(cond)
Abort the process if the condition is not satisfied.
Definition: platform.hpp:131
bool unique() const noexcept
Determines if this is the only instance managing the current handle.
Definition: shared_handle.hpp:684
HMODULE handle_type
The data-type of the handle being managed.
Definition: shared_handle.hpp:208
Declare an RAII container for system API handles (psystem::unique_handle).
std::atomic< size_t > m_usage_count
The count of shared_handle instances that are managing this handle.
Definition: shared_handle.hpp:145
std::atomic< size_t > m_weak_count
The "weak" reference count (manages the control block).
Definition: shared_handle.hpp:153
void reset(handle_type hndl, D &&deleter) noexcept
Assign a new handle to be managed by this instance with a custom deleter.
Definition: shared_handle.hpp:651
D deleter_type
The facility used to clean handle resources.
Definition: shared_handle.hpp:167
A control block for a shared_handle with a user-specified deleter.
Definition: shared_handle.hpp:163
bool operator<=(psystem::shared_handle< T, kInvalidHandle > const &h1, psystem::shared_handle< T, kInvalidHandle > const &h2) noexcept
Test if the first handle is less than or equal to the second.
Definition: shared_handle.hpp:922
shared_handle & operator=(shared_handle &&o) noexcept
Transfer ownership of a handle into this insance.
Definition: shared_handle.hpp:459
Introduces a functor to wrap CloseHandle() for RAII handle containers.
static void close_handle(T hndl) noexcept
Executes a handle clean-up routine without requiring an instance.
Definition: default_close_handle.hpp:64
size_t use_count() const noexcept
The count of shared_handle instances managing this handle.
Definition: shared_handle.hpp:694
Share management of a single "handle" between multiple owners.
Definition: shared_handle.hpp:98
shared_handle_data_with_delete(deleter_type &&deleter) noexcept
Construct this instance by taking ownership of a deleter.
Definition: shared_handle.hpp:185
Contains the process examination "system" and basic frameworks.
Definition: pstack_event_handler.hpp:28
size_t operator()(shared_handle_type const &value) const
Invoke the hash functor.
Definition: shared_handle.hpp:824
#define constexpr
Replace keyword with something useful.
Definition: platform.hpp:70
bool operator>=(psystem::shared_handle< T, kInvalidHandle > const &h1, psystem::shared_handle< T, kInvalidHandle > const &h2) noexcept
Test if the first handle is greater than or equal to the second.
Definition: shared_handle.hpp:968
virtual void delete_handle(T hndl) noexcept override
Invoke the provided deleter to clean up handle resources.
Definition: shared_handle.hpp:194
safe_address_container operator&() noexcept
Allow safe alteration of the handle by routines that return data by C-style output parameters...
Definition: shared_handle.hpp:563
virtual ~shared_handle_data() noexcept
Provide a virtual destructor for polymorphic deletion.
Definition: shared_handle.hpp:119
bool operator==(psystem::shared_handle< T, kInvalidHandle > const &h1, psystem::shared_handle< T, kInvalidHandle > const &h2) noexcept
Test for equality of two handles.
Definition: shared_handle.hpp:851
shared_handle_data() noexcept
Initialize a usage count of 1 and a weak-usage count of 1.
Definition: shared_handle.hpp:113
bool operator>(psystem::shared_handle< T, kInvalidHandle > const &h1, psystem::shared_handle< T, kInvalidHandle > const &h2) noexcept
Test if the first handle is greater than the second.
Definition: shared_handle.hpp:945
shared_handle(psystem::unique_handle< T, default_close_handle< T >, kInvalidHandle > &&other) noexcept
Transfer ownership of a handle from a default unique_handle type.
Definition: shared_handle.hpp:370
Defines the psystem::stack_allocated interface.
bool operator<(psystem::shared_handle< T, kInvalidHandle > const &h1, psystem::shared_handle< T, kInvalidHandle > const &h2) noexcept
Test if the first handle is less than the second.
Definition: shared_handle.hpp:897
The basic control block for a shared_handle.
Definition: shared_handle.hpp:110
bool operator!=(psystem::shared_handle< T, kInvalidHandle > const &h1, psystem::shared_handle< T, kInvalidHandle > const &h2) noexcept
Test for inequality of two handles.
Definition: shared_handle.hpp:874
void decrement() noexcept
Decrement the managed handle's reference count.
Definition: shared_handle.hpp:711
shared_handle(handle_type hndl=kInvalidHandle) noexcept
Construct an instance to manage a handle.
Definition: shared_handle.hpp:239
shared_handle_data_with_delete(deleter_type const &deleter) noexcept
Construct this instance with a custom deleter.
Definition: shared_handle.hpp:174