Intel® RealSense™ Cross Platform API
Intel Realsense Cross-platform API
Loading...
Searching...
No Matches
waiting-on.h
Go to the documentation of this file.
1// License: Apache 2.0. See LICENSE file in root directory.
2// Copyright(c) 2021 Intel Corporation. All Rights Reserved.
3
4#pragma once
5
6#include <mutex>
7#include <condition_variable>
8#include <thread>
9#include <atomic>
10
11
12namespace utilities {
13namespace time {
14
15// Helper class -- encapsulate a variable of type T that we want to wait on: another thread will set
16// it and signal when we can continue...
17//
18// In order to synchronize the users predicate, we expect the user to provide his conditional variable and mutex used to set the predicate.
19// As mentioned at the conditional variable documentation: (https://en.cppreference.com/w/cpp/thread/condition_variable)
20// The thread that intends to modify the shared variable has to
21// 1. acquire a std::mutex(typically via std::lock_guard)
22// 2. perform the modification while the lock is held
23// 3. execute notify_one or notify_all on the std::condition_variable(the lock does not need to be held for notification)
24// For more detailed information see https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables
25template< class T >
27{
28 // We need to be careful with timeouts: if we time out then the local waiting_on can go out of
29 // scope and then the thread cannot set anything or signal anyone! We get around this by using a
30 // shared_ptr & weak_ptr to manage access:
31 //
32public:
34 {
35 T _value;
36 std::condition_variable &_cv;
37 std::mutex &_m;
38 std::atomic_bool _valid{ true };
39 friend class waiting_on;
40
41 public:
42 wait_state_t() = delete; // Do not allow default Ctor, we need the user's CV and Mutex
43 wait_state_t( std::condition_variable & cv, std::mutex & m )
44 : _cv( cv )
45 , _m( m )
46 {
47 }
48
49 wait_state_t( std::condition_variable &cv, std::mutex &m, T const & t )
50 : _cv( cv )
51 , _m( m )
52 , _value( t )
53 {
54 }
55
56 operator T &() { return _value; }
57 operator T const &() const { return _value; }
58
59 T* operator->() { return &_value; }
60 T const* operator->() const { return &_value; }
61
62 // Set a new value and signal
63 void signal( T const & t )
64 {
65 std::unique_lock<std::mutex> locker(_m);
66 _value = t;
67 locker.unlock();
68 signal();
69 }
70 // Signal with the current value
71 void signal()
72 {
73 _cv.notify_one();
74 }
75 // Invalidate the wait_state_t so the user will not use destroyed objects
77 {
78 if ( _valid )
79 {
80 _valid = false;
81 _cv.notify_all();
82 }
83 }
84 };
85private:
86 std::shared_ptr< wait_state_t > _ptr;
87
88 // When we declare the signalling lambda for the other thread, we need to pass it the weak_ptr.
89 // This class wraps it up nicely, so you can write:
90 // waiting_on< bool > invoked( false )
91 // auto thread_function = [invoked = invoked.in_thread()]() {
92 // invoked.signal( true );
93 // }
94 //
95public:
97 {
98 std::weak_ptr< wait_state_t > const _ptr;
99 // We use an invalidator for invalidating the class when reference count is equal to Zero.
100 std::shared_ptr< std::nullptr_t > const _invalidator;
101
102 public:
103 in_thread_( waiting_on const & local )
104 : _ptr( local._ptr )
105 , _invalidator(
106 nullptr,
107 [weak_ptr = std::weak_ptr< wait_state_t >( local._ptr )]( std::nullptr_t * ) {
108 // We get here when the lambda we're in is destroyed -- so either we've
109 // already run (and signalled once) or we've never run. We signal anyway
110 // if anything's waiting they'll get woken up; otherwise nothing'll happen...
111 if( auto wait_state = weak_ptr.lock() )
112 wait_state->invalidate();
113 } )
114 {
115 }
116
117 std::shared_ptr< wait_state_t > still_alive() const { return _ptr.lock(); }
118
119 // Wake up the local function (which is using wait_until(), presumable) with a new
120 // T value
121 void signal( T const& t ) const
122 {
123 if( auto wait_state = still_alive() )
124 wait_state->signal( t );
125 }
126 };
127
128public:
129 waiting_on( std::condition_variable & cv, std::mutex & m )
130 : _ptr( std::make_shared< wait_state_t >( cv, m ) )
131 {
132 }
133 waiting_on( std::condition_variable & cv, std::mutex & m, T const & value )
134 : _ptr( std::make_shared< wait_state_t >( cv, m, value ) )
135 {
136 }
137
138 // Convert to the in-thread representation
139 in_thread_ in_thread() const { return in_thread_( *this ); }
140
141 operator T const &() const { return *_ptr; }
142
143 // struct value_t { double x; int k; };
144 // waiting_on< value_t > output({ 1., -1 });
145 // output->x = 2.;
146 T * operator->() { return &_ptr->_value; }
147 T const * operator->() const { return &_ptr->_value; }
148
149 // Wait until either the timeout occurs, or the predicate evaluates to true.
150 // Equivalent to:
151 // while( ! pred() )
152 // {
153 // wait( timeout );
154 // if( timed-out )
155 // break;
156 // }
157 template < class U, class L >
158 void wait_until( U const& timeout, L const& pred )
159 {
160 // The CV must use the same lock that locks the predicate assignment value, otherwise it
161 // could miss the signal which is a known trap of conditional variables
162 std::unique_lock< std::mutex > locker(_ptr->_m);
163 _ptr->_cv.wait_for( locker, timeout, [&]() -> bool {
164 if( ! _ptr->_valid )
165 return true;
166 return pred();
167 } );
168 }
169};
170
171
172} // namespace time
173} // namespace utilities
Definition: waiting-on.h:97
std::shared_ptr< wait_state_t > still_alive() const
Definition: waiting-on.h:117
in_thread_(waiting_on const &local)
Definition: waiting-on.h:103
void signal(T const &t) const
Definition: waiting-on.h:121
void signal(T const &t)
Definition: waiting-on.h:63
wait_state_t(std::condition_variable &cv, std::mutex &m, T const &t)
Definition: waiting-on.h:49
T const * operator->() const
Definition: waiting-on.h:60
void invalidate()
Definition: waiting-on.h:76
wait_state_t(std::condition_variable &cv, std::mutex &m)
Definition: waiting-on.h:43
T * operator->()
Definition: waiting-on.h:59
void signal()
Definition: waiting-on.h:71
Definition: waiting-on.h:27
void wait_until(U const &timeout, L const &pred)
Definition: waiting-on.h:158
T const * operator->() const
Definition: waiting-on.h:147
waiting_on(std::condition_variable &cv, std::mutex &m)
Definition: waiting-on.h:129
waiting_on(std::condition_variable &cv, std::mutex &m, T const &value)
Definition: waiting-on.h:133
in_thread_ in_thread() const
Definition: waiting-on.h:139
T * operator->()
Definition: waiting-on.h:146
Definition: stabilized-value.h:12