summaryrefslogtreecommitdiff
path: root/pthread_stop_world.c
diff options
context:
space:
mode:
authorScott Ferguson <scott.ferguson@unity3d.com>2020-06-22 08:22:57 -0400
committerIvan Maidanski <ivmai@mail.ru>2021-07-28 23:37:00 +0300
commitce05033c1dc3101b5b5d5faf6e9287cec1c36210 (patch)
treefec3ebdc8bc9ee66094f85bb8ac4a0c367f03ebb /pthread_stop_world.c
parentb104cdc50404d1f18905cc8f07add407241dfc23 (diff)
downloadbdwgc-ce05033c1dc3101b5b5d5faf6e9287cec1c36210.tar.gz
Avoid initial 3ms pause on world stop/start with GC_retry_signals (Linux)
(a cherry-pick of commit 09d56ab44 from Unity-Technologies/bdwgc) The existing GC_retry_signals case would almost always introduce a 3ms pause when stopping and starting the world. Instead only fallback to that retry case if the threads haven't responded after a timeout. * include/private/gc_priv.h [!NO_CLOCK && (NINTENDO_SWITCH || ((LINUX && __USE_POSIX199309 || CYGWIN32) && _POSIX_TIMERS)] (HAVE_CLOCK_GETTIME): Define. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL] (WAIT_UNIT, RETRY_INTERVAL): Add comment. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL && HAVE_CLOCK_GETTIME] (TS_NSEC_ADD): Define. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL] (resend_lost_signals_retry): New static function. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL] (GC_stop_world, GC_start_world): Call resend_lost_signals_retry() instead of resend_lost_signals() and suspend_restart_barrier() (if GC_retry_signals).
Diffstat (limited to 'pthread_stop_world.c')
-rw-r--r--pthread_stop_world.c57
1 files changed, 45 insertions, 12 deletions
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
index 420ae97d..4c14cd42 100644
--- a/pthread_stop_world.c
+++ b/pthread_stop_world.c
@@ -434,8 +434,8 @@ static void suspend_restart_barrier(int n_live_threads)
static int resend_lost_signals(int n_live_threads,
int (*suspend_restart_all)(void))
{
-# define WAIT_UNIT 3000
-# define RETRY_INTERVAL 100000
+# define WAIT_UNIT 3000 /* us */
+# define RETRY_INTERVAL 100000 /* us */
if (n_live_threads > 0) {
unsigned long wait_usecs = 0; /* Total wait since retry. */
@@ -479,6 +479,37 @@ static int resend_lost_signals(int n_live_threads,
return n_live_threads;
}
+#ifdef HAVE_CLOCK_GETTIME
+# define TS_NSEC_ADD(ts, ns) \
+ (ts.tv_nsec += (ns), \
+ (void)(ts.tv_nsec >= 1000000L*1000 ? \
+ (ts.tv_nsec -= 1000000L*1000, ts.tv_sec++, 0) : 0))
+#endif
+
+static void resend_lost_signals_retry(int n_live_threads,
+ int (*suspend_restart_all)(void))
+{
+# if defined(HAVE_CLOCK_GETTIME) && !defined(DONT_TIMEDWAIT_ACK_SEM)
+# define TIMEOUT_BEFORE_RESEND 10000 /* us */
+ int i;
+ struct timespec ts;
+
+ if (n_live_threads > 0 && clock_gettime(CLOCK_REALTIME, &ts) == 0) {
+ TS_NSEC_ADD(ts, TIMEOUT_BEFORE_RESEND * 1000);
+ /* First, try to wait for the semaphore with some timeout. */
+ /* On failure, fallback to WAIT_UNIT pause and resend of the signal. */
+ for (i = 0; i < n_live_threads; i++) {
+ if (0 != sem_timedwait(&GC_suspend_ack_sem, &ts))
+ break; /* Wait timed out or any other error. */
+ }
+ /* Update the count of threads to wait the ack from. */
+ n_live_threads -= i;
+ }
+# endif
+ n_live_threads = resend_lost_signals(n_live_threads, suspend_restart_all);
+ suspend_restart_barrier(n_live_threads);
+}
+
STATIC void GC_restart_handler(int sig)
{
# if defined(DEBUG_THREADS)
@@ -931,9 +962,11 @@ GC_INNER void GC_stop_world(void)
}
AO_store_release(&GC_world_is_stopped, TRUE);
n_live_threads = GC_suspend_all();
- if (GC_retry_signals)
- n_live_threads = resend_lost_signals(n_live_threads, GC_suspend_all);
- suspend_restart_barrier(n_live_threads);
+ if (GC_retry_signals) {
+ resend_lost_signals_retry(n_live_threads, GC_suspend_all);
+ } else {
+ suspend_restart_barrier(n_live_threads);
+ }
if (GC_manual_vdb)
GC_release_dirty_lock(); /* cannot be done in GC_suspend_all */
# endif
@@ -1182,15 +1215,15 @@ GC_INNER void GC_start_world(void)
n_live_threads = GC_restart_all();
# ifdef GC_OPENBSD_UTHREADS
(void)n_live_threads;
-# elif defined(GC_NETBSD_THREADS_WORKAROUND)
- if (GC_retry_signals)
- n_live_threads = resend_lost_signals(n_live_threads, GC_restart_all);
- suspend_restart_barrier(n_live_threads);
# else
if (GC_retry_signals) {
- n_live_threads = resend_lost_signals(n_live_threads, GC_restart_all);
- suspend_restart_barrier(n_live_threads);
- }
+ resend_lost_signals_retry(n_live_threads, GC_restart_all);
+ } /* else */
+# ifdef GC_NETBSD_THREADS_WORKAROUND
+ else {
+ suspend_restart_barrier(n_live_threads);
+ }
+# endif
# endif
# ifdef DEBUG_THREADS
GC_log_printf("World started\n");