mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
fix concurrency issues in mono_time
Since mono_time is accessed from the main thread as well as the toxav thread it is needed to properly lock time updates.
This commit is contained in:
parent
3d21e66a8a
commit
571897cecb
|
@ -63,7 +63,10 @@ cc_library(
|
||||||
name = "mono_time",
|
name = "mono_time",
|
||||||
srcs = ["mono_time.c"],
|
srcs = ["mono_time.c"],
|
||||||
hdrs = ["mono_time.h"],
|
hdrs = ["mono_time.h"],
|
||||||
deps = [":ccompat"],
|
deps = [
|
||||||
|
":ccompat",
|
||||||
|
"@pthread",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "mono_time.h"
|
#include "mono_time.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
@ -29,32 +30,44 @@ struct Mono_Time {
|
||||||
uint64_t time;
|
uint64_t time;
|
||||||
uint64_t base_time;
|
uint64_t base_time;
|
||||||
#ifdef OS_WIN32
|
#ifdef OS_WIN32
|
||||||
uint64_t last_clock_mono;
|
uint32_t last_clock_mono;
|
||||||
uint64_t add_clock_mono;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* protect `time` and `last_clock_mono` from concurrent access */
|
||||||
|
pthread_rwlock_t *time_update_lock;
|
||||||
|
|
||||||
mono_time_current_time_cb *current_time_callback;
|
mono_time_current_time_cb *current_time_callback;
|
||||||
void *user_data;
|
void *user_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_data)
|
static uint64_t current_time_monotonic_default(Mono_Time *mono_time, void *user_data)
|
||||||
{
|
{
|
||||||
uint64_t time;
|
uint64_t time = 0;
|
||||||
#ifdef OS_WIN32
|
#ifdef OS_WIN32
|
||||||
uint64_t old_add_clock_mono = mono_time->add_clock_mono;
|
/* let only one thread at a time check for overflow */
|
||||||
time = (uint64_t)GetTickCount() + mono_time->add_clock_mono;
|
pthread_rwlock_wrlock(mono_time->time_update_lock);
|
||||||
|
|
||||||
/* Check if time has decreased because of 32 bit wrap from GetTickCount(), while avoiding false positives from race
|
/* GetTickCount provides only a 32 bit counter, but we can't use
|
||||||
* conditions when multiple threads call this function at once */
|
* GetTickCount64 for backwards compatibility, so we handle wraparound
|
||||||
if (time + 0x10000 < mono_time->last_clock_mono) {
|
* ourselfes.
|
||||||
uint32_t add = ~0;
|
*/
|
||||||
/* use old_add_clock_mono rather than simply incrementing add_clock_mono, to handle the case that many threads
|
uint32_t ticks = GetTickCount();
|
||||||
* simultaneously detect an overflow */
|
|
||||||
mono_time->add_clock_mono = old_add_clock_mono + add;
|
/* the higher 32 bits count the amount of overflows */
|
||||||
time += add;
|
uint64_t old_ovf = mono_time->time & ~((uint64_t)UINT32_MAX);
|
||||||
|
|
||||||
|
/* Check if time has decreased because of 32 bit wrap from GetTickCount() */
|
||||||
|
if (ticks < mono_time->last_clock_mono) {
|
||||||
|
/* account for overflow */
|
||||||
|
old_ovf += UINT32_MAX + UINT64_C(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
mono_time->last_clock_mono = time;
|
mono_time->last_clock_mono = ticks;
|
||||||
|
|
||||||
|
/* splice the low and high bits back together */
|
||||||
|
time = old_ovf + ticks;
|
||||||
|
|
||||||
|
pthread_rwlock_unlock(mono_time->time_update_lock);
|
||||||
#else
|
#else
|
||||||
struct timespec clock_mono;
|
struct timespec clock_mono;
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
|
@ -83,17 +96,30 @@ Mono_Time *mono_time_new(void)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mono_time->time_update_lock = (pthread_rwlock_t *)malloc(sizeof(pthread_rwlock_t));
|
||||||
|
|
||||||
|
if (mono_time->time_update_lock == nullptr) {
|
||||||
|
free(mono_time);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_rwlock_init(mono_time->time_update_lock, nullptr) < 0) {
|
||||||
|
free(mono_time->time_update_lock);
|
||||||
|
free(mono_time);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
mono_time->current_time_callback = current_time_monotonic_default;
|
mono_time->current_time_callback = current_time_monotonic_default;
|
||||||
mono_time->user_data = nullptr;
|
mono_time->user_data = nullptr;
|
||||||
|
|
||||||
#ifdef OS_WIN32
|
#ifdef OS_WIN32
|
||||||
mono_time->last_clock_mono = 0;
|
mono_time->last_clock_mono = 0;
|
||||||
mono_time->add_clock_mono = 0;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mono_time->time = 0;
|
mono_time->time = 0;
|
||||||
mono_time->base_time = (uint64_t)time(nullptr) - (current_time_monotonic(mono_time) / 1000ULL);
|
mono_time->base_time = (uint64_t)time(nullptr) - (current_time_monotonic(mono_time) / 1000ULL);
|
||||||
|
|
||||||
|
|
||||||
mono_time_update(mono_time);
|
mono_time_update(mono_time);
|
||||||
|
|
||||||
return mono_time;
|
return mono_time;
|
||||||
|
@ -101,17 +127,26 @@ Mono_Time *mono_time_new(void)
|
||||||
|
|
||||||
void mono_time_free(Mono_Time *mono_time)
|
void mono_time_free(Mono_Time *mono_time)
|
||||||
{
|
{
|
||||||
|
pthread_rwlock_destroy(mono_time->time_update_lock);
|
||||||
|
free(mono_time->time_update_lock);
|
||||||
free(mono_time);
|
free(mono_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mono_time_update(Mono_Time *mono_time)
|
void mono_time_update(Mono_Time *mono_time)
|
||||||
{
|
{
|
||||||
mono_time->time = (current_time_monotonic(mono_time) / 1000ULL) + mono_time->base_time;
|
uint64_t time = (current_time_monotonic(mono_time) / 1000ULL) + mono_time->base_time;
|
||||||
|
pthread_rwlock_wrlock(mono_time->time_update_lock);
|
||||||
|
mono_time->time = time;
|
||||||
|
pthread_rwlock_unlock(mono_time->time_update_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t mono_time_get(const Mono_Time *mono_time)
|
uint64_t mono_time_get(const Mono_Time *mono_time)
|
||||||
{
|
{
|
||||||
return mono_time->time;
|
uint64_t time = 0;
|
||||||
|
pthread_rwlock_rdlock(mono_time->time_update_lock);
|
||||||
|
time = mono_time->time;
|
||||||
|
pthread_rwlock_unlock(mono_time->time_update_lock);
|
||||||
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mono_time_is_timeout(const Mono_Time *mono_time, uint64_t timestamp, uint64_t timeout)
|
bool mono_time_is_timeout(const Mono_Time *mono_time, uint64_t timestamp, uint64_t timeout)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user