Информация об изменениях

Сообщение Re: Временный блокировщик от 06.02.2024 13:26

Изменено 06.02.2024 18:22 Кодт

Re: Временный блокировщик
Здравствуйте, Marty, Вы писали:

M>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.

M>В других потоках, или в том же потоке, можно запрещать, рекурсивно, главное, разлочить столько же раз, сколько и залочил.

Если не вдаваться в подробности, то правда ли, что апи получается такой? Назовём эту штуку "дверь". Потому что поток проходит через неё и дальше занимается своими делами.
struct Door {
  int level = 0;

  bool is_open() const { return level >= 0; }

  // апи управляющей стороны (не ждёт) (меняет состояние двери)
  void open() { ++level; }
  void close() { --level; }

  // апи управляемой стороны (не меняет состояние двери)
  bool try_pass() const { return is_open(); }  // не ждёт
  bool pass(auto timeout) const { WAIT(timeout, is_open); return is_open(); }
  void pass() const { WAIT(is_open); }
}


Надо заметить, что поток может запереть сам себя:
void foo(Door& door) {
  door.close();
  .....
  door.pass();
  .....
}

и в этом случае ему нужна внешняя помощь — какой-то другой поток должен отпереть эту дверь

M>Что-то городить с какими-то примитивами синхронизации не вижу смысла, ну, может я ошибаюсь.


Примитивы синхронизации стоят дёшево, пока не попадают на блокировку.
Но если очень хочется упороться по лок-фри и написать своё, заведомо userspace, то ради бога.

Семафоры и мьютексы, будучи симметричными инструментами, тут не подойдут. (Не, конечно, на паре семафоров можно построить абсолютно все производные примитивы).
А вот на кондеях такое делается элементарно
class Door {
  int level = 0;
  mutable std::mutex guard;
  std::conditional_variable cv;

  bool is_open() const {
    return level > 0;
  }
  auto is_open_fun() const {
    return [this]() { return is_open();
  }

public:
  void open() {
    std::unique_lock lock(guard);
    if (is_open()) cv.notify_all();
  }
  void close() {
    std::unique_lock lock(guard);
    --level;
  }
    
  bool try_pass() const {
    std::unique_lock lock(guard);
    return is_open();
  }
  bool pass(auto timeout) const {
    std::unique_lock lock(guard);
    return cv.wait_for(lock, timeout, is_open_fun());
  }
  void pass() const {
    std::unique_lock lock(guard);
    cv.wait(lock, is_open_fun());
  }
};
Re: Временный блокировщик
Здравствуйте, Marty, Вы писали:

M>Где-то в каком-то потоке периодически что-то производится, какие-то действия. Иногда надо временно запретить их производить.

M>В других потоках, или в том же потоке, можно запрещать, рекурсивно, главное, разлочить столько же раз, сколько и залочил.

Если не вдаваться в подробности, то правда ли, что апи получается такой? Назовём эту штуку "дверь". Потому что поток проходит через неё и дальше занимается своими делами.
struct Door {
  int level = 0;

  bool is_open() const { return level >= 0; }

  // апи управляющей стороны (не ждёт) (меняет состояние двери)
  void open() { ++level; }
  void close() { --level; }

  // апи управляемой стороны (не меняет состояние двери)
  bool try_pass() const { return is_open(); }  // не ждёт
  bool pass(auto timeout) const { WAIT(timeout, is_open); return is_open(); }
  void pass() const { WAIT(is_open); }
}


Надо заметить, что поток может запереть сам себя:
void foo(Door& door) {
  door.close();
  .....
  door.pass();
  .....
}

и в этом случае ему нужна внешняя помощь — какой-то другой поток должен отпереть эту дверь

M>Что-то городить с какими-то примитивами синхронизации не вижу смысла, ну, может я ошибаюсь.


Примитивы синхронизации стоят дёшево, пока не попадают на блокировку.
Но если очень хочется упороться по лок-фри и написать своё, заведомо userspace, то ради бога.

Семафоры и мьютексы, будучи симметричными инструментами, тут не подойдут. (Не, конечно, на паре семафоров можно построить абсолютно все производные примитивы).
А вот на кондеях такое делается элементарно
class Door {
  int level = 0;
  mutable std::mutex guard;
  std::conditional_variable cv;

  bool is_open() const {
    return level > 0;
  }
  auto is_open_fun() const {
    return [this]() { return is_open();
  }

public:
  void open() {
    std::unique_lock lock(guard);
    ++level;  // спасибо vopl за фикс недочепятки
    if (is_open()) cv.notify_all();
  }
  void close() {
    std::unique_lock lock(guard);
    --level;
  }
    
  bool try_pass() const {
    std::unique_lock lock(guard);
    return is_open();
  }
  bool pass(auto timeout) const {
    std::unique_lock lock(guard);
    return cv.wait_for(lock, timeout, is_open_fun());
  }
  void pass() const {
    std::unique_lock lock(guard);
    cv.wait(lock, is_open_fun());
  }
};