Merge branch 'thread-pool-slow-really-stops-unused' into 'main'

glib/test/thread-pool-slow: Ensure all unused threads are really stopped

Closes #2685

See merge request GNOME/glib!2804
This commit is contained in:
Philip Withnall 2022-07-12 11:44:05 +00:00
commit 6997ebcfd7
2 changed files with 73 additions and 32 deletions

View File

@ -167,8 +167,6 @@ g_thread_pool_wait_for_new_pool (void)
local_max_idle_time = g_atomic_int_get (&max_idle_time); local_max_idle_time = g_atomic_int_get (&max_idle_time);
last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial); last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
g_atomic_int_inc (&unused_threads);
do do
{ {
if ((guint) g_atomic_int_get (&unused_threads) >= local_max_unused_threads) if ((guint) g_atomic_int_get (&unused_threads) >= local_max_unused_threads)
@ -237,8 +235,6 @@ g_thread_pool_wait_for_new_pool (void)
} }
while (pool == wakeup_thread_marker); while (pool == wakeup_thread_marker);
g_atomic_int_add (&unused_threads, -1);
return pool; return pool;
} }
@ -405,12 +401,16 @@ g_thread_pool_thread_proxy (gpointer data)
} }
} }
g_atomic_int_inc (&unused_threads);
g_async_queue_unlock (pool->queue); g_async_queue_unlock (pool->queue);
if (free_pool) if (free_pool)
g_thread_pool_free_internal (pool); g_thread_pool_free_internal (pool);
if ((pool = g_thread_pool_wait_for_new_pool ()) == NULL) pool = g_thread_pool_wait_for_new_pool ();
g_atomic_int_add (&unused_threads, -1);
if (pool == NULL)
break; break;
g_async_queue_lock (pool->queue); g_async_queue_lock (pool->queue);

View File

@ -62,47 +62,85 @@ test_thread_functions (void)
} }
static void static void
thread_wait_func (gpointer data, test_thread_stop_unused (void)
gpointer user_data)
{ {
guint timeout_ms = GPOINTER_TO_UINT (data); GThreadPool *pool;
guint *n_threads_executed = user_data; guint i;
guint limit = 100;
g_usleep (timeout_ms); /* Spawn a few threads. */
g_thread_pool_set_max_unused_threads (-1);
pool = g_thread_pool_new ((GFunc) g_usleep, NULL, -1, FALSE, NULL);
g_atomic_int_inc (n_threads_executed); for (i = 0; i < limit; i++)
g_thread_pool_push (pool, GUINT_TO_POINTER (1000), NULL);
/* Wait for the threads to migrate. */
while (g_thread_pool_get_num_threads (pool) != 0)
g_usleep (100);
g_assert_cmpuint (g_thread_pool_get_num_threads (pool), ==, 0);
g_assert_cmpuint (g_thread_pool_get_num_unused_threads (), >, 0);
/* Wait for threads to die. */
g_thread_pool_stop_unused_threads ();
while (g_thread_pool_get_num_unused_threads () != 0)
g_usleep (100);
g_assert_cmpuint (g_thread_pool_get_num_unused_threads (), ==, 0);
g_thread_pool_set_max_unused_threads (MAX_THREADS);
g_assert_cmpuint (g_thread_pool_get_num_threads (pool), ==, 0);
g_assert_cmpuint (g_thread_pool_get_num_unused_threads (), ==, 0);
g_thread_pool_free (pool, FALSE, TRUE);
} }
static void static void
test_thread_stop_unused (void) test_thread_stop_unused_multiple (void)
{ {
GThreadPool *pool; GThreadPool *pools[10];
guint i; guint i, j;
guint limit = 100; const guint limit = 10;
guint n_threads_executed = 0; gboolean all_stopped;
/* Spawn a few threads. */ /* Spawn a few threads. */
g_thread_pool_set_max_unused_threads (-1); g_thread_pool_set_max_unused_threads (-1);
pool = g_thread_pool_new (thread_wait_func, &n_threads_executed, -1, FALSE, NULL);
for (i = 0; i < limit; i++) for (i = 0; i < G_N_ELEMENTS (pools); i++)
g_thread_pool_push (pool, GUINT_TO_POINTER (1000), NULL); {
pools[i] = g_thread_pool_new ((GFunc) g_usleep, NULL, -1, FALSE, NULL);
/* Wait for the threads to migrate. */ for (j = 0; j < limit; j++)
while ((guint) g_atomic_int_get (&n_threads_executed) < limit) g_thread_pool_push (pools[i], GUINT_TO_POINTER (100), NULL);
g_usleep (100); }
g_thread_pool_stop_unused_threads (); all_stopped = FALSE;
while (!all_stopped)
{
all_stopped = TRUE;
for (i = 0; i < G_N_ELEMENTS (pools); i++)
all_stopped &= (g_thread_pool_get_num_threads (pools[i]) == 0);
}
/* Wait for threads to die. */ for (i = 0; i < G_N_ELEMENTS (pools); i++)
while (g_thread_pool_get_num_unused_threads () != 0) {
g_usleep (100); g_assert_cmpuint (g_thread_pool_get_num_threads (pools[i]), ==, 0);
g_assert_cmpuint (g_thread_pool_get_num_unused_threads (), >, 0);
}
g_assert_cmpint (g_thread_pool_get_num_unused_threads (), ==, 0); /* Wait for threads to die. */
g_thread_pool_stop_unused_threads ();
g_thread_pool_set_max_unused_threads (MAX_THREADS); while (g_thread_pool_get_num_unused_threads () != 0)
g_usleep (100);
g_thread_pool_free (pool, FALSE, TRUE); g_assert_cmpuint (g_thread_pool_get_num_unused_threads (), ==, 0);
for (i = 0; i < G_N_ELEMENTS (pools); i++)
g_thread_pool_free (pools[i], FALSE, TRUE);
} }
static void static void
@ -346,6 +384,9 @@ test_check_start_and_stop (gpointer user_data)
test_thread_stop_unused (); test_thread_stop_unused ();
break; break;
case 7: case 7:
test_thread_stop_unused_multiple ();
break;
case 8:
test_thread_idle_time (); test_thread_idle_time ();
break; break;
default: default:
@ -375,7 +416,7 @@ test_check_start_and_stop (gpointer user_data)
G_UNLOCK (thread_counter_sort); G_UNLOCK (thread_counter_sort);
} }
if (test_number == 7) { if (test_number == 8) {
guint idle; guint idle;
idle = g_thread_pool_get_num_unused_threads (); idle = g_thread_pool_get_num_unused_threads ();