Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.144.102.239] |
|
Сообщ.
#1
,
|
|
|
Предлагаю обсудить проблему поднятия уровня блокировки, с Read до Write, для соответствующих мьютексов, типа std::shared_mutex. Как известно, эта задача нерешаемая, но мне пришло в голову, что если при повышении защищаться не от блокировок вообще, а только от тех кто тоже пытается сделать upgrade, то уже будет неплохо. В остальных случаях можно сделать переблокировку и т.д.
На скорую руку набросал вот такой код. Как обычно, прошу критики. class CSharedMutex { public: typedef uint64_t TLockCounter; static const TLockCounter UpgradeMask = TLockCounter(1) << 63; //Блокировка на запись void lock() noexcept { m_mx.lock(); //Блокируем на запись while(m_cnt != 0) //Ждём того, кто сделал upgrade std::this_thread::yield(); } void unlock() noexcept { m_mx.unlock(); } //Блокировка на чтение void lock_shared() noexcept { m_mx.lock(); for (;;) //Ждём того, кто сделал upgrade { TLockCounter cnt = m_cnt & ~UpgradeMask; if (m_cnt.compare_exchange_weak(cnt, cnt + 1)) break; }; m_mx.unlock(); } void unlock_shared() noexcept { const TLockCounter cnt = m_cnt--; if (cnt & UpgradeMask) { if (cnt == UpgradeMask) //Если была переблокировка при upgade, разблокируем запись { m_cnt = 0; unlock(); } else if ((cnt & ~UpgradeMask) == 1) //Если последний, сбрасываем флаг upgrade m_cnt = 0; } } //Возвращает true, если был сделан upgrade, false - если переблокировка bool upgrade_lock() noexcept { for (;;) { TLockCounter cnt = m_cnt; if (cnt & UpgradeMask) //Если кто-то уже делает upgrade, переблокируем { unlock_shared(); lock(); m_cnt = UpgradeMask; return false; } if (m_cnt.compare_exchange_weak(cnt, cnt | UpgradeMask)) { //Если удалось установить флаг upgrade, ждём, когда выйдут все читатели if (cnt != 1) while (m_cnt != UpgradeMask + 1) std::this_thread::yield(); break; } std::this_thread::yield(); } return true; } protected: std::mutex m_mx; volatile std::atomic<TLockCounter> m_cnt; }; |