// potentially long-running callback is executing. |alarm_cancel| uses this
// mutex to provide a guarantee to its caller that the callback will not be
// in progress when it returns.
- std::recursive_mutex* callback_mutex;
+ std::shared_ptr<std::recursive_mutex> callback_mutex;
uint64_t creation_time_ms;
uint64_t period_ms;
uint64_t deadline_ms;
alarm_t* ret = static_cast<alarm_t*>(osi_calloc(sizeof(alarm_t)));
- ret->callback_mutex = new std::recursive_mutex;
+ std::shared_ptr<std::recursive_mutex> ptr(new std::recursive_mutex());
+ ret->callback_mutex = ptr;
ret->is_periodic = is_periodic;
ret->stats.name = osi_strdup(name);
if (!alarm) return;
alarm_cancel(alarm);
- delete alarm->callback_mutex;
+
osi_free((void*)alarm->stats.name);
alarm->closure.~CancelableClosureInStruct();
osi_free(alarm);
CHECK(alarms != NULL);
if (!alarm) return;
+ std::shared_ptr<std::recursive_mutex> local_mutex_ref;
{
std::lock_guard<std::mutex> lock(alarms_mutex);
+ local_mutex_ref = alarm->callback_mutex;
alarm_cancel_internal(alarm);
}
// If the callback for |alarm| is in progress, wait here until it completes.
- std::lock_guard<std::recursive_mutex> lock(*alarm->callback_mutex);
+ std::lock_guard<std::recursive_mutex> lock(*local_mutex_ref);
}
// Internal implementation of canceling an alarm.
alarm->deadline_ms = 0;
alarm->prev_deadline_ms = 0;
alarm->callback = NULL;
+ alarm->callback_mutex.reset();
alarm->data = NULL;
alarm->stats.canceled_count++;
alarm->queue = NULL;
alarm->queue = NULL;
}
- std::lock_guard<std::recursive_mutex> cb_lock(*alarm->callback_mutex);
+ // Increment the reference count of the mutex so it doesn't get freed
+ // before the callback gets finished executing.
+ std::shared_ptr<std::recursive_mutex> local_mutex_ref = alarm->callback_mutex;
+ std::lock_guard<std::recursive_mutex> cb_lock(*local_mutex_ref);
lock.unlock();
// Update the statistics
}
alarm_cleanup();
}
+
+static void remove_cb(void* data) {
+ alarm_free((alarm_t*)data);
+ semaphore_post(semaphore);
+}
+
+TEST_F(AlarmTest, test_delete_during_callback) {
+ for (int i = 0; i < 1000; ++i) {
+ alarm_t* alarm = alarm_new("alarm_test.test_delete_during_callback");
+ alarm_set(alarm, 0, remove_cb, alarm);
+ semaphore_wait(semaphore);
+ }
+ alarm_cleanup();
+}