[m-rev.] for review: Avoid sem_post/sem_wait race in glibc.
Julien Fischer
jfischer at opturion.com
Tue Jul 11 10:37:39 AEST 2017
Hi Peter,
On Wed, 5 Jul 2017, Peter Wang wrote:
> I'm not sure this workaround is necessary. It should be a hard bug to trigger.
> glibc 2.21 is only a couple of years old but hopefully the distributions
> sticking with older glibcs will have backported the fix.
On balance, it's probably better that users don't stumble over the
problem. The diff looks fine.
Julien.
> ---
>
> glibc prior to 2.21 is susceptible to a race between sem_post/sem_wait
> https://sourceware.org/bugzilla/show_bug.cgi?id=12674
>
> (I have not personally encountered this bug, and I hope never to.)
>
> library/thread.m:
> Use a pthread mutex and condition variable instead of a pthread
> semaphore to synchronise the newly spawned thread with its parent in
> ML_create_exclusive_thread.
> ---
> library/thread.m | 63 +++++++++++++++++++++++++++++++++++++++-----------------
> 1 file changed, 44 insertions(+), 19 deletions(-)
>
> diff --git a/library/thread.m b/library/thread.m
> index 4cfec3a..8b28447 100644
> --- a/library/thread.m
> +++ b/library/thread.m
> @@ -520,12 +520,20 @@ INIT mercury_sys_init_thread_modules
>
> typedef struct ML_ThreadWrapperArgs ML_ThreadWrapperArgs;
> struct ML_ThreadWrapperArgs {
> - MercurySem sem;
> + MercuryLock mutex;
> + MercuryCond cond;
> MR_Word goal;
> MR_ThreadLocalMuts *thread_local_mutables;
> - MR_bool thread_started;
> + MR_Integer thread_state;
> MR_String thread_id;
> };
> +
> + enum {
> + ML_THREAD_NOT_READY,
> + ML_THREAD_READY,
> + ML_THREAD_START_ERROR
> + };
> +
> #endif /* MR_THREAD_SAFE */
> ").
>
> @@ -540,11 +548,18 @@ INIT mercury_sys_init_thread_modules
>
> ML_incr_thread_barrier_count();
>
> - MR_sem_init(&args.sem, 0);
> + /*
> + ** The obvious synchronisation object to use here is a semaphore but
> + ** glibc < 2.21 had a bug which could result in sem_post reading from a
> + ** semaphore after (in another thread) sem_wait returns and destroys the
> + ** semaphore. <https://sourceware.org/bugzilla/show_bug.cgi?id=12674>
> + */
> + pthread_mutex_init(&args.mutex, MR_MUTEX_ATTR);
> + pthread_cond_init(&args.cond, MR_COND_ATTR);
> args.goal = goal;
> args.thread_local_mutables =
> MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
> - args.thread_started = MR_FALSE;
> + args.thread_state = ML_THREAD_NOT_READY;
> args.thread_id = NULL;
>
> pthread_attr_init(&attrs);
> @@ -553,21 +568,26 @@ INIT mercury_sys_init_thread_modules
> pthread_attr_destroy(&attrs);
>
> if (thread_err == 0) {
> - int sem_err;
> -
> - do {
> - sem_err = MR_SEM_WAIT(&args.sem, ""ML_create_exclusive_thread"");
> - } while (sem_err == -1 && MR_SEM_IS_EINTR(errno));
> -
> - if (sem_err != 0) {
> - MR_fatal_error(
> - ""ML_create_exclusive_thread: MR_SEM_WAIT error %d"", errno);
> + MR_LOCK(&args.mutex, ""ML_create_exclusive_thread"");
> + while (args.thread_state == ML_THREAD_NOT_READY) {
> + int cond_err;
> +
> + cond_err = MR_COND_WAIT(&args.cond, &args.mutex,
> + ""ML_create_exclusive_thread"");
> + /* EINTR should not be possible but it has happened before. */
> + if (cond_err != 0 && errno != EINTR) {
> + MR_fatal_error(
> + ""ML_create_exclusive_thread: MR_COND_WAIT error %d"",
> + errno);
> + }
> }
> + MR_UNLOCK(&args.mutex, ""ML_create_exclusive_thread"");
> }
>
> - MR_sem_destroy(&args.sem);
> + pthread_cond_destroy(&args.cond);
> + pthread_mutex_destroy(&args.mutex);
>
> - if (args.thread_started) {
> + if (args.thread_state == ML_THREAD_READY) {
> *thread_id = args.thread_id;
> return MR_TRUE;
> }
> @@ -583,8 +603,10 @@ INIT mercury_sys_init_thread_modules
> MR_String thread_id;
>
> if (MR_init_thread(MR_use_now) == MR_FALSE) {
> - args->thread_started = MR_FALSE;
> - MR_SEM_POST(&args->sem, ""ML_exclusive_thread_wrapper"");
> + MR_LOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
> + args->thread_state = ML_THREAD_START_ERROR;
> + MR_COND_SIGNAL(&args->cond, ""ML_exclusive_thread_wrapper"");
> + MR_UNLOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
> return NULL;
> }
>
> @@ -605,9 +627,12 @@ INIT mercury_sys_init_thread_modules
> ** Take a copy of the goal before telling the parent we are ready.
> */
> goal = args->goal;
> - args->thread_started = MR_TRUE;
> +
> + MR_LOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
> + args->thread_state = ML_THREAD_READY;
> args->thread_id = thread_id;
> - MR_SEM_POST(&args->sem, ""ML_exclusive_thread_wrapper"");
> + MR_COND_SIGNAL(&args->cond, ""ML_exclusive_thread_wrapper"");
> + MR_UNLOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
>
> ML_call_back_to_mercury_cc_multi(goal, thread_id);
>
> --
> 2.9.0
>
> _______________________________________________
> reviews mailing list
> reviews at lists.mercurylang.org
> https://lists.mercurylang.org/listinfo/reviews
More information about the reviews
mailing list