Pthreads: первый вызов блокировки мьютекса для типа «рекурсивный» мьютекс иногда имеет поведение «нормального» мьютекса. - PullRequest
1 голос
/ 28 мая 2020

Дизайн:

  • Синглтон, содержащий ресурс мьютекса 'recursive'.
  • 2 потока используют этот синглтон для обновления / управления данными.
  • Синглтон - это создается в зависимости от того, какой поток пытается получить к нему доступ первым.
  • Создание синглтона имеет глобальную блокировку, чтобы гарантировать, что мы вызываем mutex attr init и mutex init только один раз.

Пример кода: оба потока имеют идентичный поток (только разные данные) и сначала вызывают funcX ()

instance () имеет глобальную блокировку мьютекса () внутри, чтобы гарантировать только 1 создается экземпляр A. Он также имеет дополнительную (! _Instance) проверку вскоре после блокировки, чтобы убедиться, что мы не создадим экземпляр снова.

class A
{
public:
    void funcA();
    void funcB();
private:
    <members>
    <boost::recursive_mutex> m; 
};

void funcA()
{
    m.lock();
    <Do something>
    m.unlock();
    return;
}

void funcB()
{
    m.lock()
    <Do something>
    m.unlock()
    return;
}


void funcX()
{
    Singleton::instance().funcA();
    return;
}

void funcY()
{
    Singleton::instance().funcB();
    return;
}


========================================================================

A& Singleton::instance()
{
   <Global mutex lock>
   if (!_instance)
   {
     createInstance();
   }
   <Global mutex unlock>
   return _instance;
}

Проблема:

Очень редко первый вызов блокировки мьютекса не увеличивает the __count(0) переменную. Хотя все атрибуты __owner (thread id), __nusers (1), __lock (2) обновлены. Всякий раз, когда я пытаюсь зарегистрировать атрибут __kind, проблема не возникает.

Первоначальные результаты:

Когда возникает проблема, оба потока пытаются инициализировать синглтон (также мьютекс). Из-за глобальной блокировки при создании синглтона только 1 поток продолжает работу, создает мьютекс и инициализирует его типом recursive. Тогда поток, блокирующий мьютекс, смотрит на устаревшую память и заставляет думать, что тип мьютекса - Нормальный? __kind = 0. Блокировка мьютекса возвращает успех. И когда вызывается последующая разблокировка, тип мьютекса теперь обновляется как recursive, и, поскольку pthread разблокировка не имеет 0 проверок, он в конечном итоге уменьшает __count до INT_MAX.

else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)
                  == PTHREAD_MUTEX_RECURSIVE_NP, 1))
    {
      /* Recursive mutex.  */
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
    return EPERM;

      if (--mutex->__data.__count != 0)
    /* We still hold the mutex.  */
    return 0;
      goto normal;
    }  

Разблокировка также возвращает успех, и мьютекс никогда не освобождается, в результате чего другой поток постоянно находится в состоянии ожидания.

Каковы возможные причины этого сценария? Может ли __kind как-то испортиться?

...