@@ -66,25 +66,142 @@ namespace details {
66
66
{
67
67
}
68
68
69
- timer_impl::~timer_impl ()
69
+ class apple_timer
70
70
{
71
- }
71
+ struct callback_finisher
72
+ {
73
+ apple_timer * m_timer;
74
+ callback_finisher (apple_timer *timer) : m_timer(timer)
75
+ {
76
+ m_timer->_in_callback = true ;
77
+ }
78
+ ~callback_finisher ()
79
+ {
80
+ m_timer->_in_callback = false ;
81
+ }
82
+ };
72
83
73
- void timer_impl::start (unsigned int ms, bool repeating, TaskProc_t proc, void * context)
74
- {
75
- if (repeating)
84
+ private:
85
+ bool _repeat;
86
+ dispatch_time_t _nextPopTime;
87
+ TaskProc_t _proc;
88
+ void * _context;
89
+ std::atomic_int _callbacks_pending;
90
+ std::atomic_bool _orphan;
91
+ pplx::extensibility::event_t _completed;
92
+ std::atomic_bool _stop_requested;
93
+ std::atomic_bool _in_callback; // support reentrancy
94
+
95
+ static void timer_callback (void * context)
76
96
{
77
- throw std::logic_error (" PPLX repeating timer is not supported on the Apple platform." );
97
+ apple_timer * ptimer = (apple_timer*)context;
98
+ callback_finisher _ (ptimer);
99
+ ptimer->_callbacks_pending --;
100
+ if (ptimer->_repeat )
101
+ {
102
+ if (ptimer->_stop_requested && ptimer->_callbacks_pending == 0 ) {
103
+ ptimer->finalize_callback ();
104
+ }
105
+ else {
106
+ ptimer->_proc (ptimer->_context );
107
+ ptimer->dispatch_repeated_worker ();
108
+ }
109
+ }
110
+ else
111
+ {
112
+ ptimer->_proc (ptimer->_context );
113
+ ptimer->finalize_callback ();
114
+ }
115
+ }
116
+
117
+ void finalize_callback ()
118
+ {
119
+ if (_orphan)
120
+ {
121
+ delete this ;
122
+ }
123
+ else
124
+ {
125
+ _completed.set ();
126
+ }
78
127
}
79
-
80
- int64_t deltaNanoseconds = ms * NSEC_PER_MSEC;
81
- dispatch_time_t dispatchTime = dispatch_time (DISPATCH_TIME_NOW, deltaNanoseconds);
82
128
83
- dispatch_queue_t queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
84
- dispatch_after_f (dispatchTime, queue, context, proc);
129
+ void dispatch_repeated_worker ()
130
+ {
131
+ _callbacks_pending++;
132
+ dispatch_after_f (_nextPopTime, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), this , timer_callback);
133
+ }
134
+
135
+ void dispatch_repeated (int64_t intervalInNanoSeconds)
136
+ {
137
+ dispatch_time_t firstPopTime = dispatch_time (DISPATCH_TIME_NOW, intervalInNanoSeconds);
138
+
139
+ _nextPopTime = dispatch_time (firstPopTime, intervalInNanoSeconds);
140
+
141
+ dispatch_repeated_worker ();
142
+ }
143
+
144
+ public:
145
+ apple_timer (unsigned int ms, bool repeating, TaskProc_t proc, void * context) :
146
+ _repeat (repeating)
147
+ , _proc(proc)
148
+ , _context(context)
149
+ ,_callbacks_pending(0 )
150
+ ,_orphan(false )
151
+ ,_stop_requested(false )
152
+ ,_in_callback(false )
153
+ {
154
+ int64_t deltaNanoseconds = ms * NSEC_PER_MSEC;
155
+ _completed.reset ();
156
+
157
+ if (repeating)
158
+ {
159
+ dispatch_repeated (deltaNanoseconds);
160
+ }
161
+ else
162
+ {
163
+ dispatch_time_t dispatchTime = dispatch_time (DISPATCH_TIME_NOW, deltaNanoseconds);
164
+ dispatch_queue_t queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
165
+ _callbacks_pending++;
166
+ dispatch_after_f (dispatchTime, queue, this , timer_callback);
167
+ }
168
+ }
169
+
170
+ void stop (bool wait)
171
+ {
172
+ _stop_requested = true ;
173
+ if (_in_callback)
174
+ {
175
+ // this timer will clean up itself when it's done executing the callback
176
+ _orphan = true ;
177
+ }
178
+ else
179
+ {
180
+ if (wait || !_repeat)
181
+ {
182
+ _completed.wait ();
183
+ }
184
+ delete this ;
185
+ }
186
+ }
187
+ };
188
+
189
+ _PPLXIMP void timer_impl::start (unsigned int ms, bool repeating, TaskProc_t proc, void * context)
190
+ {
191
+ _ASSERTE (m_timerImpl == nullptr );
192
+ m_timerImpl = new apple_timer (ms, repeating, proc, context);
193
+ }
194
+
195
+ _PPLXIMP void timer_impl::stop (bool waitForCallbacks)
196
+ {
197
+ if ( m_timerImpl != nullptr )
198
+ {
199
+ m_timerImpl->stop (waitForCallbacks);
200
+ m_timerImpl = nullptr ;
201
+ }
85
202
}
86
203
87
- void timer_impl::stop ( bool /* waitForCallbacks */ )
204
+ timer_impl::~timer_impl ( )
88
205
{
89
206
}
90
207
0 commit comments