@@ -50,7 +50,7 @@ BoostVM::BoostVM(BoostEnvironment& environment,
50
50
uuidGenerator (),
51
51
portClosed (false ),
52
52
_asyncIONodeCount (0 ),
53
- preemptionTimer (environment.io_service ),
53
+ preemptionTimer (nullptr ),
54
54
alarmTimer (environment.io_service),
55
55
_terminationRequested (false ),
56
56
_terminationStatus (0 ),
@@ -103,23 +103,31 @@ void BoostVM::run() {
103
103
constexpr auto recInvokeAgainNow = VirtualMachine::recInvokeAgainNow;
104
104
constexpr auto recInvokeAgainLater = VirtualMachine::recInvokeAgainLater;
105
105
106
+ env.io_service .post ([&] {
107
+ preemptionTimer = new boost::asio::deadline_timer (env.io_service );
108
+ });
109
+
106
110
// The main loop that handles all interactions with the VM
107
111
while (true ) {
108
112
// Make sure the VM knows the reference time before starting
109
113
vm->setReferenceTime (env.getReferenceTime ());
110
114
111
115
// Setup the preemption timer
112
- preemptionTimer.expires_from_now (boost::posix_time::millisec (1 ));
113
- preemptionTimer.async_wait (boost::bind (
114
- &BoostVM::onPreemptionTimerExpire,
115
- this , boost::asio::placeholders::error));
116
+ env.io_service .post ([&](){
117
+ preemptionTimer->expires_from_now (boost::posix_time::millisec (1 ));
118
+ preemptionTimer->async_wait (boost::bind (
119
+ &BoostVM::onPreemptionTimerExpire,
120
+ this , boost::asio::placeholders::error));
121
+ });
116
122
117
123
// Run the VM
118
124
auto nextInvokePair = vm->run ();
119
125
auto nextInvoke = nextInvokePair.first ;
120
126
121
127
// Stop the preemption timer
122
- preemptionTimer.cancel ();
128
+ env.io_service .post ([&](){
129
+ preemptionTimer->expires_at (boost::posix_time::min_date_time);
130
+ });
123
131
124
132
{
125
133
// Acquire the lock that grants me access to
@@ -170,18 +178,23 @@ void BoostVM::run() {
170
178
171
179
// Called by the *IO thread*
172
180
void BoostVM::onPreemptionTimerExpire (const boost::system::error_code& error) {
173
- if (error != boost::asio::error::operation_aborted &&
174
- !_terminationRequested) {
181
+ if (error == boost::asio::error::operation_aborted) {
182
+ // Timer was cancelled
183
+ } else if (_terminationRequested) {
184
+ // Termination was requested
185
+ } else if (preemptionTimer->expires_at () == boost::posix_time::min_date_time) {
186
+ // Timer was cancelled, but we missed it (race condition in io_service)
187
+ } else {
175
188
// Preemption
176
189
vm->setReferenceTime (env.getReferenceTime ());
177
190
vm->requestPreempt ();
178
191
179
192
// Reschedule
180
- preemptionTimer. expires_at (
181
- preemptionTimer. expires_at () + boost::posix_time::millisec (1 ));
182
- preemptionTimer. async_wait (boost::bind (
183
- &BoostVM::onPreemptionTimerExpire,
184
- this , boost::asio::placeholders::error));
193
+ preemptionTimer-> expires_at (
194
+ preemptionTimer-> expires_at () + boost::posix_time::millisec (1 ));
195
+ preemptionTimer-> async_wait (boost::bind (
196
+ &BoostVM::onPreemptionTimerExpire,
197
+ this , boost::asio::placeholders::error));
185
198
}
186
199
}
187
200
@@ -320,7 +333,20 @@ void BoostVM::terminate() {
320
333
// not interact with the VM as we might have quitted run() brutally.
321
334
322
335
// Ensure the timers are stopped
323
- preemptionTimer.cancel ();
336
+ auto & preemptionTimerCopy = preemptionTimer;
337
+ // We need a copy of preemptionTimer because we cannot capture 'this'.
338
+ // It may be deleted before the execution of the callback.
339
+ // For the same reason, we access the io_service via the timer.
340
+ env.io_service .post ([preemptionTimerCopy]{
341
+ preemptionTimerCopy->expires_at (boost::posix_time::min_date_time);
342
+ // We cannot delete the timer now because the onPreemptionTimerExpire handler
343
+ // may already be in the queue. So add a delete lambda to the queue.
344
+ // The lambda will execute after any leftover handlers on the timer and
345
+ // with the timer stopped.
346
+ preemptionTimerCopy->get_io_service ().post ([preemptionTimerCopy] {
347
+ delete preemptionTimerCopy;
348
+ });
349
+ });
324
350
alarmTimer.cancel ();
325
351
326
352
portClosed = true ; // close VM port
0 commit comments