On Sat, 09 May 2015, Philip Guenther wrote:
> Looks like the problem is that while one thread is calling vfork(),
> another thread does something that acquires the spinlock inside
> rthread_dl_lock() (probably another vfork). The child of the vfork tries
> to acquire the spinlock so that it can release the recursive lock itself
> and spins forever. The solution is to have the child reset that lock.
> I'm unable to reproduce after applying the diff below.
>
> oks?
>
With your diff applied, the test suite from latest libinotify-kqueue's
git snapshot (which was even more prone to hangs than libinotify-20141104
currently in our ports tree) also runs w/o problems.
Thanks a lot Philip!
David
> Index: rthread.c
> ===================================================================
> RCS file: /data/src/openbsd/src/lib/librthread/rthread.c,v
> retrieving revision 1.81
> diff -u -p -r1.81 rthread.c
> --- rthread.c 29 Apr 2015 06:01:37 -0000 1.81
> +++ rthread.c 9 May 2015 23:22:25 -0000
> @@ -671,8 +671,7 @@ _rthread_dl_lock(int what)
> static struct pthread_queue lockers = TAILQ_HEAD_INITIALIZER(lockers);
> static int count = 0;
>
> - if (what == 0)
> - {
> + if (what == 0) {
> pthread_t self = pthread_self();
>
> /* lock, possibly recursive */
> @@ -689,9 +688,7 @@ _rthread_dl_lock(int what)
> }
> count++;
> _spinunlock(&lock);
> - }
> - else
> - {
> + } else if (what == 1) {
> /* unlock, possibly recursive */
> if (--count == 0) {
> pthread_t next;
> @@ -704,6 +701,12 @@ _rthread_dl_lock(int what)
> if (next != NULL)
> __thrwakeup(next, 1);
> }
> + } else {
> + /* reinit: used in child after fork to clear the queue */
> + lock = _SPINLOCK_UNLOCKED_ASSIGN;
> + if (--count == 0)
> + owner = NULL;
> + TAILQ_INIT(&lockers);
> }
> }
>
> Index: rthread_fork.c
> ===================================================================
> RCS file: /data/src/openbsd/src/lib/librthread/rthread_fork.c,v
> retrieving revision 1.11
> diff -u -p -r1.11 rthread_fork.c
> --- rthread_fork.c 7 Apr 2015 01:27:07 -0000 1.11
> +++ rthread_fork.c 9 May 2015 23:12:01 -0000
> @@ -105,12 +105,12 @@ _dofork(int is_vfork)
> _thread_malloc_unlock();
> _thread_atexit_unlock();
>
> + if (newid == 0) {
> #if defined(__ELF__)
> - if (_DYNAMIC)
> - _rthread_dl_lock(1);
> + /* reinitialize the lock in the child */
> + if (_DYNAMIC)
> + _rthread_dl_lock(2);
> #endif
> -
> - if (newid == 0) {
> /* update this thread's structure */
> me->tid = getthrid();
> me->donesem.lock = _SPINLOCK_UNLOCKED_ASSIGN;
> @@ -128,6 +128,10 @@ _dofork(int is_vfork)
> /* single threaded now */
> __isthreaded = 0;
> }
> +#if defined(__ELF__)
> + else if (_DYNAMIC)
> + _rthread_dl_lock(1);
> +#endif
> return newid;
> }
>