Коллеги, посоветуйте...
Хочу атомарный rwlock, но не очень понимаю, что и как должно быть в плане memory order. И да, я в курсе голодных писателей в столь наивной реализации.
Хочется что-то вроде этого:
class atomic_shared_mutex
{
public:
atomic_shared_mutex()
: mtx_(0)
{}
void lock() {
// writer пишет в состояние -1int state = mtx_.load();
while(state != 0 || !mtx_.compare_exchange_strong(state, -1)) {
std::this_thread::yield();
state = mtx_.load();
}
}
void unlock() {
mtx_.store(0);
}
void lock_shared() {
// reader просто увеличивает счётчик собратьевint state = mtx_.load();
for(;;) {
if(state == -1) {
std::this_thread::yield();
state = mtx_.load();
} else if(mtx_.compare_exchange_strong(state, state+1)) {
break;
}
}
}
void unlock_shared() {
--mtx_;
}
private:
std::atomic<int> mtx_;
};
Да, спасибо, читал.
В моём представлении должно быть как то так: acquire/release на входе/выходе + дополнительное relaxed чтение при блокировке читателем.
class atomic_shared_mutex {
public:
atomic_shared_mutex() : mtx_(0) {}
void lock_shared() {
// изначально читаем в relaxed, барьер будет далее, в CAS операцииint state = mtx_.load(std::memory_order_relaxed);
for(;;) {
if(state < 0) {
// занят писателем - прерываем поток, при возврате перечитываем состояние
std::this_thread::yield();
state = mtx_.load(std::memory_order_relaxed);
}
// не занят, увеличиваем счётчик читателейelse if(mtx_.compare_exchange_weak(state, state+1, std::memory_order_acquire))
break;
}
}
void unlock_shared() {
mtx_.fetch_sub(1, std::memory_order_release);
}
void lock() {
for(;;) {
int state = 0;
if(mtx_.compare_exchange_weak(state, -1, std::memory_order_acquire))
break;
// попытка не удалась, использовали weak CAS, отдадим управление
// только если мьютекс действительно занятif(state != 0)
std::this_thread::yield();
}
}
void unlock() {
mtx_.store(0, std::memory_order_release);
}
private:
std::atomic<int> mtx_;
};
Здравствуйте, sokel, Вы писали:
S>Хочу атомарный rwlock, но не очень понимаю, что и как должно быть в плане memory order.
я бы назвал это не "атомарный", а spin rw lock (yield может привнести задержку, зависит от твоих задач)
memory order нужен для контроля над реордерингом чтения\записи в разные переменные. у тебя в коде одна переменная, так что реордеринг не повлияет на твой код никак, но может косвенно повлиять на код снаружи имхо
я бы посоветовал не увлекаться реордерингом и заюзал seq_cst/
очень часто примитивы синхронизации сами по себе являются еще и барьерами (каждый вызов метода имеется в виду), это распространенная практика
Здравствуйте, uzhas, Вы писали:
U>мне кажется, во всех вариантах lock_shared имеет логическую ошибку
А в чём конкретно ошибка?
U>я бы начал с этого (ордерингом не заморачиваюсь по причинам, описанным выше)
Ну я как бы насчёт ордеринга только и спрашивал изначально
Погонял тесты, вроде нормально работает.
Тесты в попугаях, что то вроде N/M потоков читателей/писателей в цикле толкаются на доступе к шаред данным.
Данные — массив из 100 интов, писатель делает всем инкремент, читатель проверяет что они все равны.
Интересно ещё вот что — думал под Windows std::mutex будет критической секцией, ан нет... и тормозней он чего то (vs 2013).
Здравствуйте, sokel, Вы писали:
S>А в чём конкретно ошибка?
мне показалось, что если проинкрементить state не получилось, то забыли перечитать актуальное состояние. я ошибся, это делает метод compare_exchange_strong
S>Интересно ещё вот что — думал под Windows std::mutex будет критической секцией, ан нет... и тормозней он чего то (vs 2013).
могу предположить, что это связано с нерекурсивностью std::mutex (доп пляски вокруг критической секции в кишках). попробуй recursive_mutex на винде
Здравствуйте, uzhas, Вы писали:
U>могу предположить, что это связано с нерекурсивностью std::mutex (доп пляски вокруг критической секции в кишках).
Да, похоже на то. Ещё и pimpl.
U>попробуй recursive_mutex на винде
Да пока меня и my::mutex на CRITICAL_SECTION устраивает. С posix вот тоже не везде гладко. Например для HP-UX полезно мьютекс подспинлочить немного, через pthread_mutexattr_setspin_np. Но в std mutex это не учитывается.