pthread_once導致死鎖

yghr發表於2024-08-06

在一個pthread_once方法內又再次呼叫了這個pthread_once導致死鎖。

分析下這個pthread_once的原始碼:


可以看到這個pthread_once_t結構體就是一個整形數字加自旋鎖。

int
___pthread_once (pthread_once_t *once_control, void (*init_routine) (void))
{
  /* Fast path.  See __pthread_once_slow.  */
  int val;
  val = atomic_load_acquire (once_control);
  if (__glibc_likely ((val & __PTHREAD_ONCE_DONE) != 0)) // 這裡判斷如果已經執行完成就返回0
    return 0;
  else
    return __pthread_once_slow (once_control, init_routine);
}
static int
__attribute__ ((noinline))
__pthread_once_slow (pthread_once_t *once_control, void (*init_routine) (void))
{
  while (1)
    {
      int val, newval;

      /* CAS把狀態改為__PTHREAD_ONCE_INPROGRESS */
      /* We need acquire memory order for this load because if the value
         signals that initialization has finished, we need to see any
         data modifications done during initialization.  */
      val = atomic_load_acquire (once_control);
      do
	{
	  /* Check if the initialization has already been done.  */
	  if (__glibc_likely ((val & __PTHREAD_ONCE_DONE) != 0))
	    return 0;

	  /* We try to set the state to in-progress and having the current
	     fork generation.  We don't need atomic accesses for the fork
	     generation because it's immutable in a particular process, and
	     forked child processes start with a single thread that modified
	     the generation.  */
	  newval = __fork_generation | __PTHREAD_ONCE_INPROGRESS;
	  /* We need acquire memory order here for the same reason as for the
	     load from once_control above.  */
	}
      while (__glibc_unlikely (!atomic_compare_exchange_weak_acquire (
	  once_control, &val, newval)));

      /* Check if another thread already runs the initializer.	*/
      if ((val & __PTHREAD_ONCE_INPROGRESS) != 0)
	{
       /* 修改失敗,說明其他執行緒在修改,直接等待 */
	  /* Check whether the initializer execution was interrupted by a
	     fork.  We know that for both values, __PTHREAD_ONCE_INPROGRESS
	     is set and __PTHREAD_ONCE_DONE is not.  */
	  if (val == newval)
	    {
	      /* Same generation, some other thread was faster.  Wait and
		 retry.  */
	      futex_wait_simple ((unsigned int *) once_control,
				 (unsigned int) newval, FUTEX_PRIVATE);
	      continue;
	    }
	}

      /* This thread is the first here.  Do the initialization.
	 Register a cleanup handler so that in case the thread gets
	 interrupted the initialization can be restarted.  */
      pthread_cleanup_combined_push (clear_once_control, once_control);
      /* 呼叫使用者傳入的方法 */
      init_routine ();

      pthread_cleanup_combined_pop (0);


      /* Mark *once_control as having finished the initialization.  We need
         release memory order here because we need to synchronize with other
         threads that want to use the initialized data.  */
      /* 修改狀態為__PTHREAD_ONCE_DONE */
      atomic_store_release (once_control, __PTHREAD_ONCE_DONE);

      /* Wake up all other threads.  */
      /* 喚醒其他執行緒 */
      futex_wake ((unsigned int *) once_control, INT_MAX, FUTEX_PRIVATE);
      break;
    }

  return 0;
}

相關文章