Есть ли способ атомарно flu sh двоичный семафор в C ++ на Linux? - PullRequest
7 голосов
/ 03 марта 2020

В некоторых ядрах предусмотрена операция « flu sh» на семафоре для разблокирования всех задач, ожидающих на семафоре.

Например, VxWorks имеет semFlu sh () API, который атомарно разблокирует все задачи, отложенные для указанного семафора, т. Е. Все задачи будут разблокированы до того, как разрешено выполнение любых.

Я реализую класс C ++ в Linux, который ведет себя как двоичный семафор, а также имеет эту функциональность "flu sh". К сожалению, semaphore.h на Linux не предоставляет API типа flu sh () или broadcast ().

Что я пробовал: Использование условные переменные для реализации двоичного семафора . Вот мой псевдокод:

class BinarySem
{
    BinarySem();

    bool given;
    mutex m;
    condition_var cv;
    give();
    take();
    take( Timeout T );
    tryTake();
    flush();
}

BinarySem::BinarySem()
: given(false)
{}

// take(Timeout T), tryTake() not shown
// to make question concise on StackOverflow

BinarySem::give()
{
    {
        lock_guard lk(m);
        given = true;
    }
    cv.notify_one();
}   

BinarySem::flush()
{
    {
        lock_guard lk(m);
        given = true;
    }
    cv.notify_all();
}

BinarySem::take()
{
    unique_lock lk(m);
    while(!given)
    {
        cv.wait(lk);
    }
    given = false;
    lk.unlock();
}

Однако этот flush() не будет вести себя правильно. Скажем, у нас есть два потока, ожидающих BinarySem (то есть они оба вызвали take()). Пусть эти потоки будут hiPrioThread и loPrioThread.

Когда для объекта BinarySem вызывается flush(), hiPrioThread выходит из take() и запускается. Когда он дает (hiPrioThread просто дает, он еще не завершился), loPrioThread все равно не сможет работать, потому что логическое значение given теперь снова false. Логическое значение требуется для защиты от ложных пробуждений.

Напротив, функция flush() семафора должна просто разблокировать все потоки, и они могут запускаться всякий раз, когда у них появляется шанс.

Что если я не установлю given = false в конце take()? Это сделает мой код уязвимым для ложных пробуждений, и тогда несколько потоков могут разблокироваться при использовании give().

У кого-нибудь есть предложения?

1 Ответ

4 голосов
/ 03 марта 2020

Заимствовать концепцию из некоторых реализаций "CyclicBarrier" и иметь счетчик генерации или цикла.

«Промывка» семафора продвигает поколение. Каждый получатель записывает свое поколение, прежде чем он ждет, и получатель ждет, пока семафор будет given или , чтобы поколение изменилось:

BinarySem::flush() {
  {
    lock_guard lk(m);
    current_gen++;    // "flush" all waiters from the previous gen
    //given = true;   // No need to give; the 'current' taker will do this when done
  }
  cv.notify_all();
}

BinarySem::take() {
  lock_guard lk(m);
  uint64_t my_generation = current_gen;
  while (!given && my_generation == current_gen) {
    cv.wait(lk);
  }
  if (my_generation == current_gen) {
    given = false;
  }
}

(Предупреждение: не проверено)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...