LINUX.ORG.RU

C++, Авто-отпускающийся мьютекс для шареной памяти.

 ,


2

7

Есть кусок шареной памяти (128MB), куда часто ломятся разные процессы-читатели и читают случайные куски по 1-32 КБ. Есть один процесс «Master», которому раз в 10 сек надо обновить целиком 128 MB так, чтобы никакой читатель не прочитал полкилобайта фигни.

Решение: т.к. любой процесс-читатель могут убить админы в любой момент, глупо рассчитывать на то, что процесс обязательно добровольно отпустит некий мьютекс, размещённый в этой шареной памяти.

Предлагается такая фигня:

В шареной памяти есть атомарные INT-переменные: fence, num, lasttime.

fence:    если 1, значит закрыто для читателей
num:      число читателей, копошащихся сейчас
lasttime: время успешного захода последнего читателя

1. На входе читатель с помощью CAS пытается заинкрементить num, если было открыто (0 == fence).

2. Отметившись в num, читатель атомарно пишет текущее микросекундное время (CLOCK_MONOTONIC) в lasttime, если там лежит меньшее (СAS-ом). Если там лежит время <= текущего, то читатель ничего не делает.

3. Читатель с помощью CAS на выходе декрементит num обратно.

4. Если Master хочет сделать своё дело, он ставит fence = 1, затем бесконечно ждёт num == 0. Если в этом ожидании он замечает, что lasttime устарел на секунду от текущего времени, то Master считает что кто-то из читателей подох посреди пути и делает своё дело вероломно, записывая num = 0 и затем открывая fence = 0.

Взлетит? Может есть чё попроще? Есть решение, при котором всё это не нужно, но это уже другая тема. Интересуют заморочи. Т.е. допустим, авто-отпускающийся мьютекс нужен.



Последнее исправление: hlamotron (всего исправлений: 3)

Совет не для крутых поцанов, знающих слово CAS, но лучше сделать передачу токена. Или SysV IPC.

tailgunner ★★★★★
()
Ответ на: комментарий от hlamotron

Передача токена: клиенты подсоединяются к мастеру через локальный сокет (или получают pipe при создании), и для разрешения доступа к памяти мастер передает ему байт (токен), а после после окончания доступа клиент возвращает токен обратно. После неожиданного завершения процесса мастер получает EOF на дескрипторе клиента и проводит чистку.

SysV IPC: man semop, ищи SEM_UNDO

tailgunner ★★★★★
()

Если предположение «кто хочет прочитать — прочитает за секунду» верно, то можно кольцевую очередь из трёх буферов. Некоторые клиенты читают из предыдущего, некоторые — из текущего, мы всегда пишем в следующий. По окончании записи в атомарный указатель на текущее значение кладём адрес следующего буфера.

vzzo ★★★
()

Если в этом ожидании он замечает, что lasttime устарел на секунду от текущего времени, то Master считает что кто-то из читателей подох посреди пути

Такие алгоритмы ненадежны в силу закона Мерфи.

Проще так:

Кусок шареной памяти держать в файле. Читатели при необходимости открывают файл и читают, как хотят, ничего не меняя в нём, а затем, начитавшись, закрывают. Мастер при необходимости всё обновить создаёт новый файл, наполняет данными, а затем переименовывает его в прежнее имя. Конец.

Sorcerer ★★★★★
()
Ответ на: комментарий от Deleted

Непонятно почему три буфера, а не два.

Думал, что пишет он тоже раз в секунду.

Но вообще, зависит от того, как устроена запись. Если по таймеру раз в 10 секунд быстро пишет в буфер — то да, хватит двух. Если как-то продолжительно, то нужно три. Исходя из правила «всё пойдёт не так» — можно даже сделать четыре.

vzzo ★★★
()
Ответ на: комментарий от anonymous

transactional memory

1. Я не видел реализаций, нормально работающих с шаренной памятью.

2. STM довольно неэффективна для больших буферов. Intel TSX вообще может не работать, если обращаться к данным за пределами кэша (https://software.intel.com/en-us/node/582935) или если данные находятся на разных страницах.

hateyoufeel ★★★★★
()

man seqlock

Примитив тривиально реализуется руками и позволяет мастеру писать без блокировок, а клиентам читать с возможным рестартом.

Pavval ★★★★★
()

отпусти меня, о чудо мьютекс.

anonymous
()
Ответ на: комментарий от tailgunner

Неважно. Главное, что Ъ-STM. Даже MVCC, my ass.

Особенно наличие параллельного доступа радует, ага.

hateyoufeel ★★★★★
()

отвечает КО

тебе нужен не мутекс!

тебе нужно дополнить твои 128мб еще 128мб + 4мя вспомогательными кб шареной памяти. это будет как бы front buffer и backbuffer. в оставшиеся 4кб ты кладешь pointer, который указывает либо на первые 128мб, либо на вторые.

если все 128мб обновлять дорого, то сделай массив указателей на тайлы(в 4кб на 64битной машине уместится 512 указателей, т.е. каждый указатель будет адресовать 128м/512=256м/1024=256кб на каждый сегмент. если это дофига, просто увеличь размер вспомогательной области - 32 кб обеспечат тебе уже 8кб на сегмент например.

захотел записать: пишешь в backbuffer, потом заменяешь указатель на весь буфер/сегмент и все кто пришел после этой замены видят новые данные.

i36_zubov
()
Ответ на: отвечает КО от i36_zubov

к слову, если процессы-читатели будет читать оглядываясь все таки на факт «страничной» организации буфера, то можно даже какой-нибудь аллокатор этих «страниц» замутить.

я такую гадость делал для приема 40гбит/сек и запись на SSD, отдачу части данных на GPU, и еще небольшой анализ.

твой враг в этом случае - это когда процесс читатель читает страницу, а тебе не хватает страниц на замену.

тут можно сделать следующее: просто «забирать» страницу в мастере, а в slave проверять, после чтения очередной «страницы» не сменился ли указатель. можно младшим битом там манипулировать как признаком чтения.

i36_zubov
()
Ответ на: комментарий от Deleted

Чтобы уменьшить вероятность ситуации «читаю, хоп.. буффер отжали».

RiseOfDeath ★★★★
()
Ответ на: отвечает КО от i36_zubov

захотел записать: пишешь в backbuffer, потом заменяешь указатель на весь буфер/сегмент и все кто пришел после этой замены видят новые данные.

А потом захотел записать еще раз - и приходит северная лисичка к тем, кто еще не закончил писать там, где ты решил обновить данные.

anonymous
()
Ответ на: комментарий от anonymous

кто еще не закончил писать/читать

captcha: 1023

anonymous
()
Ответ на: комментарий от anonymous

такие проблемы решаются взятием указателя и возможно прицепленной к нему «версии», копированием буфера к себе, проверкой что указатель и «версия» не сменились.

если запись идет с небольшим темпом, то такая ситуация может вообще не случаться.

i36_zubov
()
Ответ на: комментарий от i36_zubov

такие проблемы решаются взятием указателя и возможно прицепленной к нему «версии»

И что она решит? Читатель даже если будет ее перепроверять на каждый чих, то между проверкой и чтением данные могут быть изменены.

копированием буфера к себе

Это не атомарная операция, так что без разницы - копируй не копируй, можешь получить мусор.

если запись идет с небольшим темпом, то такая ситуация может вообще не случаться.

А, ну тогда ладно, и так сойдет.

anonymous
()
27 октября 2016 г.
Ответ на: комментарий от Vic

Вопрос сначала решился забитием на него, а потом снова решили попробовать его запилить.

Запилили так: постановщик мьютекса ставит атомик-переменную в 1 (типа, «занято») и перед этим ставит 64-битную атомарную переменную в текущее CLOCK_MONOTONIC значение микросекунд.

Если другие потоки встретили «занято», то любой из них вправе перевернуть в «не занято», если прошло более 1000 микросекунд.

Характер пользования этим мьютексом в том, что «занято» делается редко, потому авто-отпускание никого не пришибёт точно.

hlamotron
() автор топика
Ответ на: комментарий от hlamotron

Запилили так: постановщик мьютекса ставит атомик-переменную в 1 (типа, «занято») и перед этим ставит 64-битную атомарную переменную в текущее CLOCK_MONOTONIC значение микросекунд.

facepalm.jpg

tailgunner ★★★★★
()
Ответ на: комментарий от peregrine

А кто даст гарантии что операция обновления указателей атомарна?

Железо, где MOV атомарен. Если адрес не выровнен, то LOCK MOV. Машинное слово, что есть указатель, машина меняет атомарно.

hlamotron
() автор топика
Ответ на: комментарий от hlamotron

Подумай, что будет, если в середине критической секции система снимет нить-владельца с процессора. Еще подумай, что будет, если система снимет с процессора нить, которая уже обновила счетчик микросекунд, но еще не обновила лок.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Насрать на оба события. Критической секции не существует. Смерть любых потоков в любой момент не страшна. В том и была задача, чтобы полностью было похрену на любые смерти потоков, на то «мьютекс» и «авто-отпускающийся». Все приложившие руку к лицу вызывают у меня очень большую руку к очень большому лицу. Хлопок от моей руки по моему лицу вышибает стёкла в радиусе ста километров!

Предположим, поток W полез лочить. Он записал счётчик микросекунд и подох. Всем похрен. Никто не смотрит на счётчик микросекунд, пока не взведён лок.

Предположим, поток R пошёл читать заслонку и увидел «занято». Полез проверять счётчик микросекунд, а там подохший W успел насрать апдейт. Всем похрен - R подождёт лишних тактов - не смертельно.

«Счётчик микросекунд» не обязательно микросекунд, пускай тактов процессора. Счётчик микросекунд и сама защёлка пакуются в одну атомарную переменную, плюс ко всему.

hlamotron
() автор топика
Ответ на: комментарий от hlamotron

Критической секции не существует.

Мютекса походу тоже.

«Счётчик микросекунд» не обязательно микросекунд, пускай тактов процессора.

Такое впечатление, что ты придумываешь детали на ходу. Или просто несешь горячечный бред.

Счётчик микросекунд и сама защёлка пакуются в одну атомарную переменную, плюс ко всему.

...шириной 65 битов

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от Pavval

Всем кроме тебя понятно. Все тоже приложили руку к лицу.

Не факт, что всем что-то там понятно. Очередное школоло решило проборцунировать в интернетике и показать, как оно над кем-то смеётся. А конструктива ноль. Возможно я что-то не так понял или не так написал, но следовало бы объяснить, для того и существует дискуссия.

hlamotron
() автор топика
Ответ на: комментарий от tailgunner

Мютекса походу тоже.

Походу ты нечетал задачу. Смысл топика был не в использовании каких-либо мьютексов, а в решении описанной задачи. Мьютексом тут был назван произвольный кусок говна, который решает задачу, а не то, что обычно называют мьютексом.

Такое впечатление, что ты придумываешь детали на ходу. Или просто несешь горячечный бред.

Такое ощущение, что тебе насрать на задачу.

...шириной 65 битов

Тебе обязательно 64 бита на такты процессора? Ты собрался считать тактами месяцы? Докопаться до детали, которую я упомянул и не сообразить, что я мог ошибиться и не поправить, а начать обсирать - признак туповатого школоло.

hlamotron
() автор топика
Ответ на: комментарий от hlamotron

Счётчик микросекунд и сама защёлка пакуются в одну атомарную переменную, плюс ко всему.

...шириной 65 битов

Тебе обязательно 64 бита на такты процессора?

Мне? Это ты говорил, что «вы» (коллектив в твоей голове, вероятно) использовали «64-битную атомарную переменную». Еще как минимум 1 бит для лока - вот и 65 бит.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Мне? Это ты говорил, что «вы» (коллектив в твоей голове, вероятно) использовали «64-битную атомарную переменную». Еще как минимум 1 бит для лока - вот и 65 бит.

А чо, я не мог ошибиться?

Тут важнее твоя политика мышления - сразу обосрать, написать про фейспалм, а не обсудить технические детали и не показать собеседнику, на что именно ты возбудился. Ты никогда не сталкивался с тем, что люди ошибаются или что-то плохо помнят? Ты профнепригоден малость, мне кажется - с таким цеплянием к словам, в которых легко могут быть ошибки, ты далеко не уедешь.

hlamotron
() автор топика
Ответ на: комментарий от hlamotron

А чо, я не мог ошибиться?

Ты не мог так ошибиться в описании реализованного решения.

Тут важнее твоя политика мышления - сразу обосрать, написать про фейспалм

Я начал с того, что предложил 2 варианта решения. А то, что описал ты, заслуживает только фейспалма.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ты не мог так ошибиться в описании реализованного решения.

Фигасе, какое знание в чём я могу ошибиться, а в чём нет. Да я и сейчас реализованное решение не помню. Могу вспомнить, если его начать обсуждать.

Я начал с того, что предложил 2 варианта решения. А то, что описал ты, заслуживает только фейспалма.

Речь про твой фейспалм, которым ты так гордишься - ты его так и не сумел конкретизировать, а бегаешь и тыкаешь в меня пальчиком. Начать надо с того, что попытаться сформулировать, на что ты там именно возбудился до фейспалма в моём решении. Зачем мне 2 твоих предложенных решения, если есть моё? Ты же не обосновал, почему оно плохое, хочешь чтобы по фейспалму было телепатически понятно. Учись конструктивно общаться.

hlamotron
() автор топика
Ответ на: комментарий от hlamotron

Хорошо, когда задачи пересекаются, можно поделиться своими решениями :)
Предлагаю вариант не блокирующего чтения/записи для блока данных в шареной памяти разными процессами.
Формат блока данных ShareData:

(ProtectKey1)(ArrayOfData)(ProtectKey2)
Читающих много, все они делают параллельно:

  1. memcpy(&MyData, &ShareData, sizeof(ShareData)); // Тут конечно же можно не один memcpy делать, а несколько, если надо не весь блок шареной памяти. Главное в первую очередь прочитать ProtectKey1 и в последнюю ProtectKey2.
  2. if (ProtectKey1 == ProtectKey2), ArrayOfData валидно, можно использовать хоть целиком, хоть кусочек. Если защитные ключи не равны нет, то ArrayOfData НЕ валидно, на usleep() и на следующее чтение.

Изменяющий один, ему пофиг на всех читающих:

  1. Готовим в своей памяти NewData, таким образом, что бы защитные ключи в новом блоке отличались от того, что доступно читающим.
  2. memcpy(&ShareData, &NewData, sizeof(ShareData)); Заменили блок в шареной памяти.

Достоинства - нет ни мьютексов, ни блокировки по записи, ни обращений к операционке (которые, весьма и весьма накладные), не требуется атомарных переменных.
Что-то похожее, но несколько сумбурно и уже сразу с несколькими блоками, предлагал i36_zubov.

Отдельная благодарность tailgunner, за подсказку проо SysV IPC: man semop и SEM_UNDO, окторые он мне дал в другой ветке, сэкономив мне много времени, желаю ему быть менее категоричным в общении.

Vic
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.