Re: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 25.06.14 23:03
Оценка: 15 (1) +4
Здравствуйте, Lazin, Вы писали:

L>Навелосипедил небольшую бибилотеку синхронизации, с целью проверить кое что — https://github.com/Lazin/Syncope

L>Там есть описание на rungilsh-е, идея очень простая на самом деле. Вместо того, чтобы добавлять мьютексы как поля в объекты, можно использовать внешний массив (пулл) мьютексов. Когда мы хотим залочить объект

Вот сколько занимаюсь многопоточной чертовщиной, ни разу не возникло необходимости выдумывать первентеров дедлоков.
ИМХО все проблемы многопоточного программирования вырастают из выделенного. "Когда мы хотим залочить".
Залочить объект нельзя "хотеть". Его может быть необходимо залочить. Так вот, всякий раз, когда программист сталкивается с необходимостью использования лока того или иного вида, он должен ответить на следующие вопросы:

1. Можно ли обойтись без лока?
2. Какие побочные эффекты у данного лока могуть быть и как их можно избежать?
3. Как минимизировать длительность лока? Желательно до одной операции?

Если ответ на первый вопрос отрицательный, то следует безотлагательно проанализировать архитектуру на предмет минимизации мест коллизий, т.е. мест, где требуется взаимодействие между потоками с блокировками.

Обычно, если следовать этим нехитрым правилам, все межпоточное взаимодействие сводится к коротким эпизодам исключительного чтения или исключительной записи. При этом объект, находящийся под локом, никаких операций сам обычно не совершает и посему организовать дедлок не может.

L>На практике это должно выглядеть как-то так: допустим у нас есть приложение — сервер, оно состоит из сетевой части, которая принимает запросы и новые подключения и (допустим) использует asymmetric lock layer для чтения настроек сетевого стека, white-list допустимых адресов и тд, далее запрос может обрабатываться на урвоне приложения, который использует другой lock layer для того, чтобы защитить доступ к изменяемым данным, далее, в процессе обработки прилоежние должно сохранить данные в БД, там используется еще один lock layer. Блокировки всегда захватываются в порядке, определенном иерархией — server lock layer -> application lock layer -> DB lock layer.


Это какой-то

грубо говоря, когда блокировки разбросаны по всему коду по типу "хотим залочить — лочим, и никаких гвоздей", от дедлоков и прочих напастей ничего уже не спасет, ибо это лишь следствие болезни
www.blinnov.com
Re: Deadlock-prevention алгоритм
От: John1979  
Дата: 25.06.14 10:43
Оценка: 6 (2) +3
Здравствуйте, Lazin, Вы писали:

в одной из предыдущих контор, у нас был кроссплатформенный мутекс (де-факто обертка над виндовым/pthread мутексами). в дебажной версии он (мутекс) регил себя в специальном механизме, который держал внутри себя граф блокировок. этот граф писался на диск, в случае обнаружения дедлока девелопер получал сразу граф — что с чем пересеклось. при этом никаких особых танцев с бубном и особенной организации кода не требуется, думаю что тебе стоит попробовать копнуть в эту сторону.
Re[25]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 30.06.14 11:28
Оценка: 31 (2) +1
Здравствуйте, B0FEE664, Вы писали:

L>>Дедлок — по определению непредусмотренное поведение программы.

BFE>Не более, чем исключение.

Всё же deadlock (незапланированный) это ошибка в коде программы — то есть не исключение, а скорее assert.
Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.
Re[5]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 12:27
Оценка: +3
Здравствуйте, B0FEE664, Вы писали:

BFE>Извините, но вот это "one producer — multiple consumers/multiple producers — one consumer" — простой вырожденный случай. Его вообще не имеет смысла рассматривать.

BFE>Вот, например, описание проекта над которым я сейчас работаю: есть восемь 11 моторов организованные в группы по 3, 3, 5 моторов.

Честно говоря, я не вижу тут врожденной необходимости в многопоточности вообще.
Максимум — один тред для общения с моторами и один тред, выдающий информацию во внешний мир — пульты, удаленные терминалы, веб-интрефейсы и так далее.
Если моторы/группы моторов сидят каждый на своем физическом интрефейсе и работа с ним возможна только в блокирующем режиме (правда, тогда непонятны требования к согласованности — ведь пока мотор не соизволит ответить, вы ему новую команду выдать не сможете, как ни извернитесь), то так и быть, на каждый мотор/группу моторов по отдельному потоку. Вот это и будет тот самый, классический "multiple producers — one consumer".

BFE>Положения некоторых моторов должны быть согласованы и синхронизированы.


Я надеюсь, что вы не напрямую управляете шаговыми моторами из non-realtime OS? Через контроллер ведь, так? Или вы пытаетесь быть этим самым контроллером?
www.blinnov.com
Re[25]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 30.06.14 11:10
Оценка: 15 (1) +1
Здравствуйте, B0FEE664, Вы писали:

L>>Обнаружить дедлок до захода в дедлок невозможно. То есть когда твой детектор сработал, предотвращать уже поздно.

BFE>А я не об этом говорю.

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

BFE>>>>>Чем это ситуация отличается от брошенного исключения? Файл уже удален, заслонка открыта, газ идет...

L>>>>Примерно всем. Исключение понятно как обработать. А дедлок? Что с ним делать?
BFE>>>Обнаружили дедлок — бросили исключение. А как обработать исключение — понятно. В чём проблема-то?

Исключение — это исключительная ситуация, которая, однако, ожидаема и может быть исправлена. Некоторые ковбои пытаются ловить и "обрабатывать" и неисправимые ситуации — вроде конца света памяти или 0x5 (под виндой), но это неправильно, т.к. исправить ситуацию уже нельзя.
Дедлок по определению ситуация не ожидаемая. Если она ожидается, то ее всегда можно исправить в коде и полностью исключить.

L>>Дедлок — по определению непредусмотренное поведение программы.

BFE>Не более, чем исключение.

О майн готт. Не знаю, смеяться или плакать.

L>>Ну бросил ты исключение? Что ты с ним делать будешь, ты ж не предполагал, что дедлок может возникнуть?

BFE>Зато я предпологал, что может быть исключение.
L>>Или ты уже предполагаешь, что дедлоки могут возникнуть и уже написал класс DeadLockException?
BFE>Как вариант.

И что ты будешь с ним делать?

L>>Тогда лучше потратить это время с бОльшей пользой и таки исправить поведение программы, чтобы исключить саму возможность дедлока.

BFE>А где обоснование этого утверждения? Предполагать исключение придётся в любом случае, а значит обработка исключений должна быть в любом случае.

Дедлок возникает в результате не предусмотренной (не замеченной, проигнорированной) во время разработки взаимоблокировки двух или более потоков. Поскольку эта ситуация в принципе не предусматривалась, точнее, была прохлопана ушами, корректно обработать ее в рантаймне не-воз-мож-но.

L>>Тогда нужно проектировать правильно, без вариантов. В общем случае ты отктаться не сможешь.

BFE>В общем случае много чего невозможно.

Проектировать правильно — возможно.
www.blinnov.com
Re[3]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 07:41
Оценка: +2
Здравствуйте, Lazin, Вы писали:

L>>Вот сколько занимаюсь многопоточной чертовщиной, ни разу не возникло необходимости выдумывать первентеров дедлоков.


L>См. первый ответ в этом топике. Меня интересуют такие алгоритмы, а вовсе не то, что они не нужны. Т.е. все то, что идет дальше, это немного оффтопик.


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

L>>ИМХО все проблемы многопоточного программирования вырастают из выделенного. "Когда мы хотим залочить".

L>>Залочить объект нельзя "хотеть". Его может быть необходимо залочить. Так вот, всякий раз, когда программист сталкивается с необходимостью использования лока того или иного вида, он должен ответить на следующие вопросы:

L>Придрался к формулировке. Понятно, что нет смысла лочить то, что лочить не нужно. Я здесь просто способ использования библиотеки старался написать, а вовсе даже не универсальный tutorial по многопоточному программированию.


Нет, не придрался. Вот ты и я понимаем, что нет смысла лочить то, что лочить не нужно. Но ты забываешь о 80% программистов-ковбоев, которые делают это, не включая моск. Дай им библиотеку, которая якобы предотвращает дедлоки, и они залочат все к такой-то матери. И тормознут крутейший суперкомпьютер, а то и создадут-таки дед лок.

L>>1. Можно ли обойтись без лока?

L>>2. Какие побочные эффекты у данного лока могуть быть и как их можно избежать?
L>>3. Как минимизировать длительность лока? Желательно до одной операции?

L>Задав себе такие вопросы, он гарантировано сделает deadlock


Нет. Правильно ответив на эти вопросы, он гарантированно напишет код, в котором дедлок будет невозможен.

L>2-й пункт я бы изменил на проверку корректности порядка захвата локов разными потоками. Если я захватываю лок, а под локом вызываю коллбэк, который захватывает другой лок — я могу получить взаимную блокировку.


Для этого нужно в первую очередь иметь этот другой лок. Два лока подряд означает, что в одно и то же время могут взаимодействовать три и более потоков. Это уже очень, очень плохой сигнал и повод к пересмотру архитектуры. Как правило, обычно многопоточные программы можно свести к паттерну "one producer — multiple consumers/multiple producers — one consumer". При этом, за счет того, что все взаимодействие идет лишь между продьюсером и консьюмером, точек взаимодействия потоков обычно мало, а зачастую она всего лишь одна и лок требуется ровно один.

L>Иерархии блокировок, это не то, что я выдумал перепив белого сухого. Вот, например, древняя статья Саттера на эту тему в Dr. Dobbs — http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163


ИМХО это рассуждение на тему поиска решения проблемы, которой не должно существовать. То есть иерархиями блокировок пользоваться можно. Но гораздо лучше сделать так, чтобы в них не было необходимости.

L>Перечисленные тобой 3 пункта не являются достаточными, так как ты не анализируешь resource allocation graph, который при испльзовании блокировок неизбежно возникает.


resource allocation сам по себе не вызовет дедлока. Опустим вырожденные случаи вроде DLLMain

L>>Если ответ на первый вопрос отрицательный, то следует безотлагательно проанализировать архитектуру на предмет минимизации мест коллизий, т.е. мест, где требуется взаимодействие между потоками с блокировками.


L>Это может быть реальность, данная нам в ощущениях. В общем, это становится похоже на разговор о том что нужно делать хорошо и не нужно делать плохо


Ну так если что-то нужно сделать, то это нужно сделать хорошо. Иначе — зачем вообще шевелиться?

L>>Это какой-то


L>Это пример.


Это какой-то пример.

L>>грубо говоря, когда блокировки разбросаны по всему коду по типу "хотим залочить — лочим, и никаких гвоздей", от дедлоков и прочих напастей ничего уже не спасет, ибо это лишь следствие болезни


L>"Я читаю то, что хочу прочитать" Еще раз, это пример. Никто не утверждает, что нужно лочить все подряд. Данная библиотека появилась в результате того, что я придумал новый алгоритм для R/W мьютекса.


А ты оверхед от всего этого фестиваля оценивал уже?
www.blinnov.com
Re[5]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 12:59
Оценка: +2
Здравствуйте, Lazin, Вы писали:

L>>Дело в том, что эти алгоритмы действительно не нужны. Т.к. это ни что иное, как заметание проблемы под ковер.


L>Буржуйский форум программистов, вопрос: у меня есть вот такая вот проблема, ответ: у этой проблемы есть такое решение. Скука.


У любой проблемы есть два решения

L>Пост-советский форум программистов, вопрос: у меня есть вот такая вот проблема, ответ: это вообще не проблема, ты просто делаешь все не правильно, а теперь давай я поучу тебя программировать!


Дело в том, что лучше не создавать ситуаций, в которых возможен дедлок, нежели искать возможность создать детекторы для них.

L>А если серьезно, проблема все же есть, в golang, например, есть deadlock-detector из коробки, он работает для каналов, которые по сути — просто ограниченые межпоточные очереди. Если поискать в каком нибудь поисковике по научным статьям по запросу "deadlock detection", то можно обнаружить, что проблема активно исследуется, следовательно, это нужно.


Наука вообще изучает много чего, но она далеко не всегда изучает то, что реально нужно. Мое мнение — это просто такая удобная для изучения тема, на которой можно зарабатывать PhD, ничего особо не делая.

L>Софт бывает разным, часто, разные части пишут разные люди, или разные команды, или вообще — разные компании. В этих случаях сложно посмотреть на код "с высоты птичьего полета". Довольно очевидно, что когда ты вызываешь какой-то код под локом, этот код может вызывать другой код, который тоже может что-нибудь лочить внутри себя и так далее.


А вот тут — стоп. Когда я вызываю какой-то код под локом, это означает, что у меня есть потенциальное взаимодействие двух потоков. Если оказывается, что этот код может попытаться получить другой лок, это означает, что взаимодействие распространяется на потенциально неограниченное количество потоков. Это уже само по себе нонсенс и означает, что лок рано или поздно выстрелит. И заранее протестировать динамическую систему на гарантированное отсутствие лока в общем случаее невозможно.

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

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

L>>>Придрался к формулировке. Понятно, что нет смысла лочить то, что лочить не нужно. Я здесь просто способ использования библиотеки старался написать, а вовсе даже не универсальный tutorial по многопоточному программированию.

L>>Нет, не придрался. Вот ты и я понимаем, что нет смысла лочить то, что лочить не нужно. Но ты забываешь о 80% программистов-ковбоев, которые делают это, не включая моск. Дай им библиотеку, которая якобы предотвращает дедлоки, и они залочат все к такой-то матери. И тормознут крутейший суперкомпьютер, а то и создадут-таки дед лок.

L>Не нужно относиться к другим так.


Нужно. Во время code review отменяются все женевские соглашения. Во время code review допускается даже харрасмент и геноцид. Иначе и педали газа клинят и самолеты падают и электростанции взрываются. Думаю, в недалеком будущем я напишу книгу об ужасных примерах "многопоточного ковбойского" программирования.

L>Любая книга по многопоточному программированию описывает взаимные блокировки и как правило там говорится о том, что локи в программе должны быть организованы в иерархическую структуру. Я уже писал о том, что по причине того, что моя библиотека прячет мьютексы за своим интерфесом, она должна предоставлять механизм проверки корректности. Т.е. если у нас есть 4 объекта — Foo a, b, c, d; Пользователь может написать код:


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

L>>Нет. Правильно ответив на эти вопросы, он гарантированно напишет код, в котором дедлок будет невозможен.


L>Нужно аргументы приводить, иначе я тоже могу еще раз сказать — "задав себе эти вопросы он гарантировано сделает deadlock"


Какие тебе нужны аргументы? У нас меня , нет, все же у нас есть четкое правило — лок должен быть обоснованным и в цепочке вызовов из-под лока не должно быть других локов.

L>Длительность локов и взаимные блокировки — иррелевантны.


Не всегда.

L>Мы не можем всегда контролировать длительность локов.


Мы всегда можем их оценить.

L>А что если нужно лочить долгую процедуру?


Зачем? В смысле, так ли уж нам надо запускать долгую операцию под локом? Нельзя ли переработать архитектуру так, чтобы этого избежать?

L>А что если она еще и callback на вход получает?


Это уже нонсенс. Запускать асинхронную операцию (колбек как бы намекает) под локом? Зачем?

L>Ты тоже вот эти вот три вопроса себе задашь и все?


Мои вопросы рекурсивны, ибо вот тут

L>Или все же подумаешь о том — не лочит ли мой callback что-нибудь, он же будет вызываться под другим локом, каким будет порядок захвата мьютексов в моей программе в этом случае?


все начинается сначала.

L>>Для этого нужно в первую очередь иметь этот другой лок. Два лока подряд означает, что в одно и то же время могут взаимодействовать три и более потоков. Это уже очень, очень плохой сигнал и повод к пересмотру архитектуры. Как правило, обычно многопоточные программы можно свести к паттерну "one producer — multiple consumers/multiple producers — one consumer". При этом, за счет того, что все взаимодействие идет лишь между продьюсером и консьюмером, точек взаимодействия потоков обычно мало, а зачастую она всего лишь одна и лок требуется ровно один.


L>Вот опять — я говорю о корректности, ты говоришь о других вещах. Но раз ты так хочешь поговорить об этом, то переменную под мьютексом можно вполне рассматривать как ограниченую блокирующую очередь в которую можно положить только один элемент. Есть такой изоморфизм, между мьютексами и передачей сообщений, в параллельном программировании. Ты будешь утверждать, что producer/consumer система должна всегда обходиться одной очередью сообщений?


Их может быть сколько тебе нужно. Для обеспечения корректности работы такой системы достаточно обеспечить отсутствие возможности взаимоблокировки этих очередей.

L>>resource allocation сам по себе не вызовет дедлока. Опустим вырожденные случаи вроде DLLMain


L> resource allocation graph это вот — http://siber.cankaya.edu.tr/OperatingSystems/ceng328/node156.html


Извини. ИМХО это попытка псевдонаучным языком (ака сложно и непонятно) объяснить очевиднейший принцип образования дедлока, который сводится к банальному "цепочка блокировок закольцевалась". Вроде "изучения влияния слабополяризованного монохроматического излучения на изделия из сталепроката".

L>>А ты оверхед от всего этого фестиваля оценивал уже?


L>Пуллы мьютексов тоже вещь не новая, их часто используют, оверхед почти нулевой, вычисление индекса мьютекса — пара asm иснтрукций а дальше все так же как и в случае просто мьютекса, если объектов много, то пулл даже дешевле, так как требует создаваать меньше объектов ядра (меньше жрется nonpaged память ядра) и меньше памяти под сами мьютексы (std mutex это не только объект ядра но и какой-то state и еще padding, всего 64 байта).


А что, хеш без коллизий? Прямо так уж пара инструкций? А где сам граф хранится?

Если же делать, как я обычно делаю, мютексов нужно считанное количество — по одному на каждую точку взаимодействия Один-три на всю систему.
www.blinnov.com
Re[35]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 04.07.14 11:30
Оценка: 5 (1)
BFE>Вообще-то локальной информации не достаточно для обнаружения deadlock'а. Чем тут try_lock поможет?

Если ты знаешь что во время захвата какого-то мьютекса может произойти дедлок, например, в callback-е, то можно захватывать его через try_lock, в некоторых случаях и обрабатывать ошибки. Что-то вроде этого:

Допустим, в приложении два мьютекса — mutex1 и mutex2. Они захватываются всегда именно в таком порядке — сначала mutex1 а потом mutex2, либо сразу mutex2 без захвата mutex1. Теперь представь, что у нас есть такая ф-я:

void do_something() {
    lock_guard<mutex> g(mutex1);
    ...do something under lock...
}


Пользователь должен вызывать ее в контексте, когда ни один лок не захвачен, иначе мы получим deadlock. Но допустим, мы используем эту ф-ю как callback в каком-то коде и там она дергается в другом контексте — в котором уже захвачен mutex2 и допустим. Может быть так, что код действительно должен вызывать callback под локом mutex2, в этом случае туда нельзя передавать именно этот callback. Можно его немного переделать, вот так:

void do_something() {
    YieldBackoff backoff(100/*times*/, /*for*/chrono::milliseconds(100));
    unique_lock<mutex> g(mutex1, defer_lock);
    while(!g.try_lock()) {
        backoff();
    }
    ...do something under lock...
}


Переменная backoff это backoff стратегия, после определенного количества вызовов она выкинет исключение и вызов callback-а завершится исключительной ситуацией а не взаимной блокировкой, что лучше.
Re[5]: Deadlock-prevention алгоритм
От: mike_rs Россия  
Дата: 27.06.14 08:57
Оценка: 1 (1)
Здравствуйте, Lazin, Вы писали:

L>Так как я заменил их на общий пулл мьютексов, то возможна такая ситуация — адреса объектов a и d хешируются на один мьютекс (MA), а адреса объектов b и c — на другой мьютекс — (MB), в этом случае первый поток попытается захватить мьютексы в порядке MA -> MB, а второй в обратном порядке — MB -> MA. Это приведет к взаимной блокировке.


Выделенное — лютый трындец. Сегодня a и b на один мьютекс попали, на следующем старте a и c попадут — это называется happy debugging. Имхо — изначальная идея порочная, все остальное только следствие из бажной концепции.
Re[9]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 02.07.14 06:38
Оценка: 1 (1)
BFE>Ну так не в скорости проблема. Проблема в том, что в разные порты надо посылать команды с разными промежутками времени и ждать ответа тоже с разными интервалами.

Для этого потоки не нужны. Reactor-based model украдена задолго до нас. У вас классический пример сервера (просто взгляните на ваш "одноядерный ARM" как на сервер, клиентами которого выступают моторы и приборные панели).
Re[13]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 08.07.14 00:02
Оценка: 1 (1)
BFE>Debian.

Т.е. у вас УЖЕ БЫЛ механизм epoll, и всё, что вам требовалось — реализовать драйвер мотора/экрана? Чтобы иметь /dev/engine1, /dev/screen1. И дальше пользовать системный epoll.
Re[13]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 09:33
Оценка: +1
Здравствуйте, Lazin, Вы писали:

L>Я не планирую ничего исправлять, у меня в голове такой юзкейс: программист использует библиотеку для создания приложения, вдруг, в каких-нибудь условиях вылезает дедлок, программист делает специальный билд, в котором включен механизм обнаружения взаимных блокировок, запускает приложение, повторяет условия при которых возникала взаимная блокировка, библиотека видит условие возникновение делока, пишет простыню в stderr и вызывает terminate.


Из опыта:

(0.Дедлоки вполне себе ловятся при помощи debugdiag от мелкософта. Да и просто достаточно снять ручной дамп и заблокированные треды как на ладони.)
1.специальный билд слегка меняет тайминги и дедлок не повторяется. Вообще никак. Хоть ты на голову встань.
2.приходит багрепорт из продакшена о стабильном зависоне. Специальный билд у клиента поставить невозможно. Снимаете ручной дамп, видите заблокированный тред и всей конторой не можете понять, как потоки вообще умудрились оказаться в таком состоянии, т.к. этого "не может быть никогда". Это следствие архитектурных просчетов.
3. Находите один лок, чините, создаете три новых. Следствие архитектурных просчетов.

L>>Во время тестирования абсолютно невозможно отловить все возможные варианты, особенно, когда мьютексы создаются на каждый чих. Если детектор выявил Х дедлоков во время тестирования, это вовсе не гарантирует, что нашли их все. Более того, это говорит о том, что код плохо спроектирован с точки зрения межпотокового взаимодействия и скорее всего, в нем притаились другие дедлоки, которые просто ждут своего шанса.


L>В том то и дело, что я свожу сложную проблему — детектор дедлоков для N мьютексов — к простой, детектор дедлоков для LockLayer-ов, которых в типичной программе должно быть один или два


Извини, но это лечение туберкулеза перцовым пластырем.
www.blinnov.com
Re[26]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 12:51
Оценка: -1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

L>>>Дедлок — по определению непредусмотренное поведение программы.

BFE>>Не более, чем исключение.
EP>Всё же deadlock (незапланированный) это ошибка в коде программы — то есть не исключение, а скорее assert.
Нет, это не ошибка, если такое поведение ускоряет выполнение приложения и было предусмотрено заранее.

EP>Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.

Всяко лучше, чем убивать подвисшие нитки извне, как это делают некоторые.
И каждый день — без права на ошибку...
Re[27]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 01.07.14 06:54
Оценка: :)
Здравствуйте, B0FEE664, Вы писали:

L>>>>Дедлок — по определению непредусмотренное поведение программы.

BFE>>>Не более, чем исключение.
EP>>Всё же deadlock (незапланированный) это ошибка в коде программы — то есть не исключение, а скорее assert.
BFE>Нет, это не ошибка, если такое поведение ускоряет выполнение приложения и было предусмотрено заранее.

Нет. Это ошибка. Без исключений. Вдвойне ошибка — оправдывать это ускорением. Причем "ускорение" это весьма умозрительное.
www.blinnov.com
Re[31]: Deadlock-prevention алгоритм
От: Erop Россия  
Дата: 07.07.14 03:55
Оценка: :)
Здравствуйте, B0FEE664, Вы писали:

EP>>2. При необходимости, assert'ы можно включить и в release'е (но нужно помнить о том, что некоторые проверки могут поменять postconditions, например алгоритмическую сложность). Видел подобные assert'ы в release в одной очень популярной программе, с сотнями миллионов установок.

BFE>Количество установок не говорит о качестве когда. Может оттого они и оставили assert'ы, что отладить не смогли.

О качестве кода тут говорит то, как часто из этих миллионов установок assert'ы таки проваливались
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[33]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 07.07.14 12:02
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

EP>>Точнее: вместо раскидывания по всей системе, локализуем низкоуровневый многопоточный код в модуле, который легко протестировать/верифицировать.

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

Не взаимодействие потоков, а использование низкоуровневых примитивов типа mutex, condition_variable, atomic, etc.

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

BFE>>>"в знаменателях повылазили значения близкие к нулю из-за вырожденности системы"
BFE>>>- разве это не баг в программе?
EP>>Если такие значения возникли не из-за ошибки программиста, а например из-за ошибочного ввода (но всё же ожидаемого) — то нет, не баг.
BFE>А разве не является первейшей задачей программиста проверить вводимые значения на валидность?

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

EP>>Но, опять таки, вариант с неким хитрым алгоритмом, который на одних данных дедлочится, а на других нет — это скорее исключение чем правило.

EP>>В то же время, например, можно элементарно придумать ситуацию, в которой плохие входные данные приведут к выходу за границы массива.
BFE>С таким подходом я согласиться никак не могу.

С каким подходом?
Вполне жизненный пример выхода за границы массива из-за плохих данных: в начале файла идут значения некоторых узлов, а в конце ссылки на них в виде индексов, неправильный индекс может выйти за границы массива.
Теперь можно пример хитрого алгоритма, который дедлочится на плохих данных?
Re[33]: Deadlock-prevention алгоритм
От: Erop Россия  
Дата: 11.07.14 05:51
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

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


Мало того, когда мы кидаем и ловим исключение, мы можем дать таки какие-то гарантии на целостность данных.
А если грубо убиваем процесс, то вообще никаких гарантий дать не можем. Даже того, что внутри-программные кэши все сбросились на диск
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[34]: Deadlock-prevention алгоритм
От: Erop Россия  
Дата: 11.07.14 05:55
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Речь-то как раз о том, что исключение вылетевшее из неизвестного состояния далеко не факт что вообще сможет дойти до обработчика на верхнем уровне.


EP>Шанс успешного прибития процесса (находящегося в неизвестном состоянии) намного выше шанса того, что исключение (вылетевшее неизвестно откуда и как) доберётся до обработчика и ничего не сломает по пути.


Это очень зависит от стратегии использования исключений и обработки ошибок в программе...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 25.06.14 09:24
Оценка:
Навелосипедил небольшую бибилотеку синхронизации, с целью проверить кое что — https://github.com/Lazin/Syncope
Там есть описание на rungilsh-е, идея очень простая на самом деле. Вместо того, чтобы добавлять мьютексы как поля в объекты, можно использовать внешний массив (пулл) мьютексов. Когда мы хотим залочить объект — мы берем его адрес, вычисляем простой хэш и используем его для получения мьютекса из пулла, после чего захватываем его. Для того, чтобы это работало без дедлоков, нужно чтобы была подходящая иерархия локов в программе. Вот такой вот массив может использоваться только на одном уровне иерархии.

Выглядит это примерно вот так:
syncope::SymmetricLockLayer lock_layer(STATIC_STRING("layer name"));
...
auto guard = lock_layer.synchronize(&obj1, &obj2, ptr3);
...do anything with obj1, obj2 and ptr3


Все будет залочено до тех пор, пока область видимости переменной guard не закончится. Порядок аргументов в вызове synchronize не важен. Помимо симметричных блокировок, реализованы не симметричные.

syncope::AsymmetricLockLayer lock_layer(STATIC_STRING("layer name"));
...
{   // write access
    auto guard = lock_layer.write_lock(&obj1, &obj2, ptr3);
    ...do anything with obj1, obj2 and ptr3
}
...
{   // read access
    auto guard = lock_layer.read_lock(&obj1);
    ...read obj1
}


read блокировку можно проапгрейдить до write блокировки при желании. Моя реализация R/W Lock сильно смещена в сторону читателей и предназначена для случаев, когда запись происходить на несколько порядков реже чем чтение (например для защиты настроек программы). В этом случае read_lock работает в 2-3 раза быстрее чем pthread_rwlock_rdlock. Также возможно, что pthread_rwlock_rdlock лучше масштабируется с ростом количества ядер/процессоров, чем моя реализация.

Архитектурно, с этой штукой нужно работать следующим образом — все блокировки разделяются на уровни и для каждого уровня создается свой LockLayer (один или несколько). Каждый LockLayer — не рекурсивен, что значит, что вы не можете захватить лок под другим локом одного и того же уровня.
auto guard = lock_layer.synchronize(&obj1);
{
    // inner scope or function call under lock
    auto guard = lock_layer.synchronize(&obj2);
}

тут вы должны просто заранее захватить оба объекта, в противном случае возможны взаимные блокировки.
На практике это должно выглядеть как-то так: допустим у нас есть приложение — сервер, оно состоит из сетевой части, которая принимает запросы и новые подключения и (допустим) использует asymmetric lock layer для чтения настроек сетевого стека, white-list допустимых адресов и тд, далее запрос может обрабатываться на урвоне приложения, который использует другой lock layer для того, чтобы защитить доступ к изменяемым данным, далее, в процессе обработки прилоежние должно сохранить данные в БД, там используется еще один lock layer. Блокировки всегда захватываются в порядке, определенном иерархией — server lock layer -> application lock layer -> DB lock layer.

Теперь собственно вопрос — хочется специальный дефайн, при определении которого, включались бы проверки корректности порядка захвата и освобождения блокировок. Пока не придумал ничего лучше, нежели строить recourse allocation graph в рантайме, во время вызовов lock/unlock, а потом искать в нем циклы. Еще придумал вариант для бедных — можно сделать массив мьютексов из одного элемента, в этом случае, любой некорректное использование библиотеки будет обязательно приводить к ваимной блокировке и можно будет сравнить стектрейсы разных потоков и понять где ошибка. Может есть еще варианты?
Re[2]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 25.06.14 11:15
Оценка:
Здравствуйте, John1979, Вы писали:

J>Здравствуйте, Lazin, Вы писали:


J>в одной из предыдущих контор, у нас был кроссплатформенный мутекс (де-факто обертка над виндовым/pthread мутексами). в дебажной версии он (мутекс) регил себя в специальном механизме, который держал внутри себя граф блокировок. этот граф писался на диск, в случае обнаружения дедлока девелопер получал сразу граф — что с чем пересеклось. при этом никаких особых танцев с бубном и особенной организации кода не требуется, думаю что тебе стоит попробовать копнуть в эту сторону.


А зачем ему писаться на диск?
Re[3]: Deadlock-prevention алгоритм
От: John1979  
Дата: 25.06.14 11:19
Оценка:
Здравствуйте, Lazin, Вы писали:

L>А зачем ему писаться на диск?

чтоб потом можно было натравить dot и увидеть граф
Re[2]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 07:15
Оценка:
L>Вот сколько занимаюсь многопоточной чертовщиной, ни разу не возникло необходимости выдумывать первентеров дедлоков.

См. первый ответ в этом топике. Меня интересуют такие алгоритмы, а вовсе не то, что они не нужны. Т.е. все то, что идет дальше, это немного оффтопик.

L>ИМХО все проблемы многопоточного программирования вырастают из выделенного. "Когда мы хотим залочить".

L>Залочить объект нельзя "хотеть". Его может быть необходимо залочить. Так вот, всякий раз, когда программист сталкивается с необходимостью использования лока того или иного вида, он должен ответить на следующие вопросы:

Придрался к формулировке. Понятно, что нет смысла лочить то, что лочить не нужно. Я здесь просто способ использования библиотеки старался написать, а вовсе даже не универсальный tutorial по многопоточному программированию.

L>1. Можно ли обойтись без лока?

L>2. Какие побочные эффекты у данного лока могуть быть и как их можно избежать?
L>3. Как минимизировать длительность лока? Желательно до одной операции?

Задав себе такие вопросы, он гарантировано сделает deadlock
2-й пункт я бы изменил на проверку корректности порядка захвата локов разными потоками. Если я захватываю лок, а под локом вызываю коллбэк, который захватывает другой лок — я могу получить взаимную блокировку. Иерархии блокировок, это не то, что я выдумал перепив белого сухого. Вот, например, древняя статья Саттера на эту тему в Dr. Dobbs — http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163

Перечисленные тобой 3 пункта не являются достаточными, так как ты не анализируешь resource allocation graph, который при испльзовании блокировок неизбежно возникает.

L>Если ответ на первый вопрос отрицательный, то следует безотлагательно проанализировать архитектуру на предмет минимизации мест коллизий, т.е. мест, где требуется взаимодействие между потоками с блокировками.


Это может быть реальность, данная нам в ощущениях. В общем, это становится похоже на разговор о том что нужно делать хорошо и не нужно делать плохо

L>>На практике это должно выглядеть как-то так: допустим у нас есть приложение — сервер, оно состоит из сетевой части, которая принимает запросы и новые подключения и (допустим) использует asymmetric lock layer для чтения настроек сетевого стека, white-list допустимых адресов и тд, далее запрос может обрабатываться на урвоне приложения, который использует другой lock layer для того, чтобы защитить доступ к изменяемым данным, далее, в процессе обработки прилоежние должно сохранить данные в БД, там используется еще один lock layer. Блокировки всегда захватываются в порядке, определенном иерархией — server lock layer -> application lock layer -> DB lock layer.


L>Это какой-то


Это пример.

L>грубо говоря, когда блокировки разбросаны по всему коду по типу "хотим залочить — лочим, и никаких гвоздей", от дедлоков и прочих напастей ничего уже не спасет, ибо это лишь следствие болезни


"Я читаю то, что хочу прочитать" Еще раз, это пример. Никто не утверждает, что нужно лочить все подряд. Данная библиотека появилась в результате того, что я придумал новый алгоритм для R/W мьютекса. Он требует пулл мьютетксов и не может быть реализован как индивидуальный мьютекс (точнее может, но накладные расходы будут велики). Поэтому я решил сделать такой объект, который бы хранил этот пулл мьютексов и умел бы лочить другие объекты (получился AsymmetricLockLayer). Потом я начал анализировать корректность и пришел к выводу о том, что нужно явно энфорсить правильный порядок использования нескольких уровней блокировки, так как из-за того, что я использую пуллы мьютексов, у меня могут быть конфликты, а конфликты могут приводить к взаимным блокировкам, если пользователь использует отдельный LockLayer как рекурсивный мьютекс, либо он использует несколько LockLayer-ов, но не соблюдает порядок (в одном потоке использует LockLayers в одном порядке, а в другом потоке — в другом порядке), даже если юзер лочит разные объекты. Если бы не этот факт, можно было бы возложить ответственность за корректность порядка захвата мьютексов на юзера. Но у юзера моей библиотеки нет мьютексов в явном виде

Как пллюс — lock hierarchy появляется в коде явным образом и анализ на дедлоки упрощается, так как нужно строить граф захвата ресурсов не для отдельных мьютексов а только для разных уровней (объектов ***LockLayer). Как я уже писал, сейчас у меня можно задать один define и получить deadlock гарантированно, если он возможен — poor man's deadlock prevention algorithm
Re[4]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 09:37
Оценка:
L>>См. первый ответ в этом топике. Меня интересуют такие алгоритмы, а вовсе не то, что они не нужны. Т.е. все то, что идет дальше, это немного оффтопик.
L>Дело в том, что эти алгоритмы действительно не нужны. Т.к. это ни что иное, как заметание проблемы под ковер.

Буржуйский форум программистов, вопрос: у меня есть вот такая вот проблема, ответ: у этой проблемы есть такое решение. Скука.
Пост-советский форум программистов, вопрос: у меня есть вот такая вот проблема, ответ: это вообще не проблема, ты просто делаешь все не правильно, а теперь давай я поучу тебя программировать!

А если серьезно, проблема все же есть, в golang, например, есть deadlock-detector из коробки, он работает для каналов, которые по сути — просто ограниченые межпоточные очереди. Если поискать в каком нибудь поисковике по научным статьям по запросу "deadlock detection", то можно обнаружить, что проблема активно исследуется, следовательно, это нужно. Софт бывает разным, часто, разные части пишут разные люди, или разные команды, или вообще — разные компании. В этих случаях сложно посмотреть на код "с высоты птичьего полета". Довольно очевидно, что когда ты вызываешь какой-то код под локом, этот код может вызывать другой код, который тоже может что-нибудь лочить внутри себя и так далее.

L>>Придрался к формулировке. Понятно, что нет смысла лочить то, что лочить не нужно. Я здесь просто способ использования библиотеки старался написать, а вовсе даже не универсальный tutorial по многопоточному программированию.

L>Нет, не придрался. Вот ты и я понимаем, что нет смысла лочить то, что лочить не нужно. Но ты забываешь о 80% программистов-ковбоев, которые делают это, не включая моск. Дай им библиотеку, которая якобы предотвращает дедлоки, и они залочат все к такой-то матери. И тормознут крутейший суперкомпьютер, а то и создадут-таки дед лок.

Не нужно относиться к другим так. Любая книга по многопоточному программированию описывает взаимные блокировки и как правило там говорится о том, что локи в программе должны быть организованы в иерархическую структуру. Я уже писал о том, что по причине того, что моя библиотека прячет мьютексы за своим интерфесом, она должна предоставлять механизм проверки корректности. Т.е. если у нас есть 4 объекта — Foo a, b, c, d; Пользователь может написать код:

// thread A
{
    lock_layer.synchronize(&a);
    ...
    {
        lock_layer.synchronize(&b);
    }
}
// thread A
{
    lock_layer.synchronize(&c);
    ...
    {
        lock_layer.synchronize(&d);
    }
}


Хороший программист (не ковбой от программирования) будет доумать так: я лочу в одном потоке сначала а, а потом b, а во втором потоке — сначала c а потом d, но здесь нет цикла, так как объекты разные, следовательно дедлок невозможен. Это было бы справедливо, если бы каждому из объектов а b c d соответстовал отдельный мьютекс.
Так как я заменил их на общий пулл мьютексов, то возможна такая ситуация — адреса объектов a и d хешируются на один мьютекс (MA), а адреса объектов b и c — на другой мьютекс — (MB), в этом случае первый поток попытается захватить мьютексы в порядке MA -> MB, а второй в обратном порядке — MB -> MA. Это приведет к взаимной блокировке. Отсюда — запрет на рекурсивные блокировки одним lock layer-ом. Но это сложно контролировать и это не интуитивно, поэтому я хочу сделать механизм, для автоматического обнаружения этой ситуации в дебажном режиме.

L>>>1. Можно ли обойтись без лока?

L>>>2. Какие побочные эффекты у данного лока могуть быть и как их можно избежать?
L>>>3. Как минимизировать длительность лока? Желательно до одной операции?
L>>Задав себе такие вопросы, он гарантировано сделает deadlock
L>Нет. Правильно ответив на эти вопросы, он гарантированно напишет код, в котором дедлок будет невозможен.

Нужно аргументы приводить, иначе я тоже могу еще раз сказать — "задав себе эти вопросы он гарантировано сделает deadlock"
Длительность локов и взаимные блокировки — иррелевантны. Мы не можем всегда контролировать длительность локов. А что если нужно лочить долгую процедуру? А что если она еще и callback на вход получает? Ты тоже вот эти вот три вопроса себе задашь и все? Или все же подумаешь о том — не лочит ли мой callback что-нибудь, он же будет вызываться под другим локом, каким будет порядок захвата мьютексов в моей программе в этом случае?

L>>2-й пункт я бы изменил на проверку корректности порядка захвата локов разными потоками. Если я захватываю лок, а под локом вызываю коллбэк, который захватывает другой лок — я могу получить взаимную блокировку.

L>Для этого нужно в первую очередь иметь этот другой лок. Два лока подряд означает, что в одно и то же время могут взаимодействовать три и более потоков. Это уже очень, очень плохой сигнал и повод к пересмотру архитектуры. Как правило, обычно многопоточные программы можно свести к паттерну "one producer — multiple consumers/multiple producers — one consumer". При этом, за счет того, что все взаимодействие идет лишь между продьюсером и консьюмером, точек взаимодействия потоков обычно мало, а зачастую она всего лишь одна и лок требуется ровно один.

Вот опять — я говорю о корректности, ты говоришь о других вещах. Но раз ты так хочешь поговорить об этом, то переменную под мьютексом можно вполне рассматривать как ограниченую блокирующую очередь в которую можно положить только один элемент. Есть такой изоморфизм, между мьютексами и передачей сообщений, в параллельном программировании. Ты будешь утверждать, что producer/consumer система должна всегда обходиться одной очередью сообщений?

L>>Перечисленные тобой 3 пункта не являются достаточными, так как ты не анализируешь resource allocation graph, который при испльзовании блокировок неизбежно возникает.

L>resource allocation сам по себе не вызовет дедлока. Опустим вырожденные случаи вроде DLLMain

resource allocation graph это вот — http://siber.cankaya.edu.tr/OperatingSystems/ceng328/node156.html

L>>"Я читаю то, что хочу прочитать" Еще раз, это пример. Никто не утверждает, что нужно лочить все подряд. Данная библиотека появилась в результате того, что я придумал новый алгоритм для R/W мьютекса.

L>А ты оверхед от всего этого фестиваля оценивал уже?
Мой R/W мьтекс быстрее R/W мьютекса из pthread на тех нагрузках, для которых он создавался, я же об этом в первом сообщении написал
Пуллы мьютексов тоже вещь не новая, их часто используют, оверхед почти нулевой, вычисление индекса мьютекса — пара asm иснтрукций а дальше все так же как и в случае просто мьютекса, если объектов много, то пулл даже дешевле, так как требует создаваать меньше объектов ядра (меньше жрется nonpaged память ядра) и меньше памяти под сами мьютексы (std mutex это не только объект ядра но и какой-то state и еще padding, всего 64 байта).
Re[4]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 26.06.14 10:28
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Для этого нужно в первую очередь иметь этот другой лок. Два лока подряд означает, что в одно и то же время могут взаимодействовать три и более потоков. Это уже очень, очень плохой сигнал и повод к пересмотру архитектуры. Как правило, обычно многопоточные программы можно свести к паттерну "one producer — multiple consumers/multiple producers — one consumer". При этом, за счет того, что все взаимодействие идет лишь между продьюсером и консьюмером, точек взаимодействия потоков обычно мало, а зачастую она всего лишь одна и лок требуется ровно один.


Извините, но вот это "one producer — multiple consumers/multiple producers — one consumer" — простой вырожденный случай. Его вообще не имеет смысла рассматривать.
Вот, например, описание проекта над которым я сейчас работаю: есть восемь 11 моторов организованные в группы по 3, 3, 5 моторов. Каждая группа моторов может быть в любой момент подключена или отключена физически оператором с помощью кабеля. Каждая группа моторов принимает команды позиционирования мотора и передаёт текущую позицию моторов. Частота обмена информацией с группой в 5 моторов, отличается от частоты обмена информации с группами по 3 мотора. Далее, имеется пуль управления, обмен информацией с которым идёт со своей частотой отличной от частоты обмена информации с моторами. Пульт управления должен отправлять команды моторам и отображать текущую информацию. Пуль может физически отсоединятся оператором в любой момент. Это не всё: есть ещё интернет соединения разного вида, которые эта установка должна поддерживать: web интерфейс отображающий текущую информацию, удалённые пульты управления соединённые по ethernet или по wi-fi, который встроен прямо в установку. Одновременно может быть подключено несколько пультов управления управляющих некоторыми из моторов одновременно и независимо друг от друга. Характерные периоды работы с моторами и с физически подсоединённым пультом составляет десятки миллисекунд. Каждый из пультов управления помимо текущего положения моторов должен отображать выданные команды (моторы имеют свою скорость, причем она разная у разных моторов). Положения некоторых моторов должны быть согласованы и синхронизированы. Давайте, попробуйте нарисовать здесь архитектуру с одним producer'ом или одним consumer'ом, учитывая, что всё это работает на одноядерном арме.
И каждый день — без права на ошибку...
Re[2]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 12:21
Оценка:
Кстати, вот это вот:

L>1. Можно ли обойтись без лока?

...
L>3. Как минимизировать длительность лока? Желательно до одной операции?
...
L>Обычно, если следовать этим нехитрым правилам, все межпоточное взаимодействие сводится к коротким эпизодам исключительного чтения или исключительной записи. При этом объект, находящийся под локом, никаких операций сам обычно не совершает и посему организовать дедлок не может.

ошибочно, в некоторых случаях с точностью до наоборот
Re[3]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 12:28
Оценка:
Здравствуйте, Lazin, Вы писали:

L>ошибочно, в некоторых случаях с точностью до наоборот


Всегда можно прочитать инструкцию и сделать с точностью до наоборот. Какой именно из пунктов ошибочен?
www.blinnov.com
Re[4]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 12:34
Оценка:
L>Всегда можно прочитать инструкцию и сделать с точностью до наоборот. Какой именно из пунктов ошибочен?

практически все процитированное
Re[5]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 12:59
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Всегда можно прочитать инструкцию и сделать с точностью до наоборот. Какой именно из пунктов ошибочен?


L>практически все процитированное


Обоснуй
www.blinnov.com
Re[6]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 26.06.14 13:06
Оценка:
Здравствуйте, landerhigh, Вы писали:

BFE>>Извините, но вот это "one producer — multiple consumers/multiple producers — one consumer" — простой вырожденный случай. Его вообще не имеет смысла рассматривать.

BFE>>Вот, например, описание проекта над которым я сейчас работаю: есть восемь 11 моторов организованные в группы по 3, 3, 5 моторов.

L>Честно говоря, я не вижу тут врожденной необходимости в многопоточности вообще.

L>Максимум — один тред для общения с моторами
Это как, если команды им следует посылать с разной (не кратной) частотой? Если команда не прошла в заданный промежуток времени, то начинать надо с команд инициализации, время выполнение которых существенно превышает необходимую скорость реакции.

L>и один тред, выдающий информацию во внешний мир — пульты, удаленные терминалы, веб-интрефейсы и так далее.

Если он один, то как он устроен? Он весит на ожидании пакета из сети или на ожидании пакета на отправку?

L>Если моторы/группы моторов сидят каждый на своем физическом интрефейсе и работа с ним возможна только в блокирующем режиме

какой режим выберете, такой и будет.

L>(правда, тогда непонятны требования к согласованности — ведь пока мотор не соизволит ответить, вы ему новую команду выдать не сможете, как ни извернитесь),

Т.е. вы хотите сказать, что это вообще не возможно? Если мотор не отвечает, значит он отсоединён. В этом случае остальные части системы должны игнорировать отсоединённое оборудование.

L>то так и быть, на каждый мотор/группу моторов по отдельному потоку.

ура!

L>Вот это и будет тот самый, классический "multiple producers — one consumer".

кто из них consumer, тот кто потребляет команды или тот, кто потребляет измеренные положения моторов?

BFE>>Положения некоторых моторов должны быть согласованы и синхронизированы.

L>Я надеюсь, что вы не напрямую управляете шаговыми моторами из non-realtime OS? Через контроллер ведь, так? Или вы пытаетесь быть этим самым контроллером?
Я пишу/читаю в несколько последовательных портов, точнее 4 — три группы моторов + пульт управления. Со стороны моторов и пульта стоят контролёры, которые ими управляют.
И это не realtime комплекс, это near real-time комплекс.
И каждый день — без права на ошибку...
Re[7]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 13:29
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Это как, если команды им следует посылать с разной (не кратной) частотой? Если команда не прошла в заданный промежуток времени, то начинать надо с команд инициализации, время выполнение которых существенно превышает необходимую скорость реакции.


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

L>>и один тред, выдающий информацию во внешний мир — пульты, удаленные терминалы, веб-интрефейсы и так далее.

BFE>Если он один, то как он устроен? Он весит на ожидании пакета из сети или на ожидании пакета на отправку?

Может, мне и систему за тебя сразу всю написать? Про неблокирующие сокеты слышал?

L>>(правда, тогда непонятны требования к согласованности — ведь пока мотор не соизволит ответить, вы ему новую команду выдать не сможете, как ни извернитесь),

BFE>Т.е. вы хотите сказать, что это вообще не возможно? Если мотор не отвечает, значит он отсоединён. В этом случае остальные части системы должны игнорировать отсоединённое оборудование.

А если мотор отвечает, но с бОльшей задержкой по сравнению с другими моторами?

L>>то так и быть, на каждый мотор/группу моторов по отдельному потоку.

BFE>ура!

чего ура-то? Максимум — один тред на один порт.

L>>Вот это и будет тот самый, классический "multiple producers — one consumer".

BFE>кто из них consumer, тот кто потребляет команды или тот, кто потребляет измеренные положения моторов?

Один консьюмер — эта та часть, которая связывает этот винигрет с внешним миром. Потребляет положения, иногда отдает команды. Потребитель услуг моторов.

L>>Я надеюсь, что вы не напрямую управляете шаговыми моторами из non-realtime OS? Через контроллер ведь, так? Или вы пытаетесь быть этим самым контроллером?

BFE>Я пишу/читаю в несколько последовательных портов, точнее 4 — три группы моторов + пульт управления. Со стороны моторов и пульта стоят контролёры, которые ими управляют.

Какие-то странные у вас контроллеры. Зачем им постоянная частота обмена с мастером? Что за производитель/модель?

Опять же, все это не имеет отношения к обсуждаемой теме. Ты можешь создать отдельный тред на мотор/группу моторов/порт, и зачастую это банально удобно с точки зрения организации алгоритма. Но мест взаимодействия потоков у тебя... ровно одно. Там, где фронт-энд этого фестиваля обменивается данными с бек-эндом. Причем, учитывая характер данных, тут может локов вообще не понадобится. Все. Вариантов организовать дед лок просто нет.
www.blinnov.com
Re[6]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 13:32
Оценка:
L>1. Можно ли обойтись без лока?
Не нужно стараться уменьшать количество используемых мьютексов, мьютексы дешевы, захватываются и освобождаются очень быстро (uncontended lock/unlock мьютекса, находящегося в кэше это десяток наносекунд, если мне память не изменяет, деление 2х интов — дороже), если какой-то код/структуру данных можно сделать потокобезопасной, то часто так и нужно сделать, а потом уже узкие места, в которых может оказаться, что следующее неверно:
L>3. Как минимизировать длительность лока? Желательно до одной операции?
Uncontended lock — очень дешев, contended lock — дорог. Взаимодействие между ядрами процессора в рамках сокета, это величина порядка 50ns, если сокетов несколько — то в несколько раз больше. Если код в hot path захватывает лок, то возможны такие варианты:
1. contended lock (мьютекс используется несколькими потоками и захват освобождение приводят к возникновению cache coherency трафика), латентность большая, захватывая лок на короткое время мы плаим за это тем, что вынуждены захватывать его часто (fine grained locking), намного лучше скомбинировать несколько критических секций в одну и захватывать лок на более длительное время (coarse grained lock). Пример — мы обрабаываем данные в цикле, лок захватывается на каждой итерации на короткое время (несколько сотен наносекунд). Можно захватывать его не на каждой итерации а на каждые N итераций. С ростом N мы будем платить все меньше и меньше за синхронизацию, но при привышении определенного порога — это может начать сказываться на производительности отрицательно.
2. uncontended lock (мьютекс используется эксклюзивно, одним потоком) захватывать мьютекс часто — сравнительно не дорого, но если мы будем его также захватывать на более длительный интервал времени, то ничего страшного не произойдет, так как поток большую часть времени все равно владеет им в единоличном порядке.
Re[6]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 13:44
Оценка:
(offtopic: очень сложно цитировать человека с ником, начинающимся с той же буквы что и твой)

L>А вот тут — стоп. Когда я вызываю какой-то код под локом, это означает, что у меня есть потенциальное взаимодействие двух потоков. Если оказывается, что этот код может попытаться получить другой лок, это означает, что взаимодействие распространяется на потенциально неограниченное количество потоков. Это уже само по себе нонсенс и означает, что лок рано или поздно выстрелит. И заранее протестировать динамическую систему на гарантированное отсутствие лока в общем случаее невозможно.


Можно, если правильно ее проектировать. (тут должен быть очередной рассказ про иерархии блокировок)
Еще раз — локов не должно быть мало, их должно быть столько, сколько нужно. Впялить мьютекс в каждый объект — нормальная практика. Плохо не тогда, когда мьютексов слишком много, плохо становится тогда, когда их мало и они постоянно лочатся/анлочатся из нескольких ядер. Это называется lock contention. Плохо когда локи не организованы в иерархию и могут создать цикл (решается правильным проектированием). Все.

L>Я одно время копал в этом направлении. Например, если у меня есть подозрение, что некий код содержит потенциальный дедлок, я могу специально организовать юнит-тест, который его вызовет. Но, в общем случае невозможно создать автотест, который будет гарантировать отсутствие дедлока.


В общем случае — нет, это сводится к "halting problem". Но у меня не общий случай, я спрашивал о том, как динамически проверить отсутствие циклов на уровне ***LockLayer-ов.

L>Еще раз — я всегда пытаюсь сводить межпоточное взаимодействие исключительно к однонаправленному чтению или записи. Никаких графов вызовов, особенно в другие потоки, из-под локов быть не должно. Мое мнение — наличие такого графа есть признак ошибки проектирования.


"Пытаюсь" — подходящее для этого слово, жизнь многогранна

L>Извини. ИМХО это попытка псевдонаучным языком (ака сложно и непонятно) объяснить очевиднейший принцип образования дедлока, который сводится к банальному "цепочка блокировок закольцевалась". Вроде "изучения влияния слабополяризованного монохроматического излучения на изделия из сталепроката".


Я пытаюсь о конкретной задаче разговаривать, resource allocation graph — вполне конкретная вещь, которую используют для анализа этих самых "цепочек блокировок".

L>А что, хеш без коллизий? Прямо так уж пара инструкций? А где сам граф хранится?

Я же говорил уже что хэш с коллизиями. Разные объекты могут хэшироваться на один и тот же мьютекс, отсюда требование к отсутствию рекурсинвых блокировок.

L>Если же делать, как я обычно делаю, мютексов нужно считанное количество — по одному на каждую точку взаимодействия Один-три на всю систему.

Ты сам себе противоречишь, то мьютексов должно быть мало — 1-3 (coarse grained locking), то они должны захватываться часто (fine grained locking). Может у меня сервер с 24-мя ядрами и гипертрейдингом, один-три лока на всю систему нужно использовать?
Re[7]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 13:47
Оценка:
L>>А где сам граф хранится?
Пока нигде. Я думал хранить отдельные ноды графа в TLS и строить его динамически, но пока не придумал как конкретно это делать.
Re[7]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 13:49
Оценка:
L>Не нужно стараться уменьшать количество используемых мьютексов, мьютексы дешевы, захватываются и освобождаются очень быстро (uncontended lock/unlock мьютекса, находящегося в кэше это десяток наносекунд, если мне память не изменяет, деление 2х интов — дороже), если какой-то код/структуру данных можно сделать потокобезопасной, то часто так и нужно сделать, а потом уже узкие места, в которых может оказаться, что следующее неверно:

а потом уже оптимизировать узкие места
Re[7]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 13:51
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>1. Можно ли обойтись без лока?

L>Не нужно стараться уменьшать количество используемых мьютексов,

Нужно. Больше количество мьютексов дают банально больше возможностей для возникновения лока.

L>мьютексы дешевы, захватываются и освобождаются очень быстро (uncontended lock/unlock мьютекса, находящегося в кэше это десяток наносекунд, если мне память не изменяет, деление 2х интов — дороже), если какой-то код/структуру данных можно сделать потокобезопасной, то часто так и нужно сделать, а потом уже узкие места, в которых может оказаться, что следующее неверно:


L>>3. Как минимизировать длительность лока? Желательно до одной операции?


L>Uncontended lock — очень дешев, contended lock — дорог.


Совершенно верно. Но все же рассматривать дороговизну этого лока как такового — это все равно, что жалеть гвоздей на постройку дома. Основной негативный эффект от contended lock заключается вовсе не во внутренней кухне мьютекса, а в вынужденном простое одного или нескольких потоков. Поэтому и нужно стремиться к тому, чтобы максимально меньшее время проводить под локом, чтобы снизить вероятность contended lock.

L>Пример — мы обрабаываем данные в цикле, лок захватывается на каждой итерации на короткое время (несколько сотен наносекунд). Можно захватывать его не на каждой итерации а на каждые N итераций. С ростом N мы будем платить все меньше и меньше за синхронизацию, но при привышении определенного порога — это может начать сказываться на производительности отрицательно.


А почему нельзя одним махом передать сразу все данные для обработки в поток? Один swap и никаких гвоздей. Зачем лочить на каждой итерации? Взял данные, обработал, как закончил — сообщил. Какой юзкейс?

L>2. uncontended lock (мьютекс используется эксклюзивно, одним потоком) захватывать мьютекс часто — сравнительно не дорого, но если мы будем его также захватывать на более длительный интервал времени, то ничего страшного не произойдет, так как поток большую часть времени все равно владеет им в единоличном порядке.


Мьютекс, используемый лишь одним потоком — это какой-то pkunzip.zip.
www.blinnov.com
Re[8]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 14:07
Оценка:
L>Нужно. Больше количество мьютексов дают банально больше возможностей для возникновения лока.
Какбэ наоборот же чем больше мьютексов, тем меньше шансов что какие-то из них образуют цикл. Чисто с точки зрения теории вероятностей Когда все используют 2 мьютекса, то любой захват в неверном порядке будет приводить к реальному deadlock-у.

L>>мьютексы дешевы, захватываются и освобождаются очень быстро (uncontended lock/unlock мьютекса, находящегося в кэше это десяток наносекунд, если мне память не изменяет, деление 2х интов — дороже), если какой-то код/структуру данных можно сделать потокобезопасной, то часто так и нужно сделать, а потом уже узкие места, в которых может оказаться, что следующее неверно:


L>>>3. Как минимизировать длительность лока? Желательно до одной операции?


L>>Uncontended lock — очень дешев, contended lock — дорог.


L>Совершенно верно. Но все же рассматривать дороговизну этого лока как такового — это все равно, что жалеть гвоздей на постройку дома. Основной негативный эффект от contended lock заключается вовсе не во внутренней кухне мьютекса, а в вынужденном простое одного или нескольких потоков. Поэтому и нужно стремиться к тому, чтобы максимально меньшее время проводить под локом, чтобы снизить вероятность contended lock.


Ну это может быть, например, очередь, в которую поток кладет что-нибудь, для обработки другими потоками. Можно на каждое добавление лочить, можно залочить один раз на длительное время и добавить множество элементов.

L>>2. uncontended lock (мьютекс используется эксклюзивно, одним потоком) захватывать мьютекс часто — сравнительно не дорого, но если мы будем его также захватывать на более длительный интервал времени, то ничего страшного не произойдет, так как поток большую часть времени все равно владеет им в единоличном порядке.


L>Мьютекс, используемый лишь одним потоком — это какой-то pkunzip.zip.


Часто используемый в параллельном программировании паттерн — в fast path захватывается какой-нибудь спин лок, который большую часть времени захватывается только одним потоком, но периодически что-нибудь происходит и какой-нибудь другой поток может залочить этот мьютекс и вмешаться в работу первого потока. Task stealing scheduler может так работать, например. Это нужно для того, чтобы захват лока был почти бесплатным большую часть времени, но при этом нужно иметь возможность обращаться к данным их других потоков иногда.
Re[7]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 14:07
Оценка:
Здравствуйте, Lazin, Вы писали:

L>(offtopic: очень сложно цитировать человека с ником, начинающимся с той же буквы что и твой)




L>>А вот тут — стоп. Когда я вызываю какой-то код под локом, это означает, что у меня есть потенциальное взаимодействие двух потоков. Если оказывается, что этот код может попытаться получить другой лок, это означает, что взаимодействие распространяется на потенциально неограниченное количество потоков. Это уже само по себе нонсенс и означает, что лок рано или поздно выстрелит. И заранее протестировать динамическую систему на гарантированное отсутствие лока в общем случаее невозможно.


L>Можно, если правильно ее проектировать. (тут должен быть очередной рассказ про иерархии блокировок)


Которые не нужны, если правильно ее проектировать

L>Еще раз — локов не должно быть мало, их должно быть столько, сколько нужно. Впялить мьютекс в каждый объект — нормальная практика.


Это как раз ненормальная, я бы даже сказал, порочная, практика. Вот впаяй мьютекс в map, только чур так, чтобы все алгоритмы работали.

L> Плохо не тогда, когда мьютексов слишком много, плохо становится тогда, когда их мало и они постоянно лочатся/анлочатся из нескольких ядер. Это называется lock contention. Плохо когда локи не организованы в иерархию и могут создать цикл (решается правильным проектированием). Все.


Иными словами, плохо, когда на архитектуру забили. О чем я и говорю

L>>Я одно время копал в этом направлении. Например, если у меня есть подозрение, что некий код содержит потенциальный дедлок, я могу специально организовать юнит-тест, который его вызовет. Но, в общем случае невозможно создать автотест, который будет гарантировать отсутствие дедлока.


L>В общем случае — нет, это сводится к "halting problem". Но у меня не общий случай, я спрашивал о том, как динамически проверить отсутствие циклов на уровне ***LockLayer-ов.


А я же продолжаю утверждать, что эта проблема не стоит того, чтобы ее решать, т.к. гораздо выгоднее решить более высокоуровневую проблему.

L>>Еще раз — я всегда пытаюсь сводить межпоточное взаимодействие исключительно к однонаправленному чтению или записи. Никаких графов вызовов, особенно в другие потоки, из-под локов быть не должно. Мое мнение — наличие такого графа есть признак ошибки проектирования.


L>"Пытаюсь" — подходящее для этого слово, жизнь многогранна


"Пытаюсь" тут скорее потому, что во время code review приходится брать тупые твердые предметы в руки.

L>>Извини. ИМХО это попытка псевдонаучным языком (ака сложно и непонятно) объяснить очевиднейший принцип образования дедлока, который сводится к банальному "цепочка блокировок закольцевалась". Вроде "изучения влияния слабополяризованного монохроматического излучения на изделия из сталепроката".


L>Я пытаюсь о конкретной задаче разговаривать, resource allocation graph — вполне конкретная вещь, которую используют для анализа этих самых "цепочек блокировок".


Предлагаю оставить анализ ученым. Они умные, пусть статьи пишут. А нам код потом поддерживать.

L>>А что, хеш без коллизий? Прямо так уж пара инструкций? А где сам граф хранится?

L>Я же говорил уже что хэш с коллизиями. Разные объекты могут хэшироваться на один и тот же мьютекс, отсюда требование к отсутствию рекурсинвых блокировок.

Вот, уже создал себе проблему.

L>>Если же делать, как я обычно делаю, мютексов нужно считанное количество — по одному на каждую точку взаимодействия Один-три на всю систему.

L>Ты сам себе противоречишь, то мьютексов должно быть мало — 1-3 (coarse grained locking),

При правильном проектировании никакого coarse grained locking не возникает. Вообще, обычно в правильной многопоточной системе много потоков работают, один ждет внешнего события или результатов. Взаимодействие сводится к коротким эпизодам обмена данными "выполни мне задачу Х" и "вот результаты работы задачи Y"

L>то они должны захватываться часто (fine grained locking). Может у меня сервер с 24-мя ядрами и гипертрейдингом, один-три лока на всю систему нужно использовать?

Если интрефейс, по которому поступают/уходят данные, один, то все в любом случае сведется к одному локу
www.blinnov.com
Re[8]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 26.06.14 14:41
Оценка:
Здравствуйте, landerhigh, Вы писали:

BFE>>Это как, если команды им следует посылать с разной (не кратной) частотой? Если команда не прошла в заданный промежуток времени, то начинать надо с команд инициализации, время выполнение которых существенно превышает необходимую скорость реакции.

L>А в чем проблема? У тебя производительность даже скромного ARMa на десять порядков превышает скорость обмена по последовательному порту. Один тред тут запросто может обслужить несколько портов, а латентность на фоне скорости обмена по интрефейсу будет просто незаметна.
Ну так не в скорости проблема. Проблема в том, что в разные порты надо посылать команды с разными промежутками времени и ждать ответа тоже с разными интервалами.

L>>>и один тред, выдающий информацию во внешний мир — пульты, удаленные терминалы, веб-интрефейсы и так далее.

BFE>>Если он один, то как он устроен? Он весит на ожидании пакета из сети или на ожидании пакета на отправку?
L>Может, мне и систему за тебя сразу всю написать? Про неблокирующие сокеты слышал?
Неблокирующие сокеты тут ничего не меняют. Что делает нитка, когда нечего посылать и нечего принимать?

L>>>(правда, тогда непонятны требования к согласованности — ведь пока мотор не соизволит ответить, вы ему новую команду выдать не сможете, как ни извернитесь),

BFE>>Т.е. вы хотите сказать, что это вообще не возможно? Если мотор не отвечает, значит он отсоединён. В этом случае остальные части системы должны игнорировать отсоединённое оборудование.
L>А если мотор отвечает, но с бОльшей задержкой по сравнению с другими моторами?
А так и есть. Частота обмена командами разная с разными моторами.

L>>>то так и быть, на каждый мотор/группу моторов по отдельному потоку.

BFE>>ура!
L>чего ура-то? Максимум — один тред на один порт.
Это уже 4 потока. И ещё минимум один для взаимодействия с сетью. Но замечу, что этот, пятый поток, ничего не делает, если нет никаких установленных сетевых соединений.

L>>>Вот это и будет тот самый, классический "multiple producers — one consumer".

BFE>>кто из них consumer, тот кто потребляет команды или тот, кто потребляет измеренные положения моторов?
L>Один консьюмер — эта та часть, которая связывает этот винигрет с внешним миром. Потребляет положения, иногда отдает команды. Потребитель услуг моторов.
Ок. С помощью каких объектов происходит этот обмен?

L>>>Я надеюсь, что вы не напрямую управляете шаговыми моторами из non-realtime OS? Через контроллер ведь, так? Или вы пытаетесь быть этим самым контроллером?

BFE>>Я пишу/читаю в несколько последовательных портов, точнее 4 — три группы моторов + пульт управления. Со стороны моторов и пульта стоят контролёры, которые ими управляют.
L>Какие-то странные у вас контроллеры. Зачем им постоянная частота обмена с мастером?
Самому интересно. Но что есть, то есть.

L>Что за производитель/модель?

Это самодельные контроллеры, которые разрабатывают простые французские инженеры. Убедить их, что надо делать что-нибудь более удобное в обращении мне не удалось.

L>Опять же, все это не имеет отношения к обсуждаемой теме. Ты можешь создать отдельный тред на мотор/группу моторов/порт, и зачастую это банально удобно с точки зрения организации алгоритма. Но мест взаимодействия потоков у тебя... ровно одно. Там, где фронт-энд этого фестиваля обменивается данными с бек-эндом. Причем, учитывая характер данных, тут может локов вообще не понадобится. Все. Вариантов организовать дед лок просто нет.


Ага. т.е. вы искусственно вводите одну нитку, которая прокачивает через себя все данные. Я правильно понимаю?
И каждый день — без права на ошибку...
Re[8]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 26.06.14 15:07
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>1. Можно ли обойтись без лока?

L>>Не нужно стараться уменьшать количество используемых мьютексов,
L>Нужно. Больше количество мьютексов дают банально больше возможностей для возникновения лока.

Т.е. если довести это размышление до предела, то на одну программу нужно не более одного мютекса? Идея в этом состоит?
И каждый день — без права на ошибку...
Re[9]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 15:13
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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


Не вижу проблемы. С этим даже банальный таймер из asio справится. Внутри одного потока.

L>>>>и один тред, выдающий информацию во внешний мир — пульты, удаленные терминалы, веб-интрефейсы и так далее.

BFE>>>Если он один, то как он устроен? Он весит на ожидании пакета из сети или на ожидании пакета на отправку?
L>>Может, мне и систему за тебя сразу всю написать? Про неблокирующие сокеты слышал?
BFE>Неблокирующие сокеты тут ничего не меняют. Что делает нитка, когда нечего посылать и нечего принимать?

Мультики смотрит. Ждет эвента. Пусть что хочет, то и делает.

L>>А если мотор отвечает, но с бОльшей задержкой по сравнению с другими моторами?

BFE>А так и есть. Частота обмена командами разная с разными моторами.

Ну и каким образом софт может скомпенсировать это, если требуется полная синхронизация моторов?

BFE>Это уже 4 потока. И ещё минимум один для взаимодействия с сетью. Но замечу, что этот, пятый поток, ничего не делает, если нет никаких установленных сетевых соединений.


Какая разница? Это стандартный паттерн при разработке серверов.

L>>>>Вот это и будет тот самый, классический "multiple producers — one consumer".

BFE>>>кто из них consumer, тот кто потребляет команды или тот, кто потребляет измеренные положения моторов?
L>>Один консьюмер — эта та часть, которая связывает этот винигрет с внешним миром. Потребляет положения, иногда отдает команды. Потребитель услуг моторов.
BFE>Ок. С помощью каких объектов происходит этот обмен?

Какие будет удобно использовать. Или ты имеешь в виду примитивы синхронизации?

L>>Какие-то странные у вас контроллеры. Зачем им постоянная частота обмена с мастером?

BFE>Самому интересно. Но что есть, то есть.

L>>Что за производитель/модель?

BFE>Это самодельные контроллеры, которые разрабатывают простые французские инженеры. Убедить их, что надо делать что-нибудь более удобное в обращении мне не удалось.

А, французы такие затейники

L>>Опять же, все это не имеет отношения к обсуждаемой теме. Ты можешь создать отдельный тред на мотор/группу моторов/порт, и зачастую это банально удобно с точки зрения организации алгоритма. Но мест взаимодействия потоков у тебя... ровно одно. Там, где фронт-энд этого фестиваля обменивается данными с бек-эндом. Причем, учитывая характер данных, тут может локов вообще не понадобится. Все. Вариантов организовать дед лок просто нет.


BFE>Ага. т.е. вы искусственно вводите одну нитку, которая прокачивает через себя все данные. Я правильно понимаю?


Не искусственно. Она даже логически одна, т.к. все данные рано или поздно должны оказаться на одной мнемосхеме.
www.blinnov.com
Re[9]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 15:22
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>Нужно. Больше количество мьютексов дают банально больше возможностей для возникновения лока.


BFE>Т.е. если довести это размышление до предела, то на одну программу нужно не более одного мютекса? Идея в этом состоит?


Нет. Нужно всего лишь гарантировать, что ни при каких условиях не возникает ситуации, когда в одном стеке вызовов присутствует более одной блокировки.
www.blinnov.com
Re[9]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 15:25
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Нужно. Больше количество мьютексов дают банально больше возможностей для возникновения лока.

L>Какбэ наоборот же чем больше мьютексов, тем меньше шансов что какие-то из них образуют цикл. Чисто с точки зрения теории вероятностей

На самом деле вероятность больше. т.к. вероятность лока между двумя любыми мьютекасами не зависит от наличия других мьютекосв. Умножай вероятности сам

L>Когда все используют 2 мьютекса, то любой захват в неверном порядке будет приводить к реальному deadlock-у.


А когда один? А если захват их в одной ветке невозможен?

L>>>>3. Как минимизировать длительность лока? Желательно до одной операции?


L>>>Uncontended lock — очень дешев, contended lock — дорог.


L>>Совершенно верно. Но все же рассматривать дороговизну этого лока как такового — это все равно, что жалеть гвоздей на постройку дома. Основной негативный эффект от contended lock заключается вовсе не во внутренней кухне мьютекса, а в вынужденном простое одного или нескольких потоков. Поэтому и нужно стремиться к тому, чтобы максимально меньшее время проводить под локом, чтобы снизить вероятность contended lock.


L>Ну это может быть, например, очередь, в которую поток кладет что-нибудь, для обработки другими потоками. Можно на каждое добавление лочить, можно залочить один раз на длительное время и добавить множество элементов.


Это стандартная задача. Если есть необходимость добавлять сразу много элементов для обработки, можно предусмотреть алгоритм для передачи целого контейнера с помощью одного swap.


void AddData(container&& _data)
{
   Lock(m_Lock);
   m_pendingData.append(std::move(_data))
}



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

L>>>2. uncontended lock (мьютекс используется эксклюзивно, одним потоком) захватывать мьютекс часто — сравнительно не дорого, но если мы будем его также захватывать на более длительный интервал времени, то ничего страшного не произойдет, так как поток большую часть времени все равно владеет им в единоличном порядке.


L>>Мьютекс, используемый лишь одним потоком — это какой-то pkunzip.zip.


L>Часто используемый в параллельном программировании паттерн — в fast path захватывается какой-нибудь спин лок, который большую часть времени захватывается только одним потоком, но периодически что-нибудь происходит и какой-нибудь другой поток может залочить этот мьютекс и вмешаться в работу первого потока. Task stealing scheduler может так работать, например. Это нужно для того, чтобы захват лока был почти бесплатным большую часть времени, но при этом нужно иметь возможность обращаться к данным их других потоков иногда.


ты же сам только что сказал, что неконкурентный лок — дешев.
www.blinnov.com
Re[10]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 26.06.14 15:36
Оценка:
Здравствуйте, landerhigh, Вы писали:

оффтопик поскипан, вернёмся к сути
BFE>>Ага. т.е. вы искусственно вводите одну нитку, которая прокачивает через себя все данные. Я правильно понимаю?
L>Не искусственно. Она даже логически одна, т.к. все данные рано или поздно должны оказаться на одной мнемосхеме.
Ну как же не искусственно? Все нитки могут общаться друг с другом напрямую. Зачем этот оверхед с "логически верной" ниткой?

Вот сами же пишите
Автор: landerhigh
Дата: 26.06.14
, что

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

И каждый день — без права на ошибку...
Re[10]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 26.06.14 15:43
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Нужно. Больше количество мьютексов дают банально больше возможностей для возникновения лока.

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

Что Lazin и пытается сделать с помощью библиотеки из первого поста. А с помощью чего вы собираетесь это гарантировать?
И каждый день — без права на ошибку...
Re[10]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 15:52
Оценка:
L>На самом деле вероятность больше. т.к. вероятность лока между двумя любыми мьютекасами не зависит от наличия других мьютекосв. Умножай вероятности сам

Я думал что ты говоришь об общем количестве локов, а не о количестве локов в стеке вызовов. С тем, что оптимально, когда у тебя в стеке вызовов всегда только один лок я согласен, это очевидно. Но все же это не всегда очевидно, поэтому ответ — "нужно делать всегда так, чтобы в стеке вызовов был только один лок" — не засчитывается

L>>Ну это может быть, например, очередь, в которую поток кладет что-нибудь, для обработки другими потоками. Можно на каждое добавление лочить, можно залочить один раз на длительное время и добавить множество элементов.

L>Это стандартная задача. Если есть необходимость добавлять сразу много элементов для обработки, можно предусмотреть алгоритм для передачи целого контейнера с помощью одного swap.
L>
L>void AddData(container&& _data)
L>{
L>   Lock(m_Lock);
L>   m_pendingData.append(std::move(_data))
L>}
L>

L>А еще гибче передавать не данные, а таски. Тоже один своп, но данных можно сразу передать сколько угодно.

А как же балансировка нагрузки между ядрами? Может же быть такое, что один поток получил намного больше работы чем остальные (и в случае таска и в случае очереди с данными)

Можно привести другой пример, ты ищешь кратчайший путь между двумя узлами в графе, один поток начинает выполнять алгоритм дейкстры из начального узла, а другой — из конечного, пока не встретятся или один из потоков не дойдет до своей цели. Коммуницируют потоки через общую память. Можно захватывать лок часто, при каждом апдейте (посмотрели ноду, посчитали вес, записали в массив), как ты советуешь, а можно посчитать сразу для N-ого количества нод и обновить все за один захват мьютекса. В данном примере у нас contended lock, но при правильном выборе параметров можно все равно получить хороший прирост производительности. Если захватывать на короткий срок для каждой ноды, то получатся тормоза, так как мы захватывать/разхватывать будем дольше чем ходить по графу. Примеров много, на самом деле. Coarse grained vs fine grained это крайности, нам же всегда нужен какой-то подходящий для того или иного случая tradeoff.

L>>>Мьютекс, используемый лишь одним потоком — это какой-то pkunzip.zip.


L>>Часто используемый в параллельном программировании паттерн — в fast path захватывается какой-нибудь спин лок, который большую часть времени захватывается только одним потоком, но периодически что-нибудь происходит и какой-нибудь другой поток может залочить этот мьютекс и вмешаться в работу первого потока. Task stealing scheduler может так работать, например. Это нужно для того, чтобы захват лока был почти бесплатным большую часть времени, но при этом нужно иметь возможность обращаться к данным их других потоков иногда.


L>ты же сам только что сказал, что неконкурентный лок — дешев.


Это я привел пример мьютекса, использующегося одним потоком и объяснил зачем это может быть нужно.
Re[11]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 26.06.14 15:54
Оценка:
L>Но все же это не всегда очевидно, поэтому ответ — "нужно делать всегда так, чтобы в стеке вызовов был только один лок" — не засчитывается

:s/очевидно/возможно
Re[11]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 20:43
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>оффтопик поскипан, вернёмся к сути

BFE>>>Ага. т.е. вы искусственно вводите одну нитку, которая прокачивает через себя все данные. Я правильно понимаю?
L>>Не искусственно. Она даже логически одна, т.к. все данные рано или поздно должны оказаться на одной мнемосхеме.
BFE>Ну как же не искусственно? Все нитки могут общаться друг с другом напрямую. Зачем этот оверхед с "логически верной" ниткой?

во-первых, ниткам, которые отвечают за управление своим отдельным портом/мотором/группой моторов, не о чем общаться друг с другом. Разрешать им о чем-то болтать — логическая ошибка. Если им разрешить это, то гарантировать отсутствие софт- и дед-локов ты уже в общем случае не сможешь.

Но опять же, ты смотришь с позиции конкретного кода, а я рассуждаю о сферическом коде в вакууме.

BFE>Вот сами же пишите
Автор: landerhigh
Дата: 26.06.14
, что

BFE>

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


Совершенно верно. Правильный способ это сделать — локализовать и минимизировать межпотоковое взаимодействие. Разрешать всем потокам общаться друг с другом напрямую, во-первых, бессмысленно, во-второых, ведет к серьезным ошибкам.
www.blinnov.com
Re[11]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 26.06.14 20:55
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>На самом деле вероятность больше. т.к. вероятность лока между двумя любыми мьютекасами не зависит от наличия других мьютекосв. Умножай вероятности сам


L>Я думал что ты говоришь об общем количестве локов, а не о количестве локов в стеке вызовов. С тем, что оптимально, когда у тебя в стеке вызовов всегда только один лок я согласен, это очевидно. Но все же это не всегда очевидно, поэтому ответ — "нужно делать всегда так, чтобы в стеке вызовов был только один лок" — не засчитывается


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

L>А как же балансировка нагрузки между ядрами? Может же быть такое, что один поток получил намного больше работы чем остальные (и в случае таска и в случае очереди с данными)


tough luck. Если это становится проблемой, то ее нужно решать отдельно.

L>Можно привести другой пример, ты ищешь кратчайший путь между двумя узлами в графе, один поток начинает выполнять алгоритм дейкстры из начального узла, а другой — из конечного, пока не встретятся или один из потоков не дойдет до своей цели.


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

L>Коммуницируют потоки через общую память. Можно захватывать лок часто, при каждом апдейте (посмотрели ноду, посчитали вес, записали в массив), как ты советуешь, а можно посчитать сразу для N-ого количества нод и обновить все за один захват мьютекса.


А можно использовать атомики и помечать посещенные ноды. Никаких локов вообще.

L>В данном примере у нас contended lock, но при правильном выборе параметров можно все равно получить хороший прирост производительности.


Больше двух раз все равно не выйдет.

L>Если захватывать на короткий срок для каждой ноды, то получатся тормоза, так как мы захватывать/разхватывать будем дольше чем ходить по графу. Примеров много, на самом деле.


Как я уже сказал, в этом случае можно вообще без локов обойтись.

Coarse grained vs fine grained это крайности, нам же всегда нужен какой-то подходящий для того или иного случая tradeoff.

L>>ты же сам только что сказал, что неконкурентный лок — дешев.


L>Это я привел пример мьютекса, использующегося одним потоком и объяснил зачем это может быть нужно.


Эээ, мьютексы не нужны, если они используются только одинм потоком. Они нужны как раз тогда, когда "иногда" на огонек заходит другой поток. Кстати, критические секции в винде устроены именно так, емнип — неконкурентных захват секции предельно дешев.
www.blinnov.com
Re: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 26.06.14 21:01
Оценка:
Здравствуйте, Lazin, Вы писали:

L>На практике это должно выглядеть как-то так: допустим у нас есть приложение — сервер, оно состоит из сетевой части, которая принимает запросы и новые подключения и (допустим) использует asymmetric lock layer для чтения настроек сетевого стека, white-list допустимых адресов и тд, далее запрос может обрабатываться на урвоне приложения, который использует другой lock layer для того, чтобы защитить доступ к изменяемым данным, далее, в процессе обработки прилоежние должно сохранить данные в БД, там используется еще один lock layer. Блокировки всегда захватываются в порядке, определенном иерархией — server lock layer -> application lock layer -> DB lock layer.


L>Теперь собственно вопрос — хочется специальный дефайн, при определении которого, включались бы проверки корректности порядка захвата и освобождения блокировок. Пока не придумал ничего лучше, нежели строить recourse allocation graph в рантайме, во время вызовов lock/unlock, а потом искать в нем циклы. Еще придумал вариант для бедных — можно сделать массив мьютексов из одного элемента, в этом случае, любой некорректное использование библиотеки будет обязательно приводить к ваимной блокировке и можно будет сравнить стектрейсы разных потоков и понять где ошибка. Может есть еще варианты?


Может как то явно определить для каждого lock layer глубину в иерархии и в lock-unlock складывать её в thread local стек с проверкой на 'больше' при push и на 'равно' при pop?
Re[11]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 07:04
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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


BFE>Что Lazin и пытается сделать с помощью библиотеки из первого поста. А с помощью чего вы собираетесь это гарантировать?


Разница в том, что Lazin лечит осложнения болезни.
Я же ее просто не допускаю.

Дело в том, что пытаться детектить дедлоки в рантайме бессмысленно. Дедлок в рантайме исправить невозможно. Дедлок — это всегда ошибка проектирования. Тот самый случай, когда пить боржоми уже поздно.
Во время тестирования абсолютно невозможно отловить все возможные варианты, особенно, когда мьютексы создаются на каждый чих. Если детектор выявил Х дедлоков во время тестирования, это вовсе не гарантирует, что нашли их все. Более того, это говорит о том, что код плохо спроектирован с точки зрения межпотокового взаимодействия и скорее всего, в нем притаились другие дедлоки, которые просто ждут своего шанса.
www.blinnov.com
Re[6]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 09:14
Оценка:
_>Выделенное — лютый трындец. Сегодня a и b на один мьютекс попали, на следующем старте a и c попадут — это называется happy debugging. Имхо — изначальная идея порочная, все остальное только следствие из бажной концепции.

Это совершенно обычная вещь, так часто делают. Самый частый юзкейс — это когда у тебя много объектов и каждый нужно лочить отдельно, но пихать невпихуемое^W настоящий мьютекс в каждый объект — дорого. Если ты используешь этот пулл мьютексов не рекурсивно (только один лок в стеке вызовов), то взаимная блокировка возникнуть не может. Это легко можно доказать. Можно использовать несколько разных пуллов мьютексов, если в стеке вызовов всегда все захватывается в одном и том же порядке.
Re[12]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 09:21
Оценка:
L>Дело в том, что пытаться детектить дедлоки в рантайме бессмысленно. Дедлок в рантайме исправить невозможно. Дедлок — это всегда ошибка проектирования. Тот самый случай, когда пить боржоми уже поздно.

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

L>Во время тестирования абсолютно невозможно отловить все возможные варианты, особенно, когда мьютексы создаются на каждый чих. Если детектор выявил Х дедлоков во время тестирования, это вовсе не гарантирует, что нашли их все. Более того, это говорит о том, что код плохо спроектирован с точки зрения межпотокового взаимодействия и скорее всего, в нем притаились другие дедлоки, которые просто ждут своего шанса.


В том то и дело, что я свожу сложную проблему — детектор дедлоков для N мьютексов — к простой, детектор дедлоков для LockLayer-ов, которых в типичной программе должно быть один или два
Re[7]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 09:24
Оценка:
Здравствуйте, Lazin, Вы писали:

_>>Выделенное — лютый трындец. Сегодня a и b на один мьютекс попали, на следующем старте a и c попадут — это называется happy debugging. Имхо — изначальная идея порочная, все остальное только следствие из бажной концепции.


L>Это совершенно обычная вещь, так часто делают.


Согласен, так делают часто. К сожалению. И за это надо

L>Самый частый юзкейс — это когда у тебя много объектов и каждый нужно лочить отдельно,


Я буду продолжать утверждать, что такой юзкейс — всегда следствие грубой архитектурной ошибки или вообще отсутствия архитектуры как таковой.

Доказывается это элементарно. Основной выигрыш от многопоточного программирования состоит в возможности делать несколько задач параллельно. Неважно, что это за задачи — расчет чего-то тяжелого на 124 ядрах или просто 100500 потоков на одном ядре, которые спят на блокирующем I/O. Важно то, что каждый поток большую часть времени занят своим делом и не мешает остальным. Чтобы достичь такой нирваны, блокирующий или потенциально блокирующий обмен данными между потоками должен происходить как можно реже и в как можно меньшем количестве четко определенных локаций в коде.

"Много объектов, которые нужно лочить отдельно" рушат эту концепцию на корню и создают проблемы. Дедлоки в том числе.
www.blinnov.com
Re[2]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 09:25
Оценка:
S>Может как то явно определить для каждого lock layer глубину в иерархии и в lock-unlock складывать её в thread local стек с проверкой на 'больше' при push и на 'равно' при pop?

Это сильно упростит дело (решение о наличии/отсутствии дедлока принимается потоком локально, вместо того, чтобы сравнивать стеки вызовов разных потоков), да, но не хотелось бы, так как пострадает простота использования и модульность (нужно заранее знать порядок). Это пока запасной вариант, на случай если я ничего не придумаю
Re[3]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 09:56
Оценка:
Здравствуйте, Lazin, Вы писали:

S>>Может как то явно определить для каждого lock layer глубину в иерархии и в lock-unlock складывать её в thread local стек с проверкой на 'больше' при push и на 'равно' при pop?


L>Это сильно упростит дело (решение о наличии/отсутствии дедлока принимается потоком локально, вместо того, чтобы сравнивать стеки вызовов разных потоков), да, но не хотелось бы, так как пострадает простота использования и модульность (нужно заранее знать порядок). Это пока запасной вариант, на случай если я ничего не придумаю


Ещё надо учитывать, что связка между lock-layer может быть сильной-слабой, т.е. блокировка какого либо уровня может требовать блокировки более высокого уровня или не требовать. Они синхронизируют какие то модификации разных уровней и иногда эти модификации могут быть выполнены независимо друг от друга, а иногда нет. В первом случае проверка нестрогая, просто больше, во втором случае обязательно наличие блокировки предыдущего уровня.

В принципе можно на организационном уровне как то решать, просто для порядка требовать в конструкторе guard'а ссылку на guard более высокого уровня. Для общности можно ввести null-lock-level, от которого плясать во всех точках входа потока.
Re[8]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 10:31
Оценка:
L>>Это совершенно обычная вещь, так часто делают.
L>Согласен, так делают часто. К сожалению. И за это надо

Альтернативы то какие? Убить параллелизм используя один мьютекс?

L>>Самый частый юзкейс — это когда у тебя много объектов и каждый нужно лочить отдельно,

L>Я буду продолжать утверждать, что такой юзкейс — всегда следствие грубой архитектурной ошибки или вообще отсутствия архитектуры как таковой.

I dunnu WTF R U talking about

L>Доказывается это элементарно. Основной выигрыш от многопоточного программирования состоит в возможности делать несколько задач параллельно. Неважно, что это за задачи — расчет чего-то тяжелого на 124 ядрах или просто 100500 потоков на одном ядре, которые спят на блокирующем I/O. Важно то, что каждый поток большую часть времени занят своим делом и не мешает остальным. Чтобы достичь такой нирваны, блокирующий или потенциально блокирующий обмен данными между потоками должен происходить как можно реже и в как можно меньшем количестве четко определенных локаций в коде.


Ну так в коде это может быть одно место но лочиться в этом месте могут разные объекты, в зависимости от данных. Я не спорю с тем, что потоки не должны слишком часто взаимодействовать друг с другом (сам писал об этом), но это не имеет никакого отношения к корректности многопоточных программ.

L>"Много объектов, которые нужно лочить отдельно" рушат эту концепцию на корню и создают проблемы. Дедлоки в том числе.


Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами, да, она сложнее чем coarse grained синхронизация (global lock самый простой вариант из всех возможных, тщательно организованная fine grained синхронизация, это очень сложно). Но только вот не надо говорить что всем подходит первое, а второе — ошибка архитектуры. Может я биллинг сервис пишу какой-нибудь или RTB систему, которая баннры должна показывать и параллельно с этим — аукционы в баннерной сети выигрывать. Есть ТЗ по которому может потребоваться сделать более сложную систему в которой возможно возникновение дедлоков и прочих ништяков. В общем, твой посыл понятен, но вот спорить на эту тему, на мой взгляд — бессмысленно.
Re[14]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 10:41
Оценка:
L>(0.Дедлоки вполне себе ловятся при помощи debugdiag от мелкософта. Да и просто достаточно снять ручной дамп и заблокированные треды как на ладони.)
у меня все на портируемом С++ написано и может работать там где нет debugdiag от майкрософта

L>1.специальный билд слегка меняет тайминги и дедлок не повторяется. Вообще никак. Хоть ты на голову встань.

я ищу дедлоки между LockLayer-ами, а не мьютексами, а значит отловлю не только реальные дедлоки на мьютексах но даже потенциально возможные

L>2.приходит багрепорт из продакшена о стабильном зависоне. Специальный билд у клиента поставить невозможно. Снимаете ручной дамп, видите заблокированный тред и всей конторой не можете понять, как потоки вообще умудрились оказаться в таком состоянии, т.к. этого "не может быть никогда". Это следствие архитектурных просчетов.

L>3. Находите один лок, чините, создаете три новых. Следствие архитектурных просчетов.

и эти люди учат нас писать многопоточный код

L>Извини, но это лечение туберкулеза перцовым пластырем.


Не все на свете сводится к простым схемам взаимодействия между потоками и "неправильная" арх. (с твоей точки зрения), может быть правильным решением той или иной задачи.
Re[4]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 11:06
Оценка:
S>Ещё надо учитывать, что связка между lock-layer может быть сильной-слабой, т.е. блокировка какого либо уровня может требовать блокировки более высокого уровня или не требовать. Они синхронизируют какие то модификации разных уровней и иногда эти модификации могут быть выполнены независимо друг от друга, а иногда нет. В первом случае проверка нестрогая, просто больше, во втором случае обязательно наличие блокировки предыдущего уровня.

S>В принципе можно на организационном уровне как то решать, просто для порядка требовать в конструкторе guard'а ссылку на guard более высокого уровня. Для общности можно ввести null-lock-level, от которого плясать во всех точках входа потока.


Это все хорошо, можно действительно позволять задавать родительский lock layer, это упростит анализ. Я наверное так сделаю, но только это будет опциональная возможность. Ну и для начала нужно сделать для самого сложного случая — когда явной связки между lock layer-ами нет. Технически, моей схеме синхронизации это не нужно, нужно просто проверить на отсутсвие рекурсии (самая базовая проверка, маст хэв), потом можно проверить на отсутствие циклов (желательно, так как ожидания пользователя скорее всего будут включать и эту проверку) ну и после этого можно позволить задать пользователю дополнительные ограничения, то о чем ты говоришь, и проверять еще и их. Не вижу особых проблем для этого.

Пока что меня больше интересует то, как это сделать технически. Я думал делать так: есть некий глобальный объект — DeadlockDetector, у него есть метод add и метод remove. В методе lock lock layer-а есть thread local переменная, при вызове lock, поток смотрит туда, если она не инициализрована — значит он lock layer нулевого уровня, мы можем инициализировать ее и передать в DeadlockDetector::add ссылку на себя (объект LockLayer), в котором она добавится в конец списка (один atomic exchange). Если переменная в thread local уже инициализирована, значит мы — не первый lock layer и ниже по стеку уже есть еще один лок. Мы можем пройтись по стеку вниз и получить ссылки на низлежащие lock-layer-ы. Проблема пока только в том, что нужно сравнивать стеки lock layer-ов, я не придумал как это делать пока, так же не ясно, как чистить память (может можно и не чистить ее совсем).
Re[9]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 11:17
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Согласен, так делают часто. К сожалению. И за это надо


L>Альтернативы то какие? Убить параллелизм используя один мьютекс?


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

L>>>Самый частый юзкейс — это когда у тебя много объектов и каждый нужно лочить отдельно,

L>>Я буду продолжать утверждать, что такой юзкейс — всегда следствие грубой архитектурной ошибки или вообще отсутствия архитектуры как таковой.

L>I dunnu WTF R U talking about


Какое слово непонятно?

L>Ну так в коде это может быть одно место но лочиться в этом месте могут разные объекты, в зависимости от данных.




L>>"Много объектов, которые нужно лочить отдельно" рушат эту концепцию на корню и создают проблемы. Дедлоки в том числе.


L>Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами,


Во-первых, если разные потоки работают с разными объектами, не пересекаясь, зачем вообще синхронизация? Если же им требуется делить один и тот же объект и, соответственно, блокировать друг друга, то возникает вопрос — а есть ли, собственно, выгода от многопоточности? Не получится ли так, что потоки 100 мс работают, а потом еще 200 мс ждут в очереди доступа к залоченному ресурсу?

L>да, она сложнее чем coarse grained синхронизация (global lock самый простой вариант из всех возможных, тщательно организованная fine grained синхронизация, это очень сложно). Но только вот не надо говорить что всем подходит первое, а второе — ошибка архитектуры.


Как правило, пытаясь достичь второго, умудряются лишь создать кошмарную архитектуру

L>Может я биллинг сервис пишу какой-нибудь или RTB систему, которая баннры должна показывать и параллельно с этим — аукционы в баннерной сети выигрывать.


Не имеет значения. Никакого.

L>Есть ТЗ по которому может потребоваться сделать более сложную систему в которой возможно возникновение дедлоков и прочих ништяков.


Возникновение дедлока — это всегда ошибка архитектуры. Не может требоваться система, в которой могут возникать дедлоки. Такая система бесполезна.

Вот тебе пример — построили однопутную железную дорогу и разъезд на ней, чтобы можно было пропускать встречные поезда. В первый же день оказалось, что обычные до этих мест длинные товарняки на разъезд полностью не влезают. В результате — дедлок и миллионы убытка в день и стоящая раком сеть. Кто виноват — машинист поезда или все же горе-проектировщики разъезда? Можно, конечно, заняться fine grained синхронизацией, разрезать поезда, заказать за миллиарды дополнительные локомотивы, нагрузить другие узловые станции передержкой вагонов, а перегоны увеличенным количеством поездов и справиться с ситуацией, получив задранные до небес накладные расходы, а можно выгнать горе-проектировщиков и увеличить разъезд так, чтобы дедлока не возникало.

L>В общем, твой посыл понятен, но вот спорить на эту тему, на мой взгляд — бессмысленно.


Видишь ли в чем дело. Ты аргументируешь сложностью системы. А я считаю сложность системы плохим, негодным оправданием для архитектурных просчетов. Я всегда свожу сложную систему, какой бы сложной она не казалась, к простой системе простых систем. Это общепринятый инженерный подход. И не вижу ни одной причины так не делать.
www.blinnov.com
Re[15]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 11:25
Оценка:
Здравствуйте, Lazin, Вы писали:

L>у меня все на портируемом С++ написано и может работать там где нет debugdiag от майкрософта


там будет gdb. Там, где gdb нет, с многопоточностью тоже будут недетские напряги.

L>>1.специальный билд слегка меняет тайминги и дедлок не повторяется. Вообще никак. Хоть ты на голову встань.

L>я ищу дедлоки между LockLayer-ами, а не мьютексами, а значит отловлю не только реальные дедлоки на мьютексах но даже потенциально возможные

Не отловишь.
Ты пытаешься искать лок в динамике. А динамика — она такая. Динамичная с потецниально неограниченным количеством состояний.

L>>2.приходит багрепорт из продакшена о стабильном зависоне. Специальный билд у клиента поставить невозможно. Снимаете ручной дамп, видите заблокированный тред и всей конторой не можете понять, как потоки вообще умудрились оказаться в таком состоянии, т.к. этого "не может быть никогда". Это следствие архитектурных просчетов.

L>>3. Находите один лок, чините, создаете три новых. Следствие архитектурных просчетов.

L>и эти люди учат нас писать многопоточный код


Именно. "Эти люди" на своем опыте знают, чем именно заканчивается попытка лечить архитектурные просчеты изолентой.

L>>Извини, но это лечение туберкулеза перцовым пластырем.


L>Не все на свете сводится к простым схемам взаимодействия между потоками и "неправильная" арх. (с твоей точки зрения), может быть правильным решением той или иной задачи.


Архитектура, принципиально допускающая образование дедлоков, не может быть правильным решением какой бы то ни было задачи. Она может быть условно приемлимым в определенных рамках решением, но это не относится к разработке софта.
www.blinnov.com
Re[5]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 11:50
Оценка:
Здравствуйте, Lazin, Вы писали:

L>Пока что меня больше интересует то, как это сделать технически. Я думал делать так: есть некий глобальный объект — DeadlockDetector, у него есть метод add и метод remove. В методе lock lock layer-а есть thread local переменная, при вызове lock, поток смотрит туда, если она не инициализрована — значит он lock layer нулевого уровня, мы можем инициализировать ее и передать в DeadlockDetector::add ссылку на себя (объект LockLayer), в котором она добавится в конец списка (один atomic exchange). Если переменная в thread local уже инициализирована, значит мы — не первый lock layer и ниже по стеку уже есть еще один лок. Мы можем пройтись по стеку вниз и получить ссылки на низлежащие lock-layer-ы. Проблема пока только в том, что нужно сравнивать стеки lock layer-ов, я не придумал как это делать пока, так же не ясно, как чистить память (может можно и не чистить ее совсем).


Ну проверка на deadlock бесплатной всё равно не будет, thread-local стек блокировок можно и внутри DeadlockDetector сделать, что то вроде этого:

void DeadlockDetector::on_lock(LockLayerImpl& layer, size_t hash) {
    lock_stack& thread_locks = get_thread_locks();
    // проверяем отсутствие пары (layer, hash) в стеке
    // пары (layer, H) могут присутствовать сверху в порядке hash
    // т.е. если сверху тот же layer, достаточно проверить hash order
    check_lock_loop(thread_locks, layer, hash);
    thread_locks.push_back(std::make_pair(&layer, hash));
}

void DeadlockDetector::on_unlock(LockLayerImpl& layer, size_t hash) {
    lock_stack& thread_locks = get_thread_locks();
    // проверяем соответствие пары (layer, hash) последнему элементу
    check_last_lock(thread_locks, layer, hash);
    thread_locks.pop_back();
}


Память я думаю можно и не чистить, всё равно DeadlockDetector глобальный. Атомики не нужны, thread_locks всё равно thread-local.
Re[10]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 12:00
Оценка:
L>Наоборот. Довести его до максимума, запрещая блокировать что попало на каждый чих и заставляя обдумывать каждую блокировку.

А подробнее вот об этом можно? У меня есть 1000 объектов, вероятность того, что 2 потока будут использовать в один момент вермени один объект — один из тысячи. Т.е. локов много, вероятность блокировки — очень низка, все потоки большую часть времени работают не мешая друг другу. Думаю это всем понятный кейс. Ты же предлагаешь нечто загадочное.

L>>Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами,


L>Во-первых, если разные потоки работают с разными объектами, не пересекаясь, зачем вообще синхронизация? Если же им требуется делить один и тот же объект и, соответственно, блокировать друг друга, то возникает вопрос — а есть ли, собственно, выгода от многопоточности? Не получится ли так, что потоки 100 мс работают, а потом еще 200 мс ждут в очереди доступа к залоченному ресурсу?


Смотри пример выше.

L>Как правило, пытаясь достичь второго, умудряются лишь создать кошмарную архитектуру


Общие фразы опять.

L>Возникновение дедлока — это всегда ошибка архитектуры. Не может требоваться система, в которой могут возникать дедлоки. Такая система бесполезна.


Никто и не утверждал что это нормально, я имел ввиду простую вещь — делаем сложную систему с нетривиальной синхронизацией — возможные дедлоки в следствии ошибок. Ты и сам это повторяешь постоянно. Я не говорил что нужно делать системы в которых дедлоки возможны в принципе.

L>Видишь ли в чем дело. Ты аргументируешь сложностью системы. А я считаю сложность системы плохим, негодным оправданием для архитектурных просчетов. Я всегда свожу сложную систему, какой бы сложной она не казалась, к простой системе простых систем. Это общепринятый инженерный подход. И не вижу ни одной причины так не делать.


Ты пока только общие фразы говришь и все. Вот мой пример выше, как это можно сделать просто и без блокировок (или с небольшим количеством блокировок)? Я не вижу смысла продолжать говорить "в общем", нужно либо конкретные примеры рассматривать (что сложно) либо забить на это, ибо дальше мы точно не продвинемся
Re[6]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 12:06
Оценка:
Здравствуйте, sokel, Вы писали:

S>Ну проверка на deadlock бесплатной всё равно не будет, thread-local стек блокировок можно и внутри DeadlockDetector сделать, что то вроде этого:


Ааа, sorry, ступил. Это же только на цикл проверка, для проверки порядка действительно без синхронизации не обойтись.
Re[7]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 12:18
Оценка:
Здравствуйте, sokel, Вы писали:

S>Ааа, sorry, ступил. Это же только на цикл проверка, для проверки порядка действительно без синхронизации не обойтись.


В лоб что то такое только:
// check_lock_order:
lock_layer_transitions;
foreach(prev_layer in thread_locks) {
    assert(!layer.transitions.contains(prev_layer));
    prev_layer.transitions.insert(layer);
}

Но это дорого будет наверное...
Re[8]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 12:35
Оценка:
Здравствуйте, sokel, Вы писали:

S>Но это дорого будет наверное...


Можно пронумеровать все слои в конструкторе и хранить transition-map в DeadlockDetector в виде двумерного массива. Тогда можно будет заполнять случающиеся переходы без блокировки. Только вот проверка будет не синхронизирована, но её можно и постфактум выполнить.
Re[12]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 27.06.14 12:48
Оценка:
Здравствуйте, landerhigh, Вы писали:

BFE>>Вот сами же пишите
Автор: landerhigh
Дата: 26.06.14
, что

BFE>>

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


L>Совершенно верно. Правильный способ это сделать — локализовать и минимизировать межпотоковое взаимодействие. Разрешать всем потокам общаться друг с другом напрямую, во-первых, бессмысленно, во-второых, ведет к серьезным ошибкам.


К каким ещё серьезным ошибкам? Если никакая из ниток не пытается одновременно залочить более одного мьютекса, то никакой deadlock невозможен. Тогда зачем, в общем случае, запрещать потокам обращаться друг с другом напрямую?
И каждый день — без права на ошибку...
Re[16]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 27.06.14 13:05
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Архитектура, принципиально допускающая образование дедлоков, не может быть правильным решением какой бы то ни было задачи.

Я так понимаю, что правильно написанная deadlock-prevention библиотека гарантирует отсутствие дедлоков. Я не говорю про библиотек Lazin'а, а про общий случай.
И каждый день — без права на ошибку...
Re[17]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 13:25
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>Архитектура, принципиально допускающая образование дедлоков, не может быть правильным решением какой бы то ни было задачи.

BFE>Я так понимаю, что правильно написанная deadlock-prevention библиотека гарантирует отсутствие дедлоков.

Нет. Она не может такого гарантировать. Она не может предотвращать дедлоки. Они может их обнаруживать.
В самом лучшем случае, допустим, что мы такую библиотеку написали. Система в продакшене. Допустим, эта библиотека обнаружила дедлок.
Что она должна сделать?
www.blinnov.com
Re[13]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 13:36
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>Совершенно верно. Правильный способ это сделать — локализовать и минимизировать межпотоковое взаимодействие. Разрешать всем потокам общаться друг с другом напрямую, во-первых, бессмысленно, во-второых, ведет к серьезным ошибкам.


BFE>К каким ещё серьезным ошибкам?

BFE>Если никакая из ниток не пытается одновременно залочить более одного мьютекса, то никакой deadlock невозможен.

Если любая нитка может общаться с любой другой ниткой, то дедлок весьма возможен даже в этом случае. Нарисуй картинку

BFE>Тогда зачем, в общем случае, запрещать потокам обращаться друг с другом напрямую?


Потому что в общем случае им не о чем друг с другом говорить. Поток нужен, чтобы выполнять работу. Он должен получать задания от управляющего потока и сообщать ему о результатах работы. Все. Болтовня кого попало с кем попало никому не нужна.
Есть интересные случаи параллельных алгоритмов, но они тоже сводятся к этому паттерну.
www.blinnov.com
Re[9]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 13:49
Оценка:
Здравствуйте, sokel, Вы писали:

S>Здравствуйте, sokel, Вы писали:


S>>Но это дорого будет наверное...


S>Можно пронумеровать все слои в конструкторе и хранить transition-map в DeadlockDetector в виде двумерного массива. Тогда можно будет заполнять случающиеся переходы без блокировки. Только вот проверка будет не синхронизирована, но её можно и постфактум выполнить.


Хотя можно и на месте можно lock-free получать факт deadlock'а потенциального с двумерным массивом. Предположим два потока одновременно регистрируют взаимно невозможные переходы layer1-layer2 и layer2-layer1. Нужно упорядочить регистрацию переходов. Для этого просто будем использовать половину матрицы, т.е. оба перехода фиксируем в переменной transitions[1][2] (упорядочиваем индексы). Прямой переход — значение 1, обратный переход — значение 2. Ну и дальше atomic CAS, допустимы изменения 0-1, 1-1, 0-2, 2-2. При фиксации попыток изменений 1-2 или 2-1 сигнализируем deadlock.
Re[11]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 13:52
Оценка:
Здравствуйте, Lazin, Вы писали:

L>А подробнее вот об этом можно? У меня есть 1000 объектов, вероятность того, что 2 потока будут использовать в один момент вермени один объект — один из тысячи.


Во-первых, что это за 1000 объектов? Почему они доступны сразу всем потокам? Почему нельзя их разделить и передать потокам в эксклюзивоное пользование? Нельзя ли поручить общение с этим объектами одному потоку? И так далее...

L>Т.е. локов много, вероятность блокировки — очень низка, все потоки большую часть времени работают не мешая друг другу.


ВременнАя вероятность никого не интересует. Вероятность блокировки в этом случае строго равна единице, она рано или еще раньше произойдет. И будет ли это просто конкурентный лок или же он перерастет в полноценный дед лок, зависит исключительно от архитектуры. Если граф вызовов объекта под локом ни в каком случае не заходит в другой лок, мы спасены. Но гарантировать это можно исключительно правильной архитектурой, в которой эта гарантия обеспечивается дизайном.

L>Думаю это всем понятный кейс. Ты же предлагаешь нечто загадочное.


Я предлагаю простейшее решение. Чего тут моежет быть непонятного?

L>>>Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами,


L>>Во-первых, если разные потоки работают с разными объектами, не пересекаясь, зачем вообще синхронизация? Если же им требуется делить один и тот же объект и, соответственно, блокировать друг друга, то возникает вопрос — а есть ли, собственно, выгода от многопоточности? Не получится ли так, что потоки 100 мс работают, а потом еще 200 мс ждут в очереди доступа к залоченному ресурсу?


L>Смотри пример выше.


Ответь на вопрос "зачем". Любое решение должно быть обосновано.

L>>Как правило, пытаясь достичь второго, умудряются лишь создать кошмарную архитектуру


L>Общие фразы опять.


Основаны на конкретных событиях.

L>>Возникновение дедлока — это всегда ошибка архитектуры. Не может требоваться система, в которой могут возникать дедлоки. Такая система бесполезна.


L>Никто и не утверждал что это нормально, я имел ввиду простую вещь — делаем сложную систему с нетривиальной синхронизацией — возможные дедлоки в следствии ошибок.


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

L>Ты и сам это повторяешь постоянно. Я не говорил что нужно делать системы в которых дедлоки возможны в принципе.


L>>Видишь ли в чем дело. Ты аргументируешь сложностью системы. А я считаю сложность системы плохим, негодным оправданием для архитектурных просчетов. Я всегда свожу сложную систему, какой бы сложной она не казалась, к простой системе простых систем. Это общепринятый инженерный подход. И не вижу ни одной причины так не делать.


L>Ты пока только общие фразы говришь и все.

L>Вот мой пример выше, как это можно сделать просто и без блокировок (или с небольшим количеством блокировок)? Я не вижу смысла продолжать говорить "в общем", нужно либо конкретные примеры рассматривать (что сложно) либо забить на это, ибо дальше мы точно не продвинемся

Ты сначала защити свое изначальное решение иметь 1000 объектов, расшаренных на все потоки. Больше информации, короче!
Ошибки в фундаменте здания несколько поздно чинить при установке лифтов, может оказаться, что и ни 1000 объектов не надо, ни сотен потоков.
www.blinnov.com
Re[14]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 27.06.14 14:04
Оценка:
Здравствуйте, landerhigh, Вы писали:

BFE>>Если никакая из ниток не пытается одновременно залочить более одного мьютекса, то никакой deadlock невозможен.

L>Если любая нитка может общаться с любой другой ниткой, то дедлок весьма возможен даже в этом случае. Нарисуй картинку

Давайте вы нарисуете картинку, потому что мне очень трудно нарисовать то, чего быть не может
И каждый день — без права на ошибку...
Re[6]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 14:12
Оценка:
Здравствуйте, sokel, Вы писали:

S>
S>void DeadlockDetector::on_lock(LockLayerImpl& layer, size_t hash) {
S>


Хотя multiple-lock одного слоя можно делать только через один guard, т.е. надо проверку независимо от hash делать, не на уровне lock-layer::lock, а на уровне guard::lock (hash-order блокировок в multiple quard'е проверять не надо).
Re[12]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 14:33
Оценка:
L>Во-первых, что это за 1000 объектов? Почему они доступны сразу всем потокам? Почему нельзя их разделить и передать потокам в эксклюзивоное пользование? Нельзя ли поручить общение с этим объектами одному потоку? И так далее...

Многопоточный TCP сервер, работающий с неким набором объектов, допустим это счетчики (допустим, их количество фиксировано, для простоты), есть запрос на увеличение значения счетчика(ов) и запрос на чтение, при этом данные должны быть целостными (т.е. вариант — писать все команды на изменение в лог и потом применять — не катит), есть требование sequential consistency.

я напишу обработчики как-то так:

void on_update(Query& q, Response& r) {
    Counter& cnt = counters[q.id];  // shared state, контейнер не меняется, объекты Counter - могут меняться
    std::lock_guard<std::mutex> g(cnt.mutex);
    cnt.add(q.value);
    r.set_result(cnt.value);  // возвращаем обновленное значение
}

void on_read(Query& q, Response& r) {
    Counter const& cnt = counters[q.id];
    std::lock_guard<std::mutex> g(cnt.mutex);
    r.set_result(cnt.value);  // возвращаем содержимое счетчика
}


Обработчики могут вызываться из кучи разных потоков, разными клиентскими соединениями. Объектов Counter у нас много и contention низкий, т.е. как правило разные потоки захватывают разные счетчики не мешая друг другу. Можно ввести небольшой oversubscription (потоков больше чем ядер) и все буедт загружено очень хорошо. Дедлок тут возникнуть не может, по очевидным причинам. Вложенные блокировки могут быть полезны (мы, например, можем захотеть залочить объект — соединение(aka сокет), между получением запроса и отправкой ответа на него, так как в этот сокет могут приходить другие запросы, пока мы обрабатываем текущий и они могут начать обрабатываться в другом потоке (допустим это event based server с пуллом потоков).

L>Почему они доступны сразу всем потокам?

Мы не знаем заранее кому какой объект нужен.

L>Почему нельзя их разделить и передать потокам в эксклюзивоное пользование?

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

L>Нельзя ли поручить общение с этим объектами одному потоку?

Лишние накладные расходы — roundtrip между двумя ядрами, вместо того, чтобы захватить uncontended mutex, это раз. Sequential consistency это два (нельзя положить команду на обновление счетчика в очередь, в надежде на то, что этот специальный поток все сделает, так как как за запросом на обновление счетчика может следовать запрос на чтение и он должен вернуть правильный результат).
Re[10]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 14:34
Оценка:
S>Хотя можно и на месте можно lock-free получать факт deadlock'а потенциального с двумерным массивом. Предположим два потока одновременно регистрируют взаимно невозможные переходы layer1-layer2 и layer2-layer1. Нужно упорядочить регистрацию переходов. Для этого просто будем использовать половину матрицы, т.е. оба перехода фиксируем в переменной transitions[1][2] (упорядочиваем индексы). Прямой переход — значение 1, обратный переход — значение 2. Ну и дальше atomic CAS, допустимы изменения 0-1, 1-1, 0-2, 2-2. При фиксации попыток изменений 1-2 или 2-1 сигнализируем deadlock.


С матрицей есть одна проблема — ее проблематично ресайзить.
Re[18]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 27.06.14 14:39
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Архитектура, принципиально допускающая образование дедлоков, не может быть правильным решением какой бы то ни было задачи.

BFE>>Я так понимаю, что правильно написанная deadlock-prevention библиотека гарантирует отсутствие дедлоков.
L>Нет. Она не может такого гарантировать. Она не может предотвращать дедлоки. Они может их обнаруживать.
Раз можем обнаружить, значит можем и предотвратить, так как после обнаружения мы можем произвести обработку сложившейся ситуации.

L>В самом лучшем случае, допустим, что мы такую библиотеку написали. Система в продакшене. Допустим, эта библиотека обнаружила дедлок.

L>Что она должна сделать?
Мне очевидно, надеюсь, что и вам тоже (хотя, судя по соседней ветке это не так), что дедлоки возможны только тогда, когда одна нитка должна залочить одновременно два или более мьютексов. Такой алгоритм имеет смысл, только в том случае, когда у нас есть данные, которые должны быть изменены синхронно.
Классический пример — перевод денег:
залочили первый счёт, списали деньги,
залочили второй счёт, добавили туда списанные деньги,
разлочили второй счёт,
разлочили первый счёт.
Очевидно, что здесь возможен классический дедлок.
Если мы обнаружили дедлок в какой либо нитке, то эта нитка должна восстановить предыдущее состояние тех данных, которые были защищены мьютексом, разлочить все мьютексы, после чего повторить попытку. Если мы применяем RAII, то можно бросить исключение и поймать его выше всех локов, после чего повторить попытку.

Замечу, что если у каждого счёта есть свой мьютекс, то мы можем параллельно проводить очень много переводов, которые иногда (редко, при большом числе счётов) будут "откатываться" и проводиться не с первой попытки. Если же мы все операции запишем в одну очередь, то мы сведём нашу многопоточную систему к одному исполняемому потоку, чем сильно замедлим всю систему.
И каждый день — без права на ошибку...
Re[11]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 27.06.14 14:40
Оценка:
Здравствуйте, Lazin, Вы писали:

S>>Хотя можно и на месте можно lock-free получать факт deadlock'а потенциального с двумерным массивом. Предположим два потока одновременно регистрируют взаимно невозможные переходы layer1-layer2 и layer2-layer1. Нужно упорядочить регистрацию переходов. Для этого просто будем использовать половину матрицы, т.е. оба перехода фиксируем в переменной transitions[1][2] (упорядочиваем индексы). Прямой переход — значение 1, обратный переход — значение 2. Ну и дальше atomic CAS, допустимы изменения 0-1, 1-1, 0-2, 2-2. При фиксации попыток изменений 1-2 или 2-1 сигнализируем deadlock.



L>С матрицей есть одна проблема — ее проблематично ресайзить.


Можно заранее ограничить число слоёв. Либо рассчитывать на вызов первого лока только после создания всех слоёв. Либо держать ссылки на состояния переходов в thread-local контейнере, инициализируя под блокировкой при отсутствии.
Re[13]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 27.06.14 14:43
Оценка:
L>>Почему нельзя их разделить и передать потокам в эксклюзивоное пользование?
L>>Нельзя ли поручить общение с этим объектами одному потоку?

Мне кажется, lock per object здесь самый простой вариант. Все остальные привносят сложность. Расшардить счсетчики между потоками — добавляем сложность шардирования. Отдельный поток работающий с счетчиками — добавляем сложность взаимодействия между потоком сервера и этим отдельным потоком. Неужели это не очевидно?
Re[19]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 20:22
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, landerhigh, Вы писали:


L>>Нет. Она не может такого гарантировать. Она не может предотвращать дедлоки. Они может их обнаруживать.

BFE>Раз можем обнаружить, значит можем и предотвратить, так как после обнаружения мы можем произвести обработку сложившейся ситуации.

Поздно уже предотвращать. Система в продакшене. Дедлок выстрелил, заслонка зависла в открытом положении, самолет упал, электростанция взорвалась.

L>>В самом лучшем случае, допустим, что мы такую библиотеку написали. Система в продакшене. Допустим, эта библиотека обнаружила дедлок.

L>>Что она должна сделать?
BFE>Мне очевидно, надеюсь, что и вам тоже (хотя, судя по соседней ветке это не так), что дедлоки возможны только тогда, когда одна нитка должна залочить одновременно два или более мьютексов.

О чем я и говорю. Гораздо проще архитектурными решениями гарантировать отсутствие локов под локами, нежели пытаться искать черную кошку там, где ее нет.

BFE>Такой алгоритм имеет смысл, только в том случае, когда у нас есть данные, которые должны быть изменены синхронно.

BFE>Классический пример — перевод денег:

Перевод денег делается транзакциями с подтверждениями, а не локами с мьютексами.

BFE>Если мы обнаружили дедлок в какой либо нитке, то эта нитка должна восстановить предыдущее состояние тех данных, которые были защищены мьютексом, разлочить все мьютексы, после чего повторить попытку.


Ты хочешь вернуть систему в предыдущее состояние. Это в общем случае уже невозможно. Файл уже удален, заслонка открыта, газ идет, состояние (текщее и предыдущее) других ниток неизвестно, куда откатываться, непонятно, да и просто так закрыть заслонку нельзя.

BFE>Если мы применяем RAII, то можно бросить исключение и поймать его выше всех локов, после чего повторить попытку.


Единственный более-менее приемлимый вариант — упасть с дампом. Что тоже очень плохо, но лучше зависания.
www.blinnov.com
Re[15]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 20:36
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, landerhigh, Вы писали:


BFE>>>Если никакая из ниток не пытается одновременно залочить более одного мьютекса, то никакой deadlock невозможен.

L>>Если любая нитка может общаться с любой другой ниткой, то дедлок весьма возможен даже в этом случае. Нарисуй картинку

BFE>Давайте вы нарисуете картинку, потому что мне очень трудно нарисовать то, чего быть не может


Давай не будем на "вы", ок?

ты совершенно прав, подобная схема сама по себе дедлок вызвать не сможет. Это у меня моск рисовал странные графы в подсознании.
В ней возможно возникновение софт-локов (все треды ломанулись лочить один и тот же мьютекс), что по последствиям может быть примерно столь же катастрофично, как и дедушка лок. Однако, архитектурные ошибки и ошибки разработки (например, религиозное неприятие RAII и как следствие неснятый лок из-за удачного вылета исключения или пренебрежение рекомендацией не пользоваться suspend/terminate thread) выведут даже такую систему в состояние, когда она рано или даже раньше заблокируется. И превентивно отловить это никакими детекторами будет нельзя.
Именно поэтому в вопросе предотвращения дедлоков я ставлю архитектуру на первое, второе и так далее до десятого мест, а детекторы вообще никуда не ставлю.
www.blinnov.com
Re[20]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 27.06.14 20:44
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Нет. Она не может такого гарантировать. Она не может предотвращать дедлоки. Они может их обнаруживать.

BFE>>Раз можем обнаружить, значит можем и предотвратить, так как после обнаружения мы можем произвести обработку сложившейся ситуации.
L>Поздно уже предотвращать. Система в продакшене. Дедлок выстрелил, заслонка зависла в открытом положении, самолет упал, электростанция взорвалась.
Можно предотвратить заранее. Цена — не оптимальный алгоритм.

L>>>В самом лучшем случае, допустим, что мы такую библиотеку написали. Система в продакшене. Допустим, эта библиотека обнаружила дедлок.

L>>>Что она должна сделать?
BFE>>Мне очевидно, надеюсь, что и вам тоже (хотя, судя по соседней ветке это не так), что дедлоки возможны только тогда, когда одна нитка должна залочить одновременно два или более мьютексов.
L>О чем я и говорю. Гораздо проще архитектурными решениями гарантировать отсутствие локов под локами, нежели пытаться искать черную кошку там, где ее нет.
Да? Вот здесь
Автор: landerhigh
Дата: 27.06.14
вы говорите нечто противоположное.

BFE>>Такой алгоритм имеет смысл, только в том случае, когда у нас есть данные, которые должны быть изменены синхронно.

BFE>>Классический пример — перевод денег:
L>Перевод денег делается транзакциями с подтверждениями, а не локами с мьютексами.
Ну транзакция изнутри как устроена?

BFE>>Если мы обнаружили дедлок в какой либо нитке, то эта нитка должна восстановить предыдущее состояние тех данных, которые были защищены мьютексом, разлочить все мьютексы, после чего повторить попытку.

L>Ты хочешь вернуть систему в предыдущее состояние. Это в общем случае уже невозможно. Файл уже удален, заслонка открыта, газ идет, состояние (текщее и предыдущее) других ниток неизвестно, куда откатываться, непонятно, да и просто так закрыть заслонку нельзя.
Ну так надо с умом программу писать-то. Чем это ситуация отличается от брошенного исключения? Файл уже удален, заслонка открыта, газ идет...

BFE>>Если мы применяем RAII, то можно бросить исключение и поймать его выше всех локов, после чего повторить попытку.

L>Единственный более-менее приемлимый вариант — упасть с дампом. Что тоже очень плохо, но лучше зависания.
Упасть — это не вариант.
И каждый день — без права на ошибку...
Re[13]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 20:44
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Во-первых, что это за 1000 объектов? Почему они доступны сразу всем потокам? Почему нельзя их разделить и передать потокам в эксклюзивоное пользование? Нельзя ли поручить общение с этим объектами одному потоку? И так далее...


L>Многопоточный TCP сервер, работающий с неким набором объектов,


Можно сразу вопросы? Почему многопоточный? Один поток на клиента или число потоков равно числу ядер? Какая задача решается? Крайне высокая нагрузка? Если так, то как она соотносится с ограниченной пропускной способностью сети?

L> допустим это счетчики (допустим, их количество фиксировано, для простоты), есть запрос на увеличение значения счетчика(ов) и запрос на чтение, при этом данные должны быть целостными (т.е. вариант — писать все команды на изменение в лог и потом применять — не катит), есть требование sequential consistency.


Ничего необычного, но вот это

   Counter& cnt = counters[q.id];  // shared state, контейнер не меняется, объекты Counter - могут меняться
   std::lock_guard<std::mutex> g(cnt.mutex);
   cnt.add(q.value);


Я бы спрятал внутрь функции за интерфейсом и никому снаружи бы не показывал. Чтобы ничьи шаловливые ручонки не применили этот мьютекс для чего еще.

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

L>Обработчики могут вызываться из кучи разных потоков, разными клиентскими соединениями. Объектов Counter у нас много и contention низкий, т.е. как правило разные потоки захватывают разные счетчики не мешая друг другу. Можно ввести небольшой oversubscription (потоков больше чем ядер) и все буедт загружено очень хорошо. Дедлок тут возникнуть не может, по очевидным причинам.


Ну так я же и обозначил это правило — граф вызовов под блокировкой не заходит в другой лок. Более того, там и графа-то никакого нет.

L>Вложенные блокировки могут быть полезны (мы, например, можем захотеть залочить объект — соединение(aka сокет), между получением запроса и отправкой ответа на него, так как в этот сокет могут приходить другие запросы, пока мы обрабатываем текущий и они могут начать обрабатываться в другом потоке (допустим это event based server с пуллом потоков).


Это уже даже звучит некрасиво

L>>Почему нельзя их разделить и передать потокам в эксклюзивоное пользование?

L>Из-за балансировки нагрузки. Если расшардить счетчики между потоками, можно получить ситуацию, когда один из потков будет загружен (там находится часто используемый счетчик), а другие недогружены.

Да неужели нагрузка действительно такая дикая, что ее нужно балансировать? Вот хоть убей, но мне кажется, что со всем этим фестивалем справился бы один поток. Ну а каждое соединение можно в корутину засунуть, чтобы вообще все шоколадно было.
www.blinnov.com
Re[21]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 27.06.14 20:57
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>Поздно уже предотвращать. Система в продакшене. Дедлок выстрелил, заслонка зависла в открытом положении, самолет упал, электростанция взорвалась.

BFE>Можно предотвратить заранее. Цена — не оптимальный алгоритм.

Нельзя. Если система заходит в дедлок, то боржоми пить уже поздно.

BFE>>>Классический пример — перевод денег:

L>>Перевод денег делается транзакциями с подтверждениями, а не локами с мьютексами.
BFE>Ну транзакция изнутри как устроена?

Это неважно. Важно то, что на верхнем уровне мы имеем дело с транзакцией, а не блокировками счетов.

L>>Ты хочешь вернуть систему в предыдущее состояние. Это в общем случае уже невозможно. Файл уже удален, заслонка открыта, газ идет, состояние (текщее и предыдущее) других ниток неизвестно, куда откатываться, непонятно, да и просто так закрыть заслонку нельзя.

BFE>Ну так надо с умом программу писать-то.

Вот именно. Писать надо с умом. В первую очередь так, чтобы статическая структура программы не допускала дедлоков (пресловутые не более одной блокировки в графе вызовов). Потому что обнаруживать их в рантайме уже крайне поздно.

BFE>Чем это ситуация отличается от брошенного исключения? Файл уже удален, заслонка открыта, газ идет...


Примерно всем. Исключение понятно как обработать. А дедлок? Что с ним делать? Вернуться назад? Куда назад? Никакого "назад" уже нет, начатая операция необратима, данные уже нерелевантны, остальные потоки ушли далеко вперед, а реактор уже кипит-с, можно только продолжать, а лок занят и освобождаться не собирается. Да даже если операция и обратима, то откатывать надо не только залипший тред, но надо размотать весь остальной клубок блокировок. разматывалки не хватит, да и исполнительные устройства могут отказаться разматываться на полпути.

BFE>>>Если мы применяем RAII, то можно бросить исключение и поймать его выше всех локов, после чего повторить попытку.

L>>Единственный более-менее приемлимый вариант — упасть с дампом. Что тоже очень плохо, но лучше зависания.
BFE>Упасть — это не вариант.

А заклинившая педаль газа, упавший самолет и взорвавшийся реактор — вариант?
www.blinnov.com
Re: Deadlock-prevention алгоритм
От: WinnieJayClay Финляндия  
Дата: 28.06.14 06:46
Оценка:
не читал весь стрим, но у java есть такая штука:
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/CycleDetectingLockFactory.html

возможно вы захотите реализовать подобное для C++
Re: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 28.06.14 14:23
Оценка:
Здравствуйте, Lazin, Вы писали:

L>Навелосипедил небольшую бибилотеку синхронизации, с целью проверить кое что — https://github.com/Lazin/Syncope

L>Там есть описание на rungilsh-е, идея очень простая на самом деле. Вместо того, чтобы добавлять мьютексы как поля в объекты, можно использовать внешний массив (пулл) мьютексов. Когда мы хотим залочить объект — мы берем его адрес, вычисляем простой хэш и используем его для получения мьютекса из пулла, после чего захватываем его. Для того, чтобы это работало без дедлоков, нужно чтобы была подходящая иерархия локов в программе. Вот такой вот массив может использоваться только на одном уровне иерархии.

Кстати, ошибочка одна вот, массив хэшей в guard many мало отсортировать... Надо ещё повторения выкинуть.
            std::sort(hashes_.begin(), hashes_.end());
            hash_array::iterator it = std::unique(hashes_.begin(), hashes_.end());
            hash_count_ = std::distance(hashes_.begin(), it);


Ну и блокировать только то, что отсеялось:
        void lock() {
            for(int i = 0; i < hash_count_; ++i) {
                std::cout << "lock " << i << std::endl;
                impl_.lock(hashes_[i]);
            }
            owns_lock_ = true;
        }

        void unlock() {
            for(int i = hash_count_ - 1; i >= 0; --i) {
                std::cout << "unlock " << i << std::endl;
                impl_.unlock(hashes_[i]);
            }
            owns_lock_ = false;
        }
Re[14]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.06.14 17:12
Оценка:
L>>Многопоточный TCP сервер, работающий с неким набором объектов,

L>Можно сразу вопросы? Почему многопоточный? Один поток на клиента или число потоков равно числу ядер? Какая задача решается? Крайне высокая нагрузка? Если так, то как она соотносится с ограниченной пропускной способностью сети?


Пулл потоков. Пропускная способность сети — вещь вообще иррелевантная для данного примера. Пропускная способность такого сервера будет зависет от латентностей разных подсистем, в том числе и времени обработки запроса. Когда какой-нибудь программист говорит о том, что сервис упирается в сеть, очень часто он упирается не в ее пропускную способность Но это уже тема для отдельного разговора и отношения к вопросу не имеет.

L>Я бы спрятал внутрь функции за интерфейсом и никому снаружи бы не показывал. Чтобы ничьи шаловливые ручонки не применили этот мьютекс для чего еще.

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

А что если в одном запросе нужно обновлять сразу несколько счетчиков? Вот сразу и атомики не подходят и в том что мьютекс торчит наружу нет ничего плохого

L>Ну так я же и обозначил это правило — граф вызовов под блокировкой не заходит в другой лок. Более того, там и графа-то никакого нет.


Ну так мы же вроде не это обсуждаем, а твое утверждение о том, что меньше мьютексов тем лучше.

L>Да неужели нагрузка действительно такая дикая, что ее нужно балансировать? Вот хоть убей, но мне кажется, что со всем этим фестивалем справился бы один поток. Ну а каждое соединение можно в корутину засунуть, чтобы вообще все шоколадно было.


Простой пример — один горячий элемент, который обновляют все кому не лень. Есть такая задача в параллельном програмировании — зоопарк Шредингера, есть список животных живущих в зоопарке, клиенты могут запрашивать информацию о них, но почему то чаще всего запрашивают информацию о коте Шредингера
Re[2]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.06.14 17:55
Оценка:
S>Кстати, ошибочка одна вот, массив хэшей в guard many мало отсортировать... Надо ещё повторения выкинуть.

Да, там просто раньше были рекурсивные мьютексы.

S>
S>            std::sort(hashes_.begin(), hashes_.end());
S>            hash_array::iterator it = std::unique(hashes_.begin(), hashes_.end());
S>            hash_count_ = std::distance(hashes_.begin(), it);

S>


S>Ну и блокировать только то, что отсеялось:

S>
S>        void lock() {
S>            for(int i = 0; i < hash_count_; ++i) {
S>                std::cout << "lock " << i << std::endl;
S>                impl_.lock(hashes_[i]);
S>            }
S>            owns_lock_ = true;
S>        }

S>        void unlock() {
S>            for(int i = hash_count_ - 1; i >= 0; --i) {
S>                std::cout << "unlock " << i << std::endl;
S>                impl_.unlock(hashes_[i]);
S>            }
S>            owns_lock_ = false;
S>        }
S>


Может отправишь pull request?
Re[10]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.06.14 18:38
Оценка:
S>Хотя можно и на месте можно lock-free получать факт deadlock'а потенциального с двумерным массивом. Предположим два потока одновременно регистрируют взаимно невозможные переходы layer1-layer2 и layer2-layer1. Нужно упорядочить регистрацию переходов. Для этого просто будем использовать половину матрицы, т.е. оба перехода фиксируем в переменной transitions[1][2] (упорядочиваем индексы). Прямой переход — значение 1, обратный переход — значение 2. Ну и дальше atomic CAS, допустимы изменения 0-1, 1-1, 0-2, 2-2. При фиксации попыток изменений 1-2 или 2-1 сигнализируем deadlock.

Я сейчас начал реализовывать эту идею (после первого прочтения неправильно понял). Можно же сделать достаточно большой массив заранее и не нужно будет ничего ресайзить, ну и там действительно можно обойтись одним CAS-ом.
Re[15]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 28.06.14 18:44
Оценка:
Здравствуйте, Lazin, Вы писали:

L>Пулл потоков. Пропускная способность сети — вещь вообще иррелевантная для данного примера.


Вобще-то это самый первый вопрос, который возникает при такой разработке.

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


А еще нередко какой-нибудь программист норовит применить алгоритм с O(n^n) там, где просится O(1)

L>Но это уже тема для отдельного разговора и отношения к вопросу не имеет.


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

L>>Я бы спрятал внутрь функции за интерфейсом и никому снаружи бы не показывал. Чтобы ничьи шаловливые ручонки не применили этот мьютекс для чего еще.

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

L>А что если в одном запросе нужно обновлять сразу несколько счетчиков? Вот сразу и атомики не подходят и в том что мьютекс торчит наружу нет ничего плохого


А в чем проблема? Data continuity никаким образом не нарушается. Хочешь транзакций с изоляциями? А оно тебе надо? (не обижайся, я всегда такие вопросы задаю, в 90% случаев оказывается, что на самом деле не надо. Не следует пытаться сделать больше, чем требуется.) Если надо, то так бы сразу и сказал. Но это уже совсем другая история, не так ли? Я правильно понимаю, что ты хочешь захватить сразу все локи, и только потом обновлять? Ну так это же прямой путь к дедлоку, как только залетит дятел и порядок блокировки будет нарушен.

L>>Ну так я же и обозначил это правило — граф вызовов под блокировкой не заходит в другой лок. Более того, там и графа-то никакого нет.


L>Ну так мы же вроде не это обсуждаем, а твое утверждение о том, что меньше мьютексов тем лучше.


Вообще-то это скорее так, нежели нет. Смотри выше.

L>>Да неужели нагрузка действительно такая дикая, что ее нужно балансировать? Вот хоть убей, но мне кажется, что со всем этим фестивалем справился бы один поток. Ну а каждое соединение можно в корутину засунуть, чтобы вообще все шоколадно было.


L>Простой пример — один горячий элемент, который обновляют все кому не лень. Есть такая задача в параллельном програмировании — зоопарк Шредингера, есть список животных живущих в зоопарке, клиенты могут запрашивать информацию о них, но почему то чаще всего запрашивают информацию о коте Шредингера


Видишь ли в чем дело. Ты пока не обосновал необходимость многопоточности. Если все, что твой сервис должен делать — это на запросы справляться о здоровье кота Шредингера, то вопрос о пропускной способности очереди клиентов нужно рассматривать первым. Учитывая имеющиеся входные данные, я пока вижу, что твой сервис в любом случае будет успевать отработать быстрее, нежели следующий запрос успеет прийти и быть обработанным сетевой подсистемой. Короче, один поток справится только в путь.
www.blinnov.com
Re[10]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.06.14 18:59
Оценка:
S>Хотя можно и на месте можно lock-free получать факт deadlock'а потенциального с двумерным массивом. Предположим два потока одновременно регистрируют взаимно невозможные переходы layer1-layer2 и layer2-layer1. Нужно упорядочить регистрацию переходов. Для этого просто будем использовать половину матрицы, т.е. оба перехода фиксируем в переменной transitions[1][2] (упорядочиваем индексы). Прямой переход — значение 1, обратный переход — значение 2. Ну и дальше atomic CAS, допустимы изменения 0-1, 1-1, 0-2, 2-2. При фиксации попыток изменений 1-2 или 2-1 сигнализируем deadlock.

Правда так можно только deadlock первого порядка отловить, чтобы отловить deadlock N-ого порядка, нужно проверить N^2 таких переходов (если N это глубина стека вызовов).
Re[19]: Deadlock-prevention алгоритм
От: xp1icit  
Дата: 29.06.14 07:02
Оценка:
BFE>Классический пример — перевод денег:
BFE>залочили первый счёт, списали деньги,
BFE>залочили второй счёт, добавили туда списанные деньги,
BFE>разлочили второй счёт,
BFE>разлочили первый счёт.
BFE>Очевидно, что здесь возможен классический дедлок.

элементарно же:
1) лочим оба счета в однозначно определяемом порядке (типа сначала с меньшим номером и т.п.)
2) переводим деньги
3) разлочиваем в обратном порядке

эту странную ситуацию — когда двум тредам нужно залочить два объекта ну обязательно в разном порядке — вряд ли невозможно подобным простым образом исключить
Re[20]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 29.06.14 09:14
Оценка:
Здравствуйте, xp1icit, Вы писали:

BFE>>Классический пример — перевод денег:


Классический, но академический. Ничего общего с тем, как это реально просходит, не имеет.

X>элементарно же:


Нет. В общем случае доступа ко второму счету нет. Это может быть счет в другом банке, другой системе и т.п. Заявка на перевод может требовать ручной обработки или промежуточного подтверждения.

1. Создается транзакция с параметрами "Перевод Х денег со счета А на счет Б".
2. Со счета А списывается Х денег. В зависимости от реализации они могут не списываться, а блокироваться.
3. Транзакция направляется по адресу. На обработку, проверку, в другой банк, в ФСБ и так далее.
4а. Система-адресат получает транзакцию, выполняет необходимые проверки, зачисляет деньги на счет и отправляет обратно подтверждение транзакции
4б. Система-адресат получает транзакцию, выполняет необходимые проверки, говорит "ЧТО ЭТО" и отправляет обратно отлуп.
4в. транзакция теряется по пути.
5а. Банк-отправитель получает подтверждение. В зависимости от реализации может блокированные средства перевести в списанные.
5б. Банк-отправитель получает подтверждение и возвращает деньги на счет.
5в. Спустя какое-то время взебешнный клиент начинает медленно плоскогубцами выдергивать волосы сотрудникам поддержки банка.
www.blinnov.com
Re[16]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 29.06.14 09:41
Оценка:
L>Имеет, т.к. пока непонятно, из каких требований отросло требование именно многопоточности. Любое решение должно быть обосновано, и часто бывает, что архитектурные решения принимаются из ошибочных исходных данных или ошибочных представлениях о требованиях.

Ну, например, клиент хочет купить более дорогой сервер и получить прирост, вместо уменьшения производительности. Вот купил он сервер с 4-мя зеонами у каждого из которых по 12 HT хардварных потока, запустил твой софт, в котором "число мьютексов строго соответствует колиеству месть взаимодействиям между потоками" и сервер начал вместо 100 000 rps выдавать 80 000 rps. А все потому что синхронизация теперь выполняется чаще (так как потоков стало больше, чтобы утилизировать более мощное железо) и любое использование мьютекса приводит к реальному взаимодействию между потоками (ибо их мало), но синхронизация у нас теперь выходит за границы сокета и все латентности выросли. Просто потому что у кого-то выработалась неправильная интуиция об этом во времена одноядерных машин, когда любая синхронизация шла в ядро и потоки всегда мешали друг другу, так как работали на одном ядре

Мне кажется, правило "число мьютексов строго соответствует колиеству месть взаимодействиям между потоками" в современном мире несколько иррелевантно. Во первых, следование этому правилу может усложнить код, в моем примере проще всего натыкать мьютексов и все. Если стараться следовать твоему правилу — код получится сложнее, вместо лока придется сигналить выделенному потоку или шардить данные между потоками. С точки зрения перфоманса это тоже не имеет большого смысла. Если я пишу какой-ниубдь сервер и у меня на каждый харварный поток приходится один софтверный поток, то в случае, если количество мьютексов равно количеству мест взаимодействиия потоков, захватывая мьютекс я практически гарантировано буду создавать contention, а это как раз то, что убивает перфоманс в параллельных системах. Относись к большому количеству мьютексов как к шардингу, только вместо данных ты шардишь мьютексы у тебя есть место взаимодействия между потоками и ему соответствует один мьютекс, ты берешь и реплицируешь его, чтобы уменьшить конкуренцию за отдельные мьютексы так, чтобы каждый отдельный мьютекс захватывался редко и чаще всего — только одним потоком, но корректность при этом не пострадала.

L>>А что если в одном запросе нужно обновлять сразу несколько счетчиков? Вот сразу и атомики не подходят и в том что мьютекс торчит наружу нет ничего плохого


L>А в чем проблема? Data continuity никаким образом не нарушается. Хочешь транзакций с изоляциями? А оно тебе надо? (не обижайся, я всегда такие вопросы задаю, в 90% случаев оказывается, что на самом деле не надо. Не следует пытаться сделать больше, чем требуется.) Если надо, то так бы сразу и сказал. Но это уже совсем другая история, не так ли? Я правильно понимаю, что ты хочешь захватить сразу все локи, и только потом обновлять? Ну так это же прямой путь к дедлоку, как только залетит дятел и порядок блокировки будет нарушен.


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

L>>Простой пример — один горячий элемент, который обновляют все кому не лень. Есть такая задача в параллельном програмировании — зоопарк Шредингера, есть список животных живущих в зоопарке, клиенты могут запрашивать информацию о них, но почему то чаще всего запрашивают информацию о коте Шредингера


L>Видишь ли в чем дело. Ты пока не обосновал необходимость многопоточности. Если все, что твой сервис должен делать — это на запросы справляться о здоровье кота Шредингера, то вопрос о пропускной способности очереди клиентов нужно рассматривать первым. Учитывая имеющиеся входные данные, я пока вижу, что твой сервис в любом случае будет успевать отработать быстрее, нежели следующий запрос успеет прийти и быть обработанным сетевой подсистемой. Короче, один поток справится только в путь.


10g сейчас в каждом датацентре (это если мне память не изменяет 10 000 000 пакетов в секунду), обработать все что валится через обычный GbE может быть очень сложно, а еще на одном серваке может быть несколько сетевых интерфейсов, которые, в свою очередь, могут быть подключены к двум разным свитчам.
Re[11]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 29.06.14 17:26
Оценка:
Здравствуйте, Lazin, Вы писали:

L>Правда так можно только deadlock первого порядка отловить, чтобы отловить deadlock N-ого порядка, нужно проверить N^2 таких переходов (если N это глубина стека вызовов).


thread-local можно стек и не собирать, просто последний заблокированный слой держать, атомарно отмечаем переход от слоя к слою.
И если такой переход случился впервые, синхронизируемся (чтобы все изменения observable были) и проверяем граф на циклы. Со всякими топологическими сортировками можно не заморачиваться, просто рекурсивно от ноды на которую переход пошёл пробежаться и проверить по глубине что все пути конечны (всё равно число слоев ограничили). Т.е. переход на предыдущую ноду использовать как признак зацикливания надо, но только на неё полагаться нельзя, т.к. могли быть другие циклы в параллель внесены. Тогда все потенциальные deadlock'и вылезти должны.
Re[12]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 29.06.14 20:23
Оценка:
Здравствуйте, sokel, Вы писали:

S>Здравствуйте, Lazin, Вы писали:


L>>Правда так можно только deadlock первого порядка отловить, чтобы отловить deadlock N-ого порядка, нужно проверить N^2 таких переходов (если N это глубина стека вызовов).


S>thread-local можно стек и не собирать, просто последний заблокированный слой держать, атомарно отмечаем переход от слоя к слою.

S>И если такой переход случился впервые, синхронизируемся (чтобы все изменения observable были) и проверяем граф на циклы. Со всякими топологическими сортировками можно не заморачиваться, просто рекурсивно от ноды на которую переход пошёл пробежаться и проверить по глубине что все пути конечны (всё равно число слоев ограничили). Т.е. переход на предыдущую ноду использовать как признак зацикливания надо, но только на неё полагаться нельзя, т.к. могли быть другие циклы в параллель внесены. Тогда все потенциальные deadlock'и вылезти должны.


Как то так:
#include <stack>
#include <atomic>

#define MAX_LAYERS 100
class deadlock_detector
{
    deadlock_detector()
        : layer_count_()
        , deadlock_(false)
    {}
public:
    static deadlock_detector& get() {
        static deadlock_detector instance;
        return instance;
    }
    size_t register_layer(const char* name) {
        std::lock_guard<std::mutex> lock(mtx_);
        size_t ret = (++layer_count_) - 1;
        registered_layers_[ret] = name;
        return ret;
    }
    void on_lock(size_t i, bool enter) {
        __declspec(thread) static std::stack<size_t>* lock_stack = 0;
        if (lock_stack == 0)
            lock_stack = new std::stack<size_t>();
        if (enter) {
            if (!lock_stack->empty())
                on_transition(lock_stack->top(), i);
            lock_stack->push(i);
        }
        else {
            lock_stack->pop();
        }
    }
private:
    void on_transition(size_t i, size_t j) {
        size_t a, b;
        int trans;
        if (i < j) { a = i; b = j; trans = 1; }
        else { a = j; b = i; trans = 2; }
        std::atomic_int& edge = transitions_[a][b];
        if (edge == trans)
            return;
        int prev_trans = 0;
        std::atomic_compare_exchange_weak(&edge, &prev_trans, trans);
        std::lock_guard<std::mutex> lock(mtx_);
        if (!deadlock_) {
            if (prev_trans != trans || !check_cycle(j, i)) {
                deadlock_ = true;
                std::cout << "deadlock detected" << std::endl;
                print_transitions();
                //assert(false);
            }
        }
    }
    void print_transitions() {
        for (int j = 0; j < layer_count_; ++j) {
            for (int i = 0; i <= j; ++i) {
                int t = transitions_[i][j];
                if(!t) continue;
                int a = i, b = j;
                if (t != 1) std::swap(a, b);
                std::cout << registered_layers_[a] << "->" << registered_layers_[b] << std::endl;
            }
        }

    }
    bool check_cycle(int x, int init, int depth = 1) {
        if (depth > layer_count_) return true;
        if (x == init) return true;
        bool ret = false;
        int i = 0;
        while (!ret && i < x) {
            if (transitions_[i][x] == 2)
                ret = check_cycle(i, init, depth + 1);
            ++i;
        }
        if (transitions_[i][i]) return true;
        while (!ret && i < layer_count_) {
            if (transitions_[x][i] == 1)
                ret = check_cycle(i, init, depth + 1);
            ++i;
        }
        return ret;
    }
private:
    bool deadlock_;
    size_t layer_count_;
    std::mutex mtx_;
    const char* registered_layers_[MAX_LAYERS];
    std::atomic_int transitions_[MAX_LAYERS][MAX_LAYERS];
};
Re[13]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 29.06.14 20:34
Оценка:
Здравствуйте, sokel, Вы писали:

Тут и атомики конечно не нужны, можно volatile обойтись.
Re[14]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 29.06.14 20:47
Оценка:
Здравствуйте, sokel, Вы писали:

S>Здравствуйте, sokel, Вы писали:


S>Тут и атомики конечно не нужны, можно volatile обойтись.


Тем более с атомиками ошибка....

Так вроде работает (просто volatile, без атомиков):
    void on_transition(size_t i, size_t j) {
        size_t a, b;
        int trans;
        if (i < j) { a = i; b = j; trans = 1; }
        else { a = j; b = i; trans = 2; }
        if (transitions_[a][b] == trans)
            return;
        std::lock_guard<std::mutex> lock(mtx_);
        if (transitions_[a][b] && transitions_[a][b] != trans)
            on_deadlock();
        else {
            transitions_[a][b] = trans;
            if (check_cycle(j, i))
                on_deadlock();
        }
    }
    void on_deadlock() {
        if (!deadlock_) {
            deadlock_ = true;
            std::cout << "deadlock detected" << std::endl;
            print_transitions();
        }
    }


тест:

syncope::SymmetricLockLayer l1(STATIC_STRING("layer 1"));
syncope::SymmetricLockLayer l2(STATIC_STRING("layer 2"));
syncope::SymmetricLockLayer l3(STATIC_STRING("layer 3"));

int64_t a[1000];

int main() {

    std::thread t1([]()
    {
        auto g1 = l1.synchronize(&a[0]);
        auto g2 = l2.synchronize(&a[0]);
    });
    std::thread t2([]()
    {
        auto g1 = l2.synchronize(&a[100]);
        auto g2 = l3.synchronize(&a[100]);
    });
    std::thread t3([]()
    {
        auto g1 = l3.synchronize(&a[200]);
        auto g2 = l1.synchronize(&a[200]);
    });

    t1.join();
    t2.join();
    t3.join();

    return 0;
}


Реального deadlock'a нет, блокируются независимо 0-1, 1-2, 2-3. Цикл определяет:
deadlock detected
layer 1->layer 2
layer 3->layer 1
layer 2->layer 3
Re[15]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 29.06.14 21:47
Оценка:
Здравствуйте, sokel, Вы писали:

А раз атомимки не нужны, можно факт перехода вообще по простому битом отмечать. Единственно минус в том что когда все слои созданы надо initialize сказать.
#pragma once
#include <stack>
#include <vector>
#include <atomic>

class deadlock_detector
{
    deadlock_detector()
        : deadlock_(false)
    {}
public:
    static deadlock_detector& get() {
        static deadlock_detector instance;
        return instance;
    }
    size_t register_layer(const char* name) {
        std::lock_guard<std::mutex> lock(mtx_);
        assert(transitions_.empty());
        registered_layers_.push_back(name);
        return registered_layers_.size() - 1;
    }
    void initialize() {
        std::lock_guard<std::mutex> lock(mtx_);
        assert(transitions_.empty());
        transitions_.resize(registered_layers_.size() * registered_layers_.size());
    }
    void on_lock(size_t i, bool enter) {
        __declspec(thread) static std::stack<size_t>* lock_stack = 0;
        if (lock_stack == 0)
            lock_stack = new std::stack<size_t>();
        if (enter) {
            if (!lock_stack->empty())
                on_transition(lock_stack->top(), i);
            lock_stack->push(i);
        }
        else {
            lock_stack->pop();
        }
    }
private:
    void on_transition(size_t i, size_t j) {
        assert(!transitions_.empty());
        if (get_transition(i, j))
            return;
        std::lock_guard<std::mutex> lock(mtx_);
        add_transition(i, j);
        if (check_cycle(j, i))
            on_deadlock();
    }
    void on_deadlock() {
        if (!deadlock_) {
            deadlock_ = true;
            std::cout << "deadlock detected" << std::endl;
            print_transitions();
        }
    }
    void print_transitions() const {
        for (int i = 0; i < layer_count(); ++i) {
            for (int j = 0; j < layer_count(); ++j) {
                if (!get_transition(i, j)) continue;
                std::cout << registered_layers_[i] << "->" << registered_layers_[j] << std::endl;
            }
        }
    }
    bool check_cycle(int x, int init, int depth = 1) const {
        if (depth > layer_count() || x == init)
            return true;
        bool ret = false;
        for (int i = 0; i < layer_count() && !ret; ++i) {
            if (get_transition(x, i))
                ret = check_cycle(i, init, depth + 1);
        }
        return ret;
    }
    bool get_transition(size_t i, size_t j) const {
        return transitions_[i*layer_count() + j];
    }
    void add_transition(size_t i, size_t j) {
        transitions_[i*layer_count() + j] = true;
    }
    size_t layer_count() const {
        return registered_layers_.size();
    }
private:
    bool deadlock_;
    std::mutex mtx_;
    std::vector<const char*> registered_layers_;
    std::vector<bool> transitions_;
};
Re[17]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 30.06.14 06:54
Оценка:
Здравствуйте, Lazin, Вы писали:

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


L>Ну, например, клиент хочет купить более дорогой сервер и получить прирост, вместо уменьшения производительности. Вот купил он сервер с 4-мя зеонами у каждого из которых по 12 HT хардварных потока, запустил твой софт, в котором "число мьютексов строго соответствует колиеству месть взаимодействиям между потоками" и сервер начал вместо 100 000 rps выдавать 80 000 rps.


Или вместо 100 попугаев — 150. А все потому, что под синхронизацией каждая операция проводит не более 1% операций, требуемых для обработки одного запроса и тупо добавление ядер позволило эффективно распараллелить остальные 99% операций.

L>Просто потому что у кого-то выработалась неправильная интуиция об этом во времена одноядерных машин, когда любая синхронизация шла в ядро и потоки всегда мешали друг другу, так как работали на одном ядре


Правильность или неправильность интуиции нельзя обсуждать в контексте сферического сервера в вакууме.

L>Если я пишу какой-ниубдь сервер и у меня на каждый харварный поток приходится один софтверный поток, то в случае, если количество мьютексов равно количеству мест взаимодействиия потоков, захватывая мьютекс я практически гарантировано буду создавать contention, а это как раз то, что убивает перфоманс в параллельных системах.


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

L>Относись к большому количеству мьютексов как к шардингу, только вместо данных ты шардишь мьютексы у тебя есть место взаимодействия между потоками и ему соответствует один мьютекс, ты берешь и реплицируешь его, чтобы уменьшить конкуренцию за отдельные мьютексы так, чтобы каждый отдельный мьютекс захватывался редко и чаще всего — только одним потоком, но корректность при этом не пострадала.


И получаешь проблему, вынесенную в название этой темы? Спасибо, не надо. Лучше надежно работающий сервис, чем не работающий вообще. Сервис, допускающий дедлоки — нерабочий.

L>>>А что если в одном запросе нужно обновлять сразу несколько счетчиков? Вот сразу и атомики не подходят и в том что мьютекс торчит наружу нет ничего плохого


L>>А в чем проблема? Data continuity никаким образом не нарушается. Хочешь транзакций с изоляциями? А оно тебе надо? (не обижайся, я всегда такие вопросы задаю, в 90% случаев оказывается, что на самом деле не надо. Не следует пытаться сделать больше, чем требуется.) Если надо, то так бы сразу и сказал. Но это уже совсем другая история, не так ли? Я правильно понимаю, что ты хочешь захватить сразу все локи, и только потом обновлять? Ну так это же прямой путь к дедлоку, как только залетит дятел и порядок блокировки будет нарушен.


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


Не знаю о каком репозитории ты говоришь, но тут ды допустил сразу несколько логических ошибок. Во-первых, у тебя есть куча совершенно независимых друг от друга данных (отдельный мьютекс на каждый индикатор на это намекает). Попытка последовательно блокировать все мьютексы перед досупом/апдейдом тебе не даст ровным счетом ничего. Никакой транзакционности. Ты не получишь ничего такого, чего не смог бы получить при помощи атомиков, зато устраиваешь себе свой любимый contention, да еще и с риском дедлока. С точки зрения клиента разницы в поведении не будет, т.к. что ровно в момент начала транзакции, что в любой момент после ее начала, может вмешаться другой поток и изменить значение N-ного элемента из запрошенного набора данных. Причем, пока этот второй поток этот элемент меняет, твой первый поток курит бамбук. Посему отказ от атомиков тут пока никак не обоснован.

А если ты хочешь транзакционности, то вот ее и нужно реализовывать, взяв за основу один из механизмов, используемых в СУБД, например. Только в случае блокировок блокировать блокировать сразу целые таблицы, а не отдельные записи. Ничего не напоминает?

L>10g сейчас в каждом датацентре (это если мне память не изменяет 10 000 000 пакетов в секунду),


Размер ethernet фрейма не фиксирован. Он лишь обычно ограничен сверху.

L>обработать все что валится через обычный GbE может быть очень сложно, а еще на одном серваке может быть несколько сетевых интерфейсов, которые, в свою очередь, могут быть подключены к двум разным свитчам.


Информация для размышления. Грубый подсчет на пальцах: 10g это примерно 1Гб/с за минусом накладных расходов. Если запрос имеет длину... ну 100 байт, ну возьмем 1000, то это дает миллион запросов в секунду. Что на порядок больше, чем в среднем получает гугль. Что-то очень часто народ интересуется здоровьем кота Шредингера
Короче, без требований к нагрузочной способности и выяснения, где у нас тут бутылочное горлышко, рассуждать о необходимости многопоточности сложно.
www.blinnov.com
Re[3]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 30.06.14 07:25
Оценка:
Здравствуйте, Lazin, Вы писали:

Ещё вот такая штука — не лучше ли при расчёте хэшей завязываться на sizeof? Т.е. сейчас хэш по адресу считается с шагом адреса в 64 байта. Допустим у нас размер наиболее часто блокируемых объектов 128 байт, причем аллокируются объекты на страницах с выравниванием не меньше чем 128 байт. Т.о. из всего набора мьютексов для них будет использоваться только половина. При размере 256 байт будет использоваться только четверть мьютексов, ну и так далее.

Такой хэш будет давать лучшее распределение блокировок:
        //! Simple hash - simply returns it's argument
        struct SimpleHash {
            template<typename T>
            size_t operator() (const T* value) const {
                return reinterpret_cast<size_t>(value)/sizeof(T);
            }
        };
Re[4]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 30.06.14 07:32
Оценка:
Здравствуйте, sokel, Вы писали:

А, ну и мало того, маскирование по размеру пула нужно тоже в хэш функцию вынести, иначе unique бесполезен.

        //! Simple hash - simply returns it's argument
        struct SimpleHash {
            template<typename T>
            size_t operator() (const T* value) const {
                return (reinterpret_cast<size_t>(value) / sizeof(T))&LockLayerImpl::MASK;
            }
        };
Re[18]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 30.06.14 07:48
Оценка:
Я, честно говоря, задолбался уже спорить о сферических конях в вакууме, если бы не нужно было сидеть и ждать эвакуатор целый час...

L>>Ну, например, клиент хочет купить более дорогой сервер и получить прирост, вместо уменьшения производительности. Вот купил он сервер с 4-мя зеонами у каждого из которых по 12 HT хардварных потока, запустил твой софт, в котором "число мьютексов строго соответствует колиеству месть взаимодействиям между потоками" и сервер начал вместо 100 000 rps выдавать 80 000 rps.

L>Или вместо 100 попугаев — 150. А все потому, что под синхронизацией каждая операция проводит не более 1% операций, требуемых для обработки одного запроса и тупо добавление ядер позволило эффективно распараллелить остальные 99% операций.

Ну так а как они распараллелятся, если мьютекс один (твой идеальный случай, идеальнее некуда — одно место взаимодействия потоков с одним мьютексом в серединке).

L>Правильность или неправильность интуиции нельзя обсуждать в контексте сферического сервера в вакууме.


Я, собственно, для этого конкретный пример и предложил.

L>>Если я пишу какой-ниубдь сервер и у меня на каждый харварный поток приходится один софтверный поток, то в случае, если количество мьютексов равно количеству мест взаимодействиия потоков, захватывая мьютекс я практически гарантировано буду создавать contention, а это как раз то, что убивает перфоманс в параллельных системах.

L>В твоем примере для создания дикой конкуренции достаточно, чтобы все клиенты стали в каждом запросе интересоваться здоровьем кота Шредингера. И никакого контроля за этим ты не имеешь.

Ну так остальные запросы простаивать то тоже не будут, они будут обслуживаться другими ядрами, так как запрос может обработать любое ядро.

L>>Относись к большому количеству мьютексов как к шардингу, только вместо данных ты шардишь мьютексы у тебя есть место взаимодействия между потоками и ему соответствует один мьютекс, ты берешь и реплицируешь его, чтобы уменьшить конкуренцию за отдельные мьютексы так, чтобы каждый отдельный мьютекс захватывался редко и чаще всего — только одним потоком, но корректность при этом не пострадала.

L>И получаешь проблему, вынесенную в название этой темы? Спасибо, не надо. Лучше надежно работающий сервис, чем не работающий вообще. Сервис, допускающий дедлоки — нерабочий.

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

L>>>>А что если в одном запросе нужно обновлять сразу несколько счетчиков? Вот сразу и атомики не подходят и в том что мьютекс торчит наружу нет ничего плохого


L>>>А в чем проблема? Data continuity никаким образом не нарушается. Хочешь транзакций с изоляциями? А оно тебе надо? (не обижайся, я всегда такие вопросы задаю, в 90% случаев оказывается, что на самом деле не надо. Не следует пытаться сделать больше, чем требуется.) Если надо, то так бы сразу и сказал. Но это уже совсем другая история, не так ли? Я правильно понимаю, что ты хочешь захватить сразу все локи, и только потом обновлять? Ну так это же прямой путь к дедлоку, как только залетит дятел и порядок блокировки будет нарушен.


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


L>Не знаю о каком репозитории ты говоришь, но тут ды допустил сразу несколько логических ошибок. Во-первых, у тебя есть куча совершенно независимых друг от друга данных (отдельный мьютекс на каждый индикатор на это намекает). Попытка последовательно блокировать все мьютексы перед досупом/апдейдом тебе не даст ровным счетом ничего. Никакой транзакционности. Ты не получишь ничего такого, чего не смог бы получить при помощи атомиков, зато устраиваешь себе свой любимый contention, да еще и с риском дедлока. С точки зрения клиента разницы в поведении не будет, т.к. что ровно в момент начала транзакции, что в любой момент после ее начала, может вмешаться другой поток и изменить значение N-ного элемента из запрошенного набора данных. Причем, пока этот второй поток этот элемент меняет, твой первый поток курит бамбук. Посему отказ от атомиков тут пока никак не обоснован.


Я говорю о моем репозитории с кодом из первого поста. Если все это тлен, как ты говоришь, то зря я парюсь, нужно удалить его и все. Eine Frau, ein Loch, eine mutex!
Итак, атомики — не нужны, ибо мне может потребоваться не только складывать числа (это вообще высосано из пальца для иллюстрации идеи). Это может быть регистрация показания датчика или click stream от пользователя сайта. Помимо этого, атомики имеют примерно ту же стоимость, что и локи (если речь идет о спин локе), атомик за который есть конкуренция потоков будет медленным (RMW операция за такую переменную будет стоить сотен тактов, как и в случае спинлока), так что атомики на параллельную архитектуру влияют мало. Один хрен нужно ограничивать конкуренцию потоков (aka contention). Ну и если ты не в курсе — захват локов в одном, глобавльном, фиксированном порядке гарантирует целостность данных (если ты конечно захватил все локи) и отсутствие дедлоков.

L>А если ты хочешь транзакционности, то вот ее и нужно реализовывать, взяв за основу один из механизмов, используемых в СУБД, например. Только в случае блокировок блокировать блокировать сразу целые таблицы, а не отдельные записи. Ничего не напоминает?


Зависит от СУБД, в постгресе вон вообще без локов таблиц обходоится обычно.

L>Информация для размышления. Грубый подсчет на пальцах: 10g это примерно 1Гб/с за минусом накладных расходов. Если запрос имеет длину... ну 100 байт, ну возьмем 1000, то это дает миллион запросов в секунду. Что на порядок больше, чем в среднем получает гугль. Что-то очень часто народ интересуется здоровьем кота Шредингера

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

Есть задачи где на один сервер может валиться больше. Статистика гугла показывает запросы пользователей. Получается, что на предыдущей работе у нас сервер, занимающийся сбором time-series данных от сенсоров обрабатывал больше запросов на запись в секунду, чем весь гугл? А может ты просто теплое с мягким сравниваешь? Я могу сказать с уверенностью, что всякие time-series данные от всяких сенсоров, клик стримы (и вообще обработка стримов), RTB, баннерокрутилки и тд и тп — легко могут требовать дикой конкурентности и выдают как правило 100500 запросов в сек и больше (буквально).
Re[19]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 30.06.14 08:49
Оценка:
Здравствуйте, Lazin, Вы писали:

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


Зачем ты ее поломал?

L>>Или вместо 100 попугаев — 150. А все потому, что под синхронизацией каждая операция проводит не более 1% операций, требуемых для обработки одного запроса и тупо добавление ядер позволило эффективно распараллелить остальные 99% операций.


L>Ну так а как они распараллелятся, если мьютекс один (твой идеальный случай, идеальнее некуда — одно место взаимодействия потоков с одним мьютексом в серединке).


99% процентов времени каждый поток занят своим делом. Нормальное такое распараллелирвание. Будут, конечено, время от времени возникать коллизии, но ничего критичного не произойдет.

L>>В твоем примере для создания дикой конкуренции достаточно, чтобы все клиенты стали в каждом запросе интересоваться здоровьем кота Шредингера. И никакого контроля за этим ты не имеешь.


L>Ну так остальные запросы простаивать то тоже не будут, они будут обслуживаться другими ядрами, так как запрос может обработать любое ядро.


А остальных запросов-то и нет. Всем нужен кот.

L>>И получаешь проблему, вынесенную в название этой темы? Спасибо, не надо. Лучше надежно работающий сервис, чем не работающий вообще. Сервис, допускающий дедлоки — нерабочий.


L>Если голова на месте, то не получаешь. И каким образом из того что я написал следует то, что сервис допускает дедлоки?


Ну так а проблема-то откуда-то ведь выросла?

L>>>>>А что если в одном запросе нужно обновлять сразу несколько счетчиков? Вот сразу и атомики не подходят и в том что мьютекс торчит наружу нет ничего плохого


L>Я говорю о моем репозитории с кодом из первого поста. Если все это тлен, как ты говоришь, то зря я парюсь, нужно удалить его и все. Eine Frau, ein Loch, eine mutex!


Речь не об одном мьютексе, а об архитектуре. И таки да, я буду продолжать утрвеждать, что детекторы дедлоков имеют мало смысла с точки зрения верификации ПО. Если архитектура настолько ущербна, что допускает дедлоки, то ничего, кроме ее переработки, не поможет. Детектор может помочь выявить и даже несколько дедлоков, но он не способен гарантировать отсутствие других, не обнаруженных пока, локов. Как инструмент раскопок в говнокоде, который нужно заставить хоть как-то работать, их использовать можно. Вопрос только, нужно ли?

L>Итак, атомики — не нужны, ибо мне может потребоваться не только складывать числа (это вообще высосано из пальца для иллюстрации идеи). Это может быть регистрация показания датчика или click stream от пользователя сайта. Помимо этого, атомики имеют примерно ту же стоимость, что и локи (если речь идет о спин локе), атомик за который есть конкуренция потоков будет медленным (RMW операция за такую переменную будет стоить сотен тактов, как и в случае спинлока), так что атомики на параллельную архитектуру влияют мало.


Они как минимум убирают необходимость в 100500 явных блокировок.

L>Один хрен нужно ограничивать конкуренцию потоков (aka contention). Ну и если ты не в курсе — захват локов в одном, глобавльном, фиксированном порядке гарантирует целостность данных (если ты конечно захватил все локи) и отсутствие дедлоков.


Это я-то не в курсе?

L>>А если ты хочешь транзакционности, то вот ее и нужно реализовывать, взяв за основу один из механизмов, используемых в СУБД, например. Только в случае блокировок блокировать блокировать сразу целые таблицы, а не отдельные записи. Ничего не напоминает?


L>Зависит от СУБД, в постгресе вон вообще без локов таблиц обходоится обычно.


Потому что постгрес версионник. За это, кстати, приходится расплачиваться переодическим vacuum.

L>Есть задачи где на один сервер может валиться больше. Статистика гугла показывает запросы пользователей. Получается, что на предыдущей работе у нас сервер, занимающийся сбором time-series данных от сенсоров обрабатывал больше запросов на запись в секунду, чем весь гугл? А может ты просто теплое с мягким сравниваешь? Я могу сказать с уверенностью, что всякие time-series данные от всяких сенсоров,


Ты кагбы можешь мне это не рассказывать, для меня IEC61850, MODBUS, BACNET, DNP3 и прочие страшные слова — вовсе не пустой звук. Но мы подобных проблем почему-то не имеем.
www.blinnov.com
Re[20]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 09:14
Оценка:
Здравствуйте, xp1icit, Вы писали:

X>элементарно же:

X>1) лочим оба счета в однозначно определяемом порядке (типа сначала с меньшим номером и т.п.)
X>2) переводим деньги
X>3) разлочиваем в обратном порядке
X>эту странную ситуацию — когда двум тредам нужно залочить два объекта ну обязательно в разном порядке — вряд ли невозможно подобным простым образом исключить

Собственно, этот метод, но с группированием мютексов по уровням и предлагается делать в статье, на базе которой Lazin написал библиотеку.

С чем спорит landerhigh — мне не понятно.
И каждый день — без права на ошибку...
Re[21]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 30.06.14 09:16
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Собственно, этот метод, но с группированием мютексов по уровням и предлагается делать в статье, на базе которой Lazin написал библиотеку.


BFE>С чем спорит landerhigh — мне не понятно.


Не спорит, а утверждает, что группирование мьютексов по уровням — это уже само по себе заметание проблемы под ковер.
www.blinnov.com
Re[22]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 09:18
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Поздно уже предотвращать. Система в продакшене. Дедлок выстрелил, заслонка зависла в открытом положении, самолет упал, электростанция взорвалась.

BFE>>Можно предотвратить заранее. Цена — не оптимальный алгоритм.
L>Нельзя. Если система заходит в дедлок, то боржоми пить уже поздно.
Я же говорю, можно предотвратить заранее, до захода в дедлок.

BFE>>Чем это ситуация отличается от брошенного исключения? Файл уже удален, заслонка открыта, газ идет...


L>Примерно всем. Исключение понятно как обработать. А дедлок? Что с ним делать?

Обнаружили дедлок — бросили исключение. А как обработать исключение — понятно. В чём проблема-то?

BFE>>>>Если мы применяем RAII, то можно бросить исключение и поймать его выше всех локов, после чего повторить попытку.

L>>>Единственный более-менее приемлимый вариант — упасть с дампом. Что тоже очень плохо, но лучше зависания.
BFE>>Упасть — это не вариант.
L>А заклинившая педаль газа, упавший самолет и взорвавшийся реактор — вариант?
Я же говорю — не вариант.
И каждый день — без права на ошибку...
Re[22]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 09:28
Оценка:
Здравствуйте, landerhigh, Вы писали:

BFE>>Собственно, этот метод, но с группированием мютексов по уровням и предлагается делать в статье, на базе которой Lazin написал библиотеку.

BFE>>С чем спорит landerhigh — мне не понятно.
L>Не спорит, а утверждает, что группирование мьютексов по уровням — это уже само по себе заметание проблемы под ковер.

То решение, что вы предлагаете, существенно проигрывает по скорости для некоторых задач.
И каждый день — без права на ошибку...
Re[23]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 30.06.14 09:50
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>Нельзя. Если система заходит в дедлок, то боржоми пить уже поздно.

BFE>Я же говорю, можно предотвратить заранее, до захода в дедлок.

Обнаружить дедлок до захода в дедлок невозможно. То есть когда твой детектор сработал, предотвращать уже поздно.

BFE>>>Чем это ситуация отличается от брошенного исключения? Файл уже удален, заслонка открыта, газ идет...


L>>Примерно всем. Исключение понятно как обработать. А дедлок? Что с ним делать?

BFE>Обнаружили дедлок — бросили исключение. А как обработать исключение — понятно. В чём проблема-то?

Дедлок — по определению непредусмотренное поведение программы. Ну бросил ты исключение? Что ты с ним делать будешь, ты ж не предполагал, что дедлок может возникнуть? Или ты уже предполагаешь, что дедлоки могут возникнуть и уже написал класс DeadLockException? Тогда лучше потратить это время с бОльшей пользой и таки исправить поведение программы, чтобы исключить саму возможность дедлока.

BFE>>>>>Если мы применяем RAII, то можно бросить исключение и поймать его выше всех локов, после чего повторить попытку.

L>>>>Единственный более-менее приемлимый вариант — упасть с дампом. Что тоже очень плохо, но лучше зависания.
BFE>>>Упасть — это не вариант.
L>>А заклинившая педаль газа, упавший самолет и взорвавшийся реактор — вариант?
BFE>Я же говорю — не вариант.

Тогда нужно проектировать правильно, без вариантов. В общем случае ты отктаться не сможешь.
www.blinnov.com
Re[23]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 30.06.14 09:52
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>С чем спорит landerhigh — мне не понятно.


L>>Не спорит, а утверждает, что группирование мьютексов по уровням — это уже само по себе заметание проблемы под ковер.


BFE>То решение, что вы предлагаете, существенно проигрывает по скорости для некоторых задач.


Пока что мой опыт показывает, что попытка выиграть в скорости за счет игнорирования проблемы дедлоков приводит к проигрышу по всем фронтам без исключения.
Никому не нужно "быстрое решение", которое "иногда" лочится. Относительно "медленное", но работающее корректно всегда предпочтительнее.
www.blinnov.com
Re[24]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 10:35
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Нельзя. Если система заходит в дедлок, то боржоми пить уже поздно.

BFE>>Я же говорю, можно предотвратить заранее, до захода в дедлок.
L>Обнаружить дедлок до захода в дедлок невозможно. То есть когда твой детектор сработал, предотвращать уже поздно.
А я не об этом говорю.

BFE>>>>Чем это ситуация отличается от брошенного исключения? Файл уже удален, заслонка открыта, газ идет...

L>>>Примерно всем. Исключение понятно как обработать. А дедлок? Что с ним делать?
BFE>>Обнаружили дедлок — бросили исключение. А как обработать исключение — понятно. В чём проблема-то?

L>Дедлок — по определению непредусмотренное поведение программы.

Не более, чем исключение.
L>Ну бросил ты исключение? Что ты с ним делать будешь, ты ж не предполагал, что дедлок может возникнуть?
Зато я предпологал, что может быть исключение.

L>Или ты уже предполагаешь, что дедлоки могут возникнуть и уже написал класс DeadLockException?

Как вариант.

L>Тогда лучше потратить это время с бОльшей пользой и таки исправить поведение программы, чтобы исключить саму возможность дедлока.

А где обоснование этого утверждения? Предполагать исключение придётся в любом случае, а значит обработка исключений должна быть в любом случае.

BFE>>>>>>Если мы применяем RAII, то можно бросить исключение и поймать его выше всех локов, после чего повторить попытку.

L>>>>>Единственный более-менее приемлимый вариант — упасть с дампом. Что тоже очень плохо, но лучше зависания.
BFE>>>>Упасть — это не вариант.
L>>>А заклинившая педаль газа, упавший самолет и взорвавшийся реактор — вариант?
BFE>>Я же говорю — не вариант.
L>Тогда нужно проектировать правильно, без вариантов. В общем случае ты отктаться не сможешь.
В общем случае много чего невозможно.
И каждый день — без права на ошибку...
Re: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 30.06.14 11:17
Оценка:
Здравствуйте, Lazin, Вы писали:

L>read блокировку можно проапгрейдить до write блокировки при желании. Моя реализация R/W Lock сильно смещена в сторону читателей и предназначена для случаев, когда запись происходить на несколько порядков реже чем чтение (например для защиты настроек программы). В этом случае read_lock работает в 2-3 раза быстрее чем pthread_rwlock_rdlock. Также возможно, что pthread_rwlock_rdlock лучше масштабируется с ростом количества ядер/процессоров, чем моя реализация.


Тут всё зависит от того, как фишка ляжет. Предположим два потока постоянно читают один и тот же объект из настроек, т.е. составляющая хэша для конкретно этого объекта будет постоянной. И если трёхбитный хэш от thread_id у них различается — повезло, а вот если совпадёт, получаем mutual exclusion при доступе. pthread_rwlock в этом плане стабильней.

Можно сделать rwlock на mutex + conditional_variable. Только опасно, нужно помнить про read/write starvation.

как то так

Обобщил lock_pool для mutex/rwlock (без biased hash). lock_type как и размер пула параметрами шаблона. Заменил указатели на ссылки. Блокировка lock_type на чтение через внешнюю ф-цию, lock_for_reading.
hash функция тоже внешняя, size_t lock_hash(T const&, size_t lock_count) c default реализацией по адресу объекта. Иногда полезно например hash брать по ID какому то...
Плюс для guard'ов общую базу добавил (auto для бедных, как когда то его Кодт назвал).

#include "syncope.hpp"

typedef syncope::lock_pool<syncope::write_starvation_lock, 256> test_pool;
test_pool foo_sync("foo sync");

struct foo { foo(int id) : id(id) {} int id; };
inline size_t lock_hash(const foo& obj, size_t lock_count) {
    return obj.id%lock_count;
}

int main()
{
    foo a(1), b(2), c(3);
    {
        syncope::pool_guard guard1 = foo_sync.synchronize_read(a, b);
        std::cout << "read locked 1" << std::endl;
        syncope::pool_guard guard2 = foo_sync.synchronize_read(a, b);
        std::cout << "read locked 2" << std::endl;
    }
    {
        syncope::pool_guard guard = foo_sync.synchronize(a, b, c);
        std::cout << "write locked" << std::endl;
    }
    return 0;
}


выводит
foo sync read lock 1
foo sync read lock 2
read locked 1
foo sync read lock 1
foo sync read lock 2
read locked 2
foo sync unlock 2
foo sync unlock 1
foo sync unlock 2
foo sync unlock 1
foo sync lock 1
foo sync lock 2
foo sync lock 3
write locked
foo sync unlock 3
foo sync unlock 2
foo sync unlock 1
Re: Deadlock-prevention алгоритм
От: MasterZiv СССР  
Дата: 30.06.14 11:50
Оценка:
> Теперь собственно вопрос — хочется специальный дефайн, при определении
> которого, включались бы проверки корректности порядка захвата и
> освобождения блокировок.

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

Про графы: если делать по уму, то по идее граф должен выродиться
в линейный список мьютексов. Т.е. я хочу сказать, что то, что вы
делаете, обычно делают (видимо) проще -- лочат мьютексы в строго
определённом порядке.
Posted via RSDN NNTP Server 2.1 beta
Re[26]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 12:40
Оценка:
Здравствуйте, landerhigh, Вы писали:

BFE>>>>Обнаружили дедлок — бросили исключение. А как обработать исключение — понятно. В чём проблема-то?

L>Исключение — это исключительная ситуация, которая, однако, ожидаема и может быть исправлена.
Если вы ожидаете deadlock, то deadlock — это исключительная ситуация, которая, однако, ожидаема и может быть исправлена.
Придумайте что-нибудь посерьёзнее в качестве аргумента.

L>Некоторые ковбои пытаются ловить и "обрабатывать" и неисправимые ситуации — вроде конца света памяти или 0x5 (под виндой), но это неправильно, т.к. исправить ситуацию уже нельзя.

L>Дедлок по определению ситуация не ожидаемая. Если она ожидается, то ее всегда можно исправить в коде и полностью исключить.
Тогда давайте писать без исключений, раз исключение — ожидаемое событие, то ее всегда можно исправить в коде и полностью исключить.

L>>>Дедлок — по определению непредусмотренное поведение программы.

BFE>>Не более, чем исключение.
L>О майн готт. Не знаю, смеяться или плакать.
Я вижу одни эмоции без аргументов. Если вы можете обнаружить deadlock в момент исполнения, то он является одной из исключительных ситуаций. Разумеется для этого надо иметь механизм обнаружения deadlock в момент захвата очередного мютекса.

L>>>Ну бросил ты исключение? Что ты с ним делать будешь, ты ж не предполагал, что дедлок может возникнуть?

BFE>>Зато я предпологал, что может быть исключение.
L>>>Или ты уже предполагаешь, что дедлоки могут возникнуть и уже написал класс DeadLockException?
BFE>>Как вариант.
L>И что ты будешь с ним делать?
здесь
Автор: B0FEE664
Дата: 27.06.14


L>>>Тогда лучше потратить это время с бОльшей пользой и таки исправить поведение программы, чтобы исключить саму возможность дедлока.

BFE>>А где обоснование этого утверждения? Предполагать исключение придётся в любом случае, а значит обработка исключений должна быть в любом случае.
L>Дедлок возникает в результате не предусмотренной (не замеченной, проигнорированной) во время разработки взаимоблокировки двух или более потоков. Поскольку эта ситуация в принципе не предусматривалась, точнее, была прохлопана ушами, корректно обработать ее в рантаймне не-воз-мож-но.
Да уж, "обоснованице". Ну а как вам такое "контробоснование":
std::invalid_argument исключение возникает в результате не предусмотренной (не замеченной, проигнорированной) во время разработки передачи невалидного параметра в какую-то функцию. Поскольку эта ситуация в принципе не предусматривалась, точнее, была прохлопана ушами, корректно обработать ее в рантаймне не-воз-мож-но.
— это что? Это обоснование?

L>>>Тогда нужно проектировать правильно, без вариантов. В общем случае ты отктаться не сможешь.

BFE>>В общем случае много чего невозможно.
L>Проектировать правильно — возможно.
Что и предлагается сделать с помощью библиотеки.
И каждый день — без права на ошибку...
Re[2]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 30.06.14 12:58
Оценка:
Здравствуйте, sokel, Вы писали:

S>Плюс для guard'ов общую базу добавил (auto для бедных, как когда то его Кодт назвал).


Ну и совсем версия для бедных, без C++11.
Re[26]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 30.06.14 13:07
Оценка:
EP>Всё же deadlock (незапланированный) это ошибка в коде программы — то есть не исключение, а скорее assert.
EP>Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.

В некоторых случаях, иметь такой механизм в продакшене может быть полезно, если например сервис работает под управлением супервизора, который перезапускает его в случае падения. Если сервис просто зависнет из-за дедлока, супервизор будет думать что все хорошо (если между супервизором и сервисом нет какого-нибудь протокола для проверки работоспособности) и не растартанет сервис. В этом случае лучше обнаружить дедлок и вызвать terminate.
Re[2]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 30.06.14 13:10
Оценка:
Я позже посмотрю, сейчас работу нужно работать
У тебя нет гитхаб аккаунта? Можно было бы сделать форк+pull request, что бы ты появился в списке контрибьюторов.
Re[27]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 30.06.14 13:11
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>Исключение — это исключительная ситуация, которая, однако, ожидаема и может быть исправлена.

BFE>Если вы ожидаете deadlock, то deadlock — это исключительная ситуация, которая, однако, ожидаема и может быть исправлена.

Скажу резко — если вы в своем коде ожидаете дедлок, то вам лучше не заниматься многопоточным программированием. По крайней мере пока.

BFE>Придумайте что-нибудь посерьёзнее в качестве аргумента.


Серьезнее некуда.

L>>Некоторые ковбои пытаются ловить и "обрабатывать" и неисправимые ситуации — вроде конца света памяти или 0x5 (под виндой), но это неправильно, т.к. исправить ситуацию уже нельзя.

L>>Дедлок по определению ситуация не ожидаемая. Если она ожидается, то ее всегда можно исправить в коде и полностью исключить.
BFE>Тогда давайте писать без исключений, раз исключение — ожидаемое событие, то ее всегда можно исправить в коде и полностью исключить.

Ради бога, занимайтесь километрами спагетти-кода с кодами возврата.
Исключения — не более чем механизм для корректного сообщения о проблеме, делающей невозможным продолжение работы на данном уровне вложенности, на предыдущие уровни для того, чтобы они могли принять соответствующее решение. Для этого они подходят как нельзя лучше. Но вот для лечения дефектов внутиутробного развития ПО они не подходят.

L>>>>Дедлок — по определению непредусмотренное поведение программы.

BFE>>>Не более, чем исключение.
L>>О майн готт. Не знаю, смеяться или плакать.
BFE>Я вижу одни эмоции без аргументов. Если вы можете обнаружить deadlock в момент исполнения, то он является одной из исключительных ситуаций.

Видимо, придется плакать.
Обнаруживать дедлок в момент исполнения уже поздно. Как в моем примере с однопутной железной дорогой.

L>>>>Ну бросил ты исключение? Что ты с ним делать будешь, ты ж не предполагал, что дедлок может возникнуть?

BFE>>>Зато я предпологал, что может быть исключение.
L>>>>Или ты уже предполагаешь, что дедлоки могут возникнуть и уже написал класс DeadLockException?
BFE>>>Как вариант.
L>>И что ты будешь с ним делать?
BFE>здесь
Автор: B0FEE664
Дата: 27.06.14


Если мы обнаружили дедлок в какой либо нитке, то эта нитка должна восстановить предыдущее состояние тех данных, которые были защищены мьютексом, разлочить все мьютексы, после чего повторить попытку.


А если предыдущее состояние тех данных невосстановимо? А если это не данные, а команда на открытие воздушного прервыателя на 300 КВ, у которого trip time под десять секунд и наработка на отказ всего несколько десятков переключений и при попытке получить его текущий статус твой тред залип? Ты хочешь послать команду на закрытие, чтобы "восстановиться и попробовать снова"? Боюсь, тебе даже не дадут забрать свои вещи из кьюбилка, если ты такое предложишь, потому что этот факап может стоить кому-то жизней, а компании — многомиллионных судебных исков.

Дедлок в общем случае — не восстанавливаемая ситуация. Period.
Факт дедлока говорит о серьезном факапе в execution flow и рассчитывать на то, что тебе удастся откатиться, нельзя.

BFE>>>В общем случае много чего невозможно.

L>>Проектировать правильно — возможно.
BFE>Что и предлагается сделать с помощью библиотеки.

Еще раз — исправлять косяки проектирования в рантайме поздно. И в общем случае невозможно.
www.blinnov.com
Re[20]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 30.06.14 13:28
Оценка:
L>Зачем ты ее поломал?

Я ничего не делал, она сама просто взяла и загорелась

L>>Ну так остальные запросы простаивать то тоже не будут, они будут обслуживаться другими ядрами, так как запрос может обработать любое ядро.


L>А остальных запросов-то и нет. Всем нужен кот.


Это вырожденный случай, но даже в этом случае можно сделать так, чтобы к коту могли обращаться из разных потоков параллельно, если реплицировать самого кота и периодически reduce-ить его состояние (то что наимзеняли из разных потоков сливать вместе). Но это уже будет eventualy consistent реализация.

L>>Я говорю о моем репозитории с кодом из первого поста. Если все это тлен, как ты говоришь, то зря я парюсь, нужно удалить его и все. Eine Frau, ein Loch, eine mutex!


L>Речь не об одном мьютексе, а об архитектуре. И таки да, я буду продолжать утрвеждать, что детекторы дедлоков имеют мало смысла с точки зрения верификации ПО. Если архитектура настолько ущербна, что допускает дедлоки, то ничего, кроме ее переработки, не поможет. Детектор может помочь выявить и даже несколько дедлоков, но он не способен гарантировать отсутствие других, не обнаруженных пока, локов. Как инструмент раскопок в говнокоде, который нужно заставить хоть как-то работать, их использовать можно. Вопрос только, нужно ли?


Ты меня тогда не понял, видимо. Я в курсе что нужно делать хорошую архитектуру, в которой невозможны дедлоки и не делать плохую. Дело в том, что я прячу отдельные мьютексы за интерфейсом и говорю что объекты можно лочить так и так. Пользователь может решить (ошибочно), что он лочит разные объекты и дедлок невозможен в принципе, даже если он это делает в разном порядке, так как объекты реально разные и его архитектура это гарантирует, это и было бы так, если бы он использовал мьютексы напрямую, но у меня может получиться конфликт и возникнет дедлок. Поинт в том, что если ты берешь на себя захват/освобождение мьютексов а также сам мапишь их на разные объекты программы — ты также должен предоставить механизм проверки корректности. Period. Нормальная библиотека должна учитывать разные аспекты ее использования, не только основную логику и производительность, но и тестируемость, отлаживаемость и человеко-понимаемость.

L>Они как минимум убирают необходимость в 100500 явных блокировок.

atomic-и это тоже синхронизация

L>>Есть задачи где на один сервер может валиться больше. Статистика гугла показывает запросы пользователей. Получается, что на предыдущей работе у нас сервер, занимающийся сбором time-series данных от сенсоров обрабатывал больше запросов на запись в секунду, чем весь гугл? А может ты просто теплое с мягким сравниваешь? Я могу сказать с уверенностью, что всякие time-series данные от всяких сенсоров,


L>Ты кагбы можешь мне это не рассказывать, для меня IEC61850, MODBUS, BACNET, DNP3 и прочие страшные слова — вовсе не пустой звук. Но мы подобных проблем почему-то не имеем.

софт о котором я говорил не имеет подобных проблем, просто приведен в качестве примера сервиса, обрабатывающего столько данных, сколько по твоему не бывает он используется, например, в ЦДУ СО ЕЭС (судя по использованным аббревиатурам ты должен знать что это такое) для сбора всей телеметрии, какая только есть
Re[27]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 30.06.14 13:37
Оценка:
Здравствуйте, Lazin, Вы писали:

EP>>Всё же deadlock (незапланированный) это ошибка в коде программы — то есть не исключение, а скорее assert.

EP>>Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.
L>В некоторых случаях, иметь такой механизм в продакшене может быть полезно, если например сервис работает под управлением супервизора, который перезапускает его в случае падения. Если сервис просто зависнет из-за дедлока, супервизор будет думать что все хорошо (если между супервизором и сервисом нет какого-нибудь протокола для проверки работоспособности) и не растартанет сервис.

То что такой механизм (для fault tolerance) в некоторых случаях (скорее редких) полезен, и даже необходим — я полностью согласен.
Но вот использовать исключения для сигнализации deadlock'а — имхо не лучший вариант.

L>В этом случае лучше обнаружить дедлок и вызвать terminate.


Так о том и речь, что например terminate был бы лучше исключения.
Re[28]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 13:58
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Исключение — это исключительная ситуация, которая, однако, ожидаема и может быть исправлена.

BFE>>Если вы ожидаете deadlock, то deadlock — это исключительная ситуация, которая, однако, ожидаема и может быть исправлена.
L>Скажу резко — если вы в своем коде ожидаете дедлок, то вам лучше не заниматься многопоточным программированием. По крайней мере пока.
BFE>>Придумайте что-нибудь посерьёзнее в качестве аргумента.
L>Серьезнее некуда.
Вы в самом деле считаете переход на личность серьёзным аргументом?

L>>>>>Дедлок — по определению непредусмотренное поведение программы.

BFE>>>>Не более, чем исключение.
L>>>О майн готт. Не знаю, смеяться или плакать.
BFE>>Я вижу одни эмоции без аргументов. Если вы можете обнаружить deadlock в момент исполнения, то он является одной из исключительных ситуаций.
L>Видимо, придется плакать.
L>Обнаруживать дедлок в момент исполнения уже поздно. Как в моем примере с однопутной железной дорогой.
Вы такой эмоциональный.

L>

L>Если мы обнаружили дедлок в какой либо нитке, то эта нитка должна восстановить предыдущее состояние тех данных, которые были защищены мьютексом, разлочить все мьютексы, после чего повторить попытку.

L>А если предыдущее состояние тех данных невосстановимо?
Если предыдущее состояние восстановимо, то вы уже где-то раньше сделали ошибку. Если у вас два элемента системы должны находится в согласованном состоянии и один из элементов невосстановимым, то почему эти два элемента защищены двумя разными мютексами?

L>А если это не данные, а команда на открытие воздушного прервыателя на 300 КВ, у которого trip time под десять секунд и наработка на отказ всего несколько десятков переключений и при попытке получить его текущий статус твой тред залип? Ты хочешь послать команду на закрытие, чтобы "восстановиться и попробовать снова"? Боюсь, тебе даже не дадут забрать свои вещи из кьюбилка, если ты такое предложишь, потому что этот факап может стоить кому-то жизней, а компании — многомиллионных судебных исков.

Вы всё время ссылаетесь на runtime системы. Скажите, а в своих runtime системах вы работаете в терминах ниток обменивающихся данными или, всё же, в терминах real-time таймеров?

L>Дедлок в общем случае — не восстанавливаемая ситуация. Period.

L>Факт дедлока говорит о серьезном факапе в execution flow и рассчитывать на то, что тебе удастся откатиться, нельзя.
В общем случае, если вы пишите на C++ и не можете безопасно развернуть стек, то вы пишите с ошибками. Или у вас вокруг каждого вызова функции стоит try-catch ?

L>Еще раз — исправлять косяки проектирования в рантайме поздно. И в общем случае невозможно.

В самом деле, если у вас все данные протаскиваются через одну нитку, то исправить такой архитектурный косяк при нехватки производительности невозможно.
И каждый день — без права на ошибку...
Re[27]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 30.06.14 14:06
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>>>Дедлок — по определению непредусмотренное поведение программы.

BFE>>>Не более, чем исключение.
EP>>Всё же deadlock (незапланированный) это ошибка в коде программы — то есть не исключение, а скорее assert.
BFE>Нет, это не ошибка, если такое поведение ускоряет выполнение приложения и было предусмотрено заранее.

Я же говорю про незапланированный deadlock, который в подавляющем большинстве случаев таковым и является.
Можно конечно представить некий хитрый алгоритм, который работает быстро, но иногда приводит к deadlock'у в силу своих особенностей, а не недосмотра — но это скорее исключение чем правило.
Сразу вспоминается SR-71, у которого на стоянке вытекает топливо.


EP>>Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.

BFE>Всяко лучше, чем убивать подвисшие нитки извне, как это делают некоторые.

Чем же лучше? При раскрутке стэка может произойти другой deadlock, или что ещё хуже — livelock.
Для нормальной fault tolerance всё равно придётся предусматривать аварийное завершение, даже если без учёта deadlock'ов. Почему бы не использовать тот же самый механизм и для deadlock'ов, который всяко надёжней исключений, да ещё и подойдёт к livelock'ам.
Re[3]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 30.06.14 14:14
Оценка:
Здравствуйте, Lazin, Вы писали:

L>Я позже посмотрю, сейчас работу нужно работать

L>У тебя нет гитхаб аккаунта? Можно было бы сделать форк+pull request, что бы ты появился в списке контрибьюторов.

Да аккаунт то есть, но ты сам смотри, какое у тебя видение, мне особо deadlock prevention пока не нужен, просто пулы мьютексов-рвлоков у себя давно причесать хотел.
Мне в общем обычно и multiple lock не нужен, просто блокирую объекты из коллекций по айдишнику на чтение — запись.

как то в таком вот духе:
class foo {
    typedef syncope::lock_pool<my_rwlock, 1024> lock_storage;
    typedef syncope::pool_guard_one<lock_storage, false> write_guard;
    typedef syncope::pool_guard_one<lock_storage, true> read_guard;
    friend class foo_read_accessor;
    friend class foo_read;
    friend class foo_modify;
    static lock_storage accessors;
public:
    foo() : bar_(0) {}
private:
    int bar_;
};

foo::lock_storage foo::accessors("foo sync");

class foo_read_accessor {
    friend class foo_read;
    friend class foo_modify;
private:
    foo_read_accessor(foo& obj)
        : obj_(obj)
    {}
public:
    int bar() const { return obj_.bar_; }
private:
    foo& obj_;
};

class foo_read
    : foo::read_guard
    , public foo_read_accessor
{
public:
    foo_read(foo& obj)
        : foo::read_guard(foo::accessors, obj)
        , foo_read_accessor(obj)
    {}
};

class foo_modify
    : foo::write_guard
    , public foo_read_accessor
{
public:
    foo_modify(foo& obj)
        : foo::write_guard(foo::accessors, obj)
        , foo_read_accessor(obj)
        , bar_changed_(false)
    {}
    ~foo_modify() {
        unlock();
        // process changes outside lock
        if(bar_changed_) {
            std::cout << "foo bar changed: " << foo_read(obj_).bar() << std::endl;
        }
    }
    void set_bar(int v) {
        if(v != obj_.bar_) {
            obj_.bar_ = v;
            bar_changed_ = true;
        }
    }
private:
    bool bar_changed_;
};


int main()
{
    foo f;
    foo_modify(f).set_bar(123);
    return 0;
}


Замотано всё, чтобы без блокировки ни прочитать и записать...
Кстати, в контексте пула локов полезны бывают ещё отмасштабированные по его размеру контейнеры, выбираешь по хэшу, работаешь внутри скопа без блокировок.
Re[4]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 30.06.14 15:14
Оценка:
Здравствуйте, sokel, Вы писали:

S>Здравствуйте, Lazin, Вы писали:


Ещё пара мелочей, определение наличия rdlock через SFINAE + убрал lock_count из ф-ции lock_hash.
Re[28]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 16:01
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

BFE>>Нет, это не ошибка, если такое поведение ускоряет выполнение приложения и было предусмотрено заранее.

EP>Я же говорю про незапланированный deadlock, который в подавляющем большинстве случаев таковым и является.
А что можно сказать про незапланированный deadlock? Всё. Висим. Точка. Речь о том, чтобы в принципе избежать такой ситуации.

BFE>>Всяко лучше, чем убивать подвисшие нитки извне, как это делают некоторые.

EP>Чем же лучше? При раскрутке стэка может произойти другой deadlock,
Если написать некорректную обработку исключения, то ещё и не такое может произойти.

EP>или что ещё хуже — livelock.

livelock возможен, но и с ним понятно как бороться — отдавать время другим ниткам из той нитки, что deadlock обнаружила (после снятия всех блокировок). Если так действовать, то livelock возможен только при перегрузке системы. В конце-концов, частое выбрасывание исключений без отказа оборудования говорит об ошибке в архитектуре.

EP>Для нормальной fault tolerance всё равно придётся предусматривать аварийное завершение, даже если без учёта deadlock'ов. Почему бы не использовать тот же самый механизм и для deadlock'ов, который всяко надёжней исключений, да ещё и подойдёт к livelock'ам.

Это зависит от серьёзности возможных последствий и оценки стоимости. Убийство нитки не гарантирует освобождение всех ресурсов алокированных этой ниткой. Может оказаться, что такая утечка настолько незначительна, а убийство ниток столь редко, что система работает годами...
И каждый день — без права на ошибку...
Re[29]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 30.06.14 17:06
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>Нет, это не ошибка, если такое поведение ускоряет выполнение приложения и было предусмотрено заранее.

EP>>Я же говорю про незапланированный deadlock, который в подавляющем большинстве случаев таковым и является.
BFE>А что можно сказать про незапланированный deadlock?

Это ошибка в коде

BFE>Всё. Висим. Точка. Речь о том, чтобы в принципе избежать такой ситуации.


Избежать deadlock'а или чего?

BFE>>>Всяко лучше, чем убивать подвисшие нитки извне, как это делают некоторые.

EP>>Чем же лучше? При раскрутке стэка может произойти другой deadlock,
BFE>Если написать некорректную обработку исключения, то ещё и не такое может произойти.

Так мы же и так рассматриваем ситуацию, которой в корректной программе быть не должно. И соответственно раз произошёл deadlock, "то и не такое может произойти"

EP>>или что ещё хуже — livelock.

BFE>livelock возможен, но и с ним понятно как бороться — отдавать время другим ниткам из той нитки, что deadlock обнаружила (после снятия всех блокировок). Если так действовать, то livelock возможен только при перегрузке системы.

Незапланированный deadlock возможен только в коде с багом, точно также как и livelock.
Раз уж мы получили deadlock, то почему при раскрутке стэка не может вылезти livelock или ещё что-нибудь?

EP>>Для нормальной fault tolerance всё равно придётся предусматривать аварийное завершение, даже если без учёта deadlock'ов. Почему бы не использовать тот же самый механизм и для deadlock'ов, который всяко надёжней исключений, да ещё и подойдёт к livelock'ам.

BFE>Это зависит от серьёзности возможных последствий и оценки стоимости. Убийство нитки не гарантирует освобождение всех ресурсов алокированных этой ниткой. Может оказаться, что такая утечка настолько незначительна, а убийство ниток столь редко, что система работает годами...

Если требуется fault tolerance, не считая какие-то совсем тривиальные случаи, то придётся планировать аварийное завершение с освобождением всех ресурсов.
Re[29]: Deadlock-prevention алгоритм
От: SleepyDrago Украина  
Дата: 30.06.14 17:27
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, Evgeny.Panasyuk, Вы писали:


...
BFE>>>Всяко лучше, чем убивать подвисшие нитки извне, как это делают некоторые.
EP>>Чем же лучше? При раскрутке стэка может произойти другой deadlock,
BFE>Если написать некорректную обработку исключения, то ещё и не такое может произойти.
На моей памяти код который не удалось запустить на тестовом стенде лучше просто удалить. быстрое завершение намного проще потом лечить. Запускать этот не протестированный код пытающийся закатить все солнца назад вручную в продакшене это самоубийство.

...
EP>>Для нормальной fault tolerance всё равно придётся предусматривать аварийное завершение, даже если без учёта deadlock'ов. Почему бы не использовать тот же самый механизм и для deadlock'ов, который всяко надёжней исключений, да ещё и подойдёт к livelock'ам.
BFE>Это зависит от серьёзности возможных последствий и оценки стоимости. Убийство нитки не гарантирует освобождение всех ресурсов алокированных этой ниткой. Может оказаться, что такая утечка настолько незначительна, а убийство ниток столь редко, что система работает годами...
Решать что будет на продакшене после краха по любому надо. Электрики (с) все равно придут. Так что готовиться к terminate/power-down надо начинать изначально. Ну или весь этот fault tolerance удалять как неиспользуемый код =)
Re[26]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 30.06.14 22:12
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

L>>>Дедлок — по определению непредусмотренное поведение программы.

BFE>>Не более, чем исключение.
EP>Всё же deadlock (незапланированный) это ошибка в коде программы — то есть не исключение, а скорее assert.
EP>Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.

Может и не лучший, но вот, что поиском нашёл в стандарте:

30.4.2.2.2 unique_lock locking [thread.lock.unique.locking]
void lock();
1 Effects: pm->lock()
2 Postcondition: owns == true
3 Throws: Any exception thrown by pm->lock(). system_error if an exception is required (30.2.2).
system_error with an error condition of operation_not_permitted if pm is 0. system_error with
an error condition of resource_deadlock_would_occur if on entry owns is true.




Ну и

30.4.1.2/
6 The expression m.lock() shall be well-formed and have the following semantics:
12 Throws: system_error when an exception is required (30.2.2).
13 Error conditions:
— operation_not_permitted — if the thread does not have the privilege to perform the operation.
— resource_deadlock_would_occur — if the implementation detects that a deadlock would occur.
— device_or_resource_busy — if the mutex is already locked and blocking is not possible.



30.2.2 Exceptions [thread.req.exception]
1 Some functions described in this Clause are specified to throw exceptions of type system_error (19.5.6).
Such exceptions shall be thrown if any of the function’s error conditions is detected or a call to an operating
system or other underlying API results in an error that prevents the library function from meeting its
specifications.


Так что если пользоваться стандартом, то такие исключения надо обрабатывать.
И каждый день — без права на ошибку...
Re[27]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 30.06.14 23:36
Оценка:
Здравствуйте, B0FEE664, Вы писали:

EP>>Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.

BFE>Может и не лучший, но вот, что поиском нашёл в стандарте:
BFE>

BFE>3 Throws: Any exception thrown by pm->lock(). system_error if an exception is required (30.2.2).
BFE>system_error with an error condition of operation_not_permitted if pm is 0. system_error with
BFE>an error condition of resource_deadlock_would_occur if on entry owns is true.

BFE>

Видел это. Какой именно была rationale — не знаю, возможно максимальная гибкость.
К слову сказать, Александр Степанов в своих лекциях
Автор: andyp
Дата: 17.07.13
рассказывал, что кто-то из членов комитета предлагал в стандартные итераторы добавить встроенные проверки и вроде даже исключениями, что-то типа итератор должен знать из какого он контейнера и т.п. — к счастью STL пошёл по другому пути.

BFE>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.


Во-первых, какие-то специальные телодвижения не нужны (разве что поставить assert ) — там же в корне std::exception.
Во-вторых, опять-таки — это же явный баг в программе, если уж случилось такое, то неизвестно как там оно дальше пойдёт в разнос.
Re[29]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 01.07.14 06:52
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>Скажу резко — если вы в своем коде ожидаете дедлок, то вам лучше не заниматься многопоточным программированием. По крайней мере пока.

L>>Серьезнее некуда.
BFE>Вы в самом деле считаете переход на личность серьёзным аргументом?

А где ты увидел преход на личности? Это опыт. Ничего личного.

L>>Видимо, придется плакать.

L>>Обнаруживать дедлок в момент исполнения уже поздно. Как в моем примере с однопутной железной дорогой.
BFE>Вы такой эмоциональный.

Исправь дедлок на однопутной железной дороге после обнаружения. Потом рассуждай об эмоциях.

L>>А если это не данные, а команда на открытие воздушного прервыателя на 300 КВ, у которого trip time под десять секунд и наработка на отказ всего несколько десятков переключений и при попытке получить его текущий статус твой тред залип? Ты хочешь послать команду на закрытие, чтобы "восстановиться и попробовать снова"? Боюсь, тебе даже не дадут забрать свои вещи из кьюбилка, если ты такое предложишь, потому что этот факап может стоить кому-то жизней, а компании — многомиллионных судебных исков.

BFE>Вы всё время ссылаетесь на runtime системы. Скажите, а в своих runtime системах вы работаете в терминах ниток обменивающихся данными или, всё же, в терминах real-time таймеров?

Мы работаем в терминах архитектурных решений, разработанных специально для конкретных нужд. Будут это потоки, таймеры или даже вовсе прерывания от устройств, нам совершенно неважно.
И ты с темы не соскакивай. Расскажи, как ты будешь исправлять такой дедлок.

L>>Дедлок в общем случае — не восстанавливаемая ситуация. Period.

L>>Факт дедлока говорит о серьезном факапе в execution flow и рассчитывать на то, что тебе удастся откатиться, нельзя.
BFE>В общем случае, если вы пишите на C++ и не можете безопасно развернуть стек, то вы пишите с ошибками. Или у вас вокруг каждого вызова функции стоит try-catch ?

Еще раз. Ты можешь хоть десять раз развернуть стек, и можешь даже вприсядку сплясать при этом. Но, даже в простом случае для того, чтобы откатиться и "попробовать снова", тебе нужно в первую очередь убрать причину дедлока. Удачи в этом, как говорится, и удачи не поймать еще один deadlock exception где-нибудь при "откате и повторе". Но в общем случае даже в системе без внешних исполнительных устройств, откатиться назад ты не сможешь — другие потоки-то работали и данные, с которыми твой поток поймал лок, как правило, после отката уже устарели. Ну а если есть исполнительные устройства — смотри выше.

L>>Еще раз — исправлять косяки проектирования в рантайме поздно. И в общем случае невозможно.

BFE>В самом деле, если у вас все данные протаскиваются через одну нитку, то исправить такой архитектурный косяк при нехватки производительности невозможно.

Никому не интересна высокая производительность системы, которая не может работать корректно. Сегодня любому самоделкину доступны технологии, с которыми он в гараже может построить машину, развивающую более 300 км/ч на прямой. Одна загвоздка — Нюрбургринг почему-то выгрывают далеко не самые быстрые машины.

С другой стороны, корректно и стабильно работающую систему, да еще и покрытую серией автотестов, весь косяк которой своидтся к недостаточной производительности, всегда можно смасштабировать. Особенно если ее разработчики такую возможность предусмотрели.
И, чтобы уж добить. У нас есть система, в которой все данные "протаскиваются через одну нитку". Пока что ни один другой продукт на рынке не смог добиться даже 1/1000 от ее производительности и 1/100 от масштабируемости. Угадай, почему?
www.blinnov.com
Re[27]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 01.07.14 06:57
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Может и не лучший, но вот, что поиском нашёл в стандарте:


Стандарт не описывает, как именно детектится дедлок, кроме случая, когда один и тот же поток пытается дважды забрать нерекурсивный мьютекс. Как и зачем это попало в стандарт —

BFE>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.


Единственный правильный способ обработать такое исключение — записать дамп и упасть.
www.blinnov.com
Re[21]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 01.07.14 07:12
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Зачем ты ее поломал?


L>Я ничего не делал, она сама просто взяла и загорелась


Ого.
Наверное, дедлок где-то в проводке


L>Ты меня тогда не понял, видимо. Я в курсе что нужно делать хорошую архитектуру, в которой невозможны дедлоки и не делать плохую. Дело в том, что я прячу отдельные мьютексы за интерфейсом и говорю что объекты можно лочить так и так. Пользователь может решить (ошибочно), что он лочит разные объекты и дедлок невозможен в принципе, даже если он это делает в разном порядке, так как объекты реально разные и его архитектура это гарантирует, это и было бы так, если бы он использовал мьютексы напрямую, но у меня может получиться конфликт и возникнет дедлок. Поинт в том, что если ты берешь на себя захват/освобождение мьютексов а также сам мапишь их на разные объекты программы — ты также должен предоставить механизм проверки корректности. Period. Нормальная библиотека должна учитывать разные аспекты ее использования, не только основную логику и производительность, но и тестируемость, отлаживаемость и человеко-понимаемость.


Ой. Все, что от нормальной библиотеки требуется — это четкая гарантия thread safety. Должно быть четко документировано, какие методы безусловно безопасны, какие — безопасны при условии того, что они работают с разными данными, а какие требуют блокировки. Еще необходимо четко описать, как именно и из какого потока вызываеются колбеки.

Как пользователь 100500 разных библиотек, насмотревшийся всякго, попрошу — пожалайста, не надо делать ничего больше! Для библиотеки крайне важно не совершать никаких ненаблюдаемых извне и не запрошенных явно действий.

L>>Они как минимум убирают необходимость в 100500 явных блокировок.

L>atomic-и это тоже синхронизация

В идеале atomic это просто объект данных, который гарантированно можно целиком записать за одну операцию и при этом у него не будет внешне наблюдаемого промежуточного "недозаписанного" состояния.

L>>Ты кагбы можешь мне это не рассказывать, для меня IEC61850, MODBUS, BACNET, DNP3 и прочие страшные слова — вовсе не пустой звук. Но мы подобных проблем почему-то не имеем.

L>софт о котором я говорил не имеет подобных проблем, просто приведен в качестве примера сервиса, обрабатывающего столько данных, сколько по твоему не бывает он используется, например, в ЦДУ СО ЕЭС (судя по использованным аббревиатурам ты должен знать что это такое) для сбора всей телеметрии, какая только есть

Эээ, это тебе в СО ЕЭС показалось, что там много данных? А я-то уж было напрягся
www.blinnov.com
Re[28]: Deadlock-prevention алгоритм
От: sokel Россия  
Дата: 01.07.14 07:24
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

BFE>>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.


Microsoft'ы, кстати, кидают на рекурсивных локах (VS13):
std::mutex m;
try {
    m.lock();
    m.lock();
}
catch(std::exception& e) {
    std::cout << "exception: " << e.what() << std::endl;
}

> device or resource busy : device or resource busy


EP>Во-первых, какие-то специальные телодвижения не нужны (разве что поставить assert ) — там же в корне std::exception.

EP>Во-вторых, опять-таки — это же явный баг в программе, если уж случилось такое, то неизвестно как там оно дальше пойдёт в разнос.

Можно попробовать остановиться... Исключение предотвращает сам deadlock, мьютекс в корректном состоянии остается, так что корректная остановка потенциально вполне возможна.
Понятно что баг и использовать нельзя, но может можно на стабильную предыдущую версию откатиться или ещё что.
Re[28]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 01.07.14 08:52
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>>Можно конечно представить некий fault tolerance, который закладывается и на такие баги кода, но имхо кидать исключение в таком случае всё равно не лучший вариант.

BFE>>Может и не лучший, но вот, что поиском нашёл в стандарте:
BFE>>

BFE>>3 Throws: Any exception thrown by pm->lock(). system_error if an exception is required (30.2.2).
BFE>>system_error with an error condition of operation_not_permitted if pm is 0. system_error with
BFE>>an error condition of resource_deadlock_would_occur if on entry owns is true.

BFE>>

EP>Видел это. Какой именно была rationale — не знаю, возможно максимальная гибкость.

Я тоже не читал логического обоснования этого, но идея кажется здравой.

BFE>>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.

EP>Во-первых, какие-то специальные телодвижения не нужны (разве что поставить assert ) — там же в корне std::exception.
assert — это исключительно средство разработки. В дебаг версии на него можно вообще никогда не наткнуться.

EP>Во-вторых, опять-таки — это же явный баг в программе, если уж случилось такое, то неизвестно как там оно дальше пойдёт в разнос.

Скажите, а вот когда происходит exception не связанный с отказом оборудования, это ошибка в программе или нет?
И каждый день — без права на ошибку...
Re[30]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 01.07.14 09:31
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>А где ты увидел преход на личности? Это опыт. Ничего личного.

Согласно моему опыту, качество архитектурных решений следует выбирать с учётом квалификации членов команды. Архитектурное решение выбранное в команде говорит об уровне квалификации членов этой команды.

L>>>Видимо, придется плакать.

L>>>Обнаруживать дедлок в момент исполнения уже поздно. Как в моем примере с однопутной железной дорогой.
BFE>>Вы такой эмоциональный.
L>Исправь дедлок на однопутной железной дороге после обнаружения. Потом рассуждай об эмоциях.
Вы разговариваете сами с собой. Какая ещё однопутная железная дорога? Давайте без демагогических аналогий.

BFE>>Вы всё время ссылаетесь на runtime системы. Скажите, а в своих runtime системах вы работаете в терминах ниток обменивающихся данными или, всё же, в терминах real-time таймеров?

L>Мы работаем в терминах архитектурных решений, разработанных специально для конкретных нужд. Будут это потоки, таймеры или даже вовсе прерывания от устройств, нам совершенно неважно.
Ясно.

L>И ты с темы не соскакивай. Расскажи, как ты будешь исправлять такой дедлок.

deadlock в реалтайм системе с необратимыми действиями? Очевидно, что один из локов лишний. Оба действия должны лежать быть защищены одним синхронизирующим объектом. А как это относится к теме?

L>>>Дедлок в общем случае — не восстанавливаемая ситуация. Period.

L>>>Факт дедлока говорит о серьезном факапе в execution flow и рассчитывать на то, что тебе удастся откатиться, нельзя.
BFE>>В общем случае, если вы пишите на C++ и не можете безопасно развернуть стек, то вы пишите с ошибками. Или у вас вокруг каждого вызова функции стоит try-catch ?

L>Еще раз. Ты можешь хоть десять раз развернуть стек, и можешь даже вприсядку сплясать при этом. Но, даже в простом случае для того, чтобы откатиться и "попробовать снова", тебе нужно в первую очередь убрать причину дедлока. Удачи в этом, как говорится, и удачи не поймать еще один deadlock exception где-нибудь при "откате и повторе". Но в общем случае даже в системе без внешних исполнительных устройств, откатиться назад ты не сможешь — другие потоки-то работали и данные, с которыми твой поток поймал лок, как правило, после отката уже устарели. Ну а если есть исполнительные устройства — смотри выше.

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

L>>>Еще раз — исправлять косяки проектирования в рантайме поздно. И в общем случае невозможно.

BFE>>В самом деле, если у вас все данные протаскиваются через одну нитку, то исправить такой архитектурный косяк при нехватки производительности невозможно.
L>Никому не интересна высокая производительность системы, которая не может работать корректно.
А я разве с этим спорю?

L>С другой стороны, корректно и стабильно работающую систему, да еще и покрытую серией автотестов, весь косяк которой своидтся к недостаточной производительности, всегда можно смасштабировать. Особенно если ее разработчики такую возможность предусмотрели.

О, да! Знаю я эти сказки.

L>И, чтобы уж добить. У нас есть система, в которой все данные "протаскиваются через одну нитку". Пока что ни один другой продукт на рынке не смог добиться даже 1/1000 от ее производительности и 1/100 от масштабируемости. Угадай, почему?

Это интересная игра: угадай почему чёрный ящик делающий неведомо что, работает лучше, чем другой чёрный ящик.
Попробую угадать.
Количество инсталляций системы меньше сотни?
Программа раздаётся бесплатно?
Замедление в 1000 пользователь всё равно заметить не сможет?
И каждый день — без права на ошибку...
Re[28]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 01.07.14 09:37
Оценка:
Здравствуйте, landerhigh, Вы писали:

BFE>>Может и не лучший, но вот, что поиском нашёл в стандарте:

L>Стандарт не описывает, как именно детектится дедлок, кроме случая, когда один и тот же поток пытается дважды забрать нерекурсивный мьютекс. Как и зачем это попало в стандарт —
Раз стандарт не описывает, как именно происходит обнаружение deadlock'а, то это значит, что в общем случае вы не можете предполагать, что он не детектируется.

BFE>>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.

L>Единственный правильный способ обработать такое исключение — записать дамп и упасть.
Упасть — не вариант. (У вас что, поддержка платная?)
И каждый день — без права на ошибку...
Re[29]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 01.07.14 09:43
Оценка:
Здравствуйте, sokel, Вы писали:

S>Microsoft'ы, кстати, кидают на рекурсивных локах (VS13):


Есть специальный std::recursive_mutex

S>
S>std::mutex m;
S>try {
S>    m.lock();
S>    m.lock();
S>}
S>catch(std::exception& e) {
S>    std::cout << "exception: " << e.what() << std::endl;
S>}

>> device or resource busy : device or resource busy
S>


По стандарту:

4 [ Note: A program may deadlock if the thread that owns a mutex object calls lock() on that object. If
the implementation can detect the deadlock, a resource_deadlock_would_occur error condition may be
observed. —end note ]

Re[29]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 01.07.14 10:19
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Раз стандарт не описывает, как именно происходит обнаружение deadlock'а, то это значит, что в общем случае вы не можете предполагать, что он не детектируется.


Я предпочитаю знать, что он не возникает, а не надеяться на чьи-то предположения.

BFE>>>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.

L>>Единственный правильный способ обработать такое исключение — записать дамп и упасть.
BFE>Упасть — не вариант.

Еще раз, медленно и по буквам. В общем случае восстановление работы программы после критического софтверного сбоя (AV, PVFC, std::out_of_memory, дедлок) невозможно. Ты можешь тешить себя надеждами, что всех перехитрил, но на самом деле ты объегориваешь лишь самого себя, поскольку продолжать работу с программой, которая загнала себя в никто не знает какое состояние, нельзя. Она тебе диск форматнет или кингстоны откроет, и ты не будешь иметь не малейшего представления о факапе, пока вода под койку не потечет.
Ты банально не знаешь, что привело к дедлоку (если бы знал, дедлок бы не случился). Ты не имеешь права делать никакие предположения о возможности и/или безопасности продолжения работы. Period. Единственное разумное действие — упасть с дампом.

BFE>(У вас что, поддержка платная?)


Очень платная и одна из лучших в индустрии. Клиенты в очереди стоят. Только ей практически не приходится иметь дело с дампами и дедлоками.
www.blinnov.com
Re[29]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 01.07.14 10:27
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.

EP>>Во-первых, какие-то специальные телодвижения не нужны (разве что поставить assert ) — там же в корне std::exception.
BFE>assert — это исключительно средство разработки. В дебаг версии на него можно вообще никогда не наткнуться.

1. По хорошему тесты и equivalence partitioning должны были запустить assert у которого существуют пути запуска. Этого конечно проще достичь когда низкоуровневый многопоточный код не раскидан по всей системе, а локализован в отдельном модуле.
2. При необходимости, assert'ы можно включить и в release'е (но нужно помнить о том, что некоторые проверки могут поменять postconditions, например алгоритмическую сложность). Видел подобные assert'ы в release в одной очень популярной программе, с сотнями миллионов установок.
3. Необязательно использовать стандартный assert, например некоторые библиотеки специально используют собственный assert, чтобы дать пользователю больший контроль, например SGI STL.

EP>>Во-вторых, опять-таки — это же явный баг в программе, если уж случилось такое, то неизвестно как там оно дальше пойдёт в разнос.

BFE>Скажите, а вот когда происходит exception не связанный с отказом оборудования, это ошибка в программе или нет?

В каком смысле? Для чего вообще используются исключения?
Для простой и "трудноигнорируемой" передачи информации об исключительных ситуациях с нижних уровней, на высшие. Причём по пути исключение может пролететь через 3rd party слой, ничего не знающий про этот тип исключений.
Исключительные ситуации, в моём понимании, это внешние проблемы по отношению к коду, а не его внутренние баги. Например при десериализации структуры внезапно закончился файл — это же не баг кода, да? или например в знаменателях повылазили значения близкие к нулю из-за вырожденности системы, или входные данные в неправильном формате и т.д и т.п.
Re[31]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 01.07.14 11:00
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>А где ты увидел преход на личности? Это опыт. Ничего личного.

BFE>Согласно моему опыту, качество архитектурных решений следует выбирать с учётом квалификации членов команды. Архитектурное решение выбранное в команде говорит об уровне квалификации членов этой команды.

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

BFE>Вы разговариваете сами с собой. Какая ещё однопутная железная дорога? Давайте без демагогических аналогий.



Вот тебе пример — построили однопутную железную дорогу и разъезд на ней, чтобы можно было пропускать встречные поезда. В первый же день оказалось, что обычные до этих мест длинные товарняки на разъезд полностью не влезают. В результате — дедлок и миллионы убытка в день и стоящая раком сеть. Кто виноват — машинист поезда или все же горе-проектировщики разъезда?


Классический пример дедлока. Откатывай/исправляй. Потом попробуем твое решение применить в софте.

L>>Мы работаем в терминах архитектурных решений, разработанных специально для конкретных нужд. Будут это потоки, таймеры или даже вовсе прерывания от устройств, нам совершенно неважно.

BFE>Ясно.

Что именно?

L>>И ты с темы не соскакивай. Расскажи, как ты будешь исправлять такой дедлок.

BFE>deadlock в реалтайм системе с необратимыми действиями?

Где ты увидел реалтайм? Это просто асинхронная система, единственное жесткое контрактное требование к которой — никогда не показывать юзеру неверных/невалидных данных.

BFE>Очевидно, что один из локов лишний.


Воооооот. То есть архитектура неправильная?

BFE>Оба действия должны лежать быть защищены одним синхронизирующим объектом. А как это относится к теме?


Да вот так, что ты только что признался, что исправить дедлок в реалтайме нельзя. Ура, кажется, мы наконец-то куда-то продвинулись!

BFE>Так или иначе, но исключение произойти может.


Еще раз. Дедлок — это не исключение. Это ошибка в коде программы. Как AV или PVFC. Попытка некоторых его задетектить и обратить в исключение ошибку в коде программы исправить не может.
Причем, в отличие от той же AV или PVFC, для того, чтобы гарантированно обнаруживать дедлоки, да еще и без ложных срабатываний, нужно реализовать весьма сложную систему только для этого. Вроде того, что Lazin пытается сделать. И даже в таком случае она окажется абсолютно бесполезной в случае внешнего для этой системы лока. Затем, даже если предположить, что такая система есть и работает, нужно также гарантировать, что ты можешь безопасно откатить все возможные локи. Я для этого нужно их всех без исключения найти и идентифицировать. И тут возникает дилемма — если ты знаешь, где у тебя может возникнуть дедлок, то почему бы просто не изменить код так, чтобы дедлок не возникал? Причем, на исправление ошибки в коде у тебя уйдет ну 2% времени, нужного для реализации системы обнаружения и обезвреживания дедлоков.
А неизвестный дедлок откатывать нельзя. Ты же не знаешь, как и почему он произошел и какие побочные эффекты возникнут.
Бессмысленное занятие, в общем.

BFE>Значит вам в любом случае, хотите вы этого или нет, придётся, хоть с бубном, хоть с плясками, но откатывать стек до какого-то уровня. И умение обрабатывать ошибочные ситуации определяет ваше умение писать качественный софт.


Ты только что сам наверху написал, что исправить сложившуюся ситуацию откатом стека нельзя. Юлить начинаешь.
Мы вообще исключения не обсуждаем, оставь их в покое.

BFE>Всех возможных ситуаций вы в сложном проекте предусмотреть заранее не сможете, так что если ваша архитектура не включает в себя обработку ошибочных ситуаций, значит у вас получится некачественный продукт, который работает только в тепличных условиях.


Настрадаю в предсказамуса — некачественный продукт получается в основном у тех, кто игнорирует ах... какие серьезные архитектурные косяки ради умозрительной "производительности".

L>>>>Еще раз — исправлять косяки проектирования в рантайме поздно. И в общем случае невозможно.

BFE>>>В самом деле, если у вас все данные протаскиваются через одну нитку, то исправить такой архитектурный косяк при нехватки производительности невозможно.
L>>Никому не интересна высокая производительность системы, которая не может работать корректно.
BFE>А я разве с этим спорю?

Споришь. Система с дедлоками по определению некорректна.

L>>С другой стороны, корректно и стабильно работающую систему, да еще и покрытую серией автотестов, весь косяк которой своидтся к недостаточной производительности, всегда можно смасштабировать. Особенно если ее разработчики такую возможность предусмотрели.

BFE>О, да! Знаю я эти сказки.

Ну не повезло тебе, видимо, встретиться с нормальными масштабируемыми системами.

L>>И, чтобы уж добить. У нас есть система, в которой все данные "протаскиваются через одну нитку". Пока что ни один другой продукт на рынке не смог добиться даже 1/1000 от ее производительности и 1/100 от масштабируемости. Угадай, почему?

BFE>Это интересная игра: угадай почему чёрный ящик делающий неведомо что, работает лучше, чем другой чёрный ящик.

Я уже писал в этом чати, какой именно черный ящик я разрабатываю. Больше заниматься пенисометрией не намерен.
www.blinnov.com
Re[32]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 01.07.14 12:27
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>А где ты увидел преход на личности? Это опыт. Ничего личного.

BFE>>Согласно моему опыту, качество архитектурных решений следует выбирать с учётом квалификации членов команды. Архитектурное решение выбранное в команде говорит об уровне квалификации членов этой команды.
L>То есть исходя из квалификации самого слабого члена команды?
Угу. Если человек не может исправить deadlock без изменения архитектуры, то тут уж ничего не поделаешь, приходится заранее под его особенности проектировать архитектуру, раз уж есть ограничения в понимании.

L>Вы на вижуал васике пишете?

нет.

BFE>>Вы разговариваете сами с собой. Какая ещё однопутная железная дорога? Давайте без демагогических аналогий.

L>

L>Вот тебе пример — построили однопутную железную дорогу и разъезд на ней, чтобы можно было пропускать встречные поезда. В первый же день оказалось, что обычные до этих мест длинные товарняки на разъезд полностью не влезают. В результате — дедлок и миллионы убытка в день и стоящая раком сеть. Кто виноват — машинист поезда или все же горе-проектировщики разъезда?

L>Классический пример дедлока. Откатывай/исправляй. Потом попробуем твое решение применить в софте.
Ну, раз вы настаиваете на демагогических приёмах, то пожалуйста. Очевидно, что стоимость работ и земли очень высока, иначе вместо короткого разъезда построили бы полноценную двухпутную дорогу. Согласны?

L>>>Мы работаем в терминах архитектурных решений, разработанных специально для конкретных нужд. Будут это потоки, таймеры или даже вовсе прерывания от устройств, нам совершенно неважно.

BFE>>Ясно.
L>Что именно?
Мне ясен ваш ответ.

BFE>>Очевидно, что один из локов лишний.

L>Воооооот. То есть архитектура неправильная?
Как Java не спасает от ошибок программистов, так и обнаружение deadlock'ов не спасает от ошибок архитектуры.

BFE>>Оба действия должны лежать быть защищены одним синхронизирующим объектом. А как это относится к теме?

L>Да вот так, что ты только что признался, что исправить дедлок в реалтайме нельзя. Ура, кажется, мы наконец-то куда-то продвинулись!
Что ж. Придётся повторить: если необходимо обеспечить согласованность данных и над первым из данных совершается необратимое действие, то операции над такими данными надо проводить под защитой одного мютекса. Если же, по каким-либо причинам, данные защищаются двумя различными мютексами, то захват обоих мютексов должен производится до начала первой операции. В стандарте даже есть специальная функция для этого.

BFE>>Так или иначе, но исключение произойти может.

L>Еще раз. Дедлок — это не исключение.
А я этого и не утверждал.

L>Это ошибка в коде программы.

Зависит исключительно от точки зрения.

L>Как AV или PVFC.

Расшифруйте, пожалуйста.

L>Попытка некоторых его задетектить и обратить в исключение ошибку в коде программы исправить не может.

Ошибку не может, а вот deadlock — может.

L>Причем, в отличие от той же AV или PVFC, для того, чтобы гарантированно обнаруживать дедлоки, да еще и без ложных срабатываний, нужно реализовать весьма сложную систему только для этого.

Если бы это было просто, это давно бы уже сделали и использовали. Это как с указателями. Сначала все ловили баги с не инициализированными указателями, потом придумали smart указатели и ценой "чудовищного" оверхеда сегодня эта проблема ушла в небытие.

L>Вроде того, что Lazin пытается сделать. И даже в таком случае она окажется абсолютно бесполезной в случае внешнего для этой системы лока. Затем, даже если предположить, что такая система есть и работает, нужно также гарантировать, что ты можешь безопасно откатить все возможные локи. Я для этого нужно их всех без исключения найти и идентифицировать. И тут возникает дилемма — если ты знаешь, где у тебя может возникнуть дедлок, то почему бы просто не изменить код так, чтобы дедлок не возникал?

Потому, что это может оказаться невыгодно с точки зрения скорости.

L>Причем, на исправление ошибки в коде у тебя уйдет ну 2% времени, нужного для реализации системы обнаружения и обезвреживания дедлоков.

Это зависит от сложности проекта. К тому же, если уже есть готовая библиотека...

L>А неизвестный дедлок откатывать нельзя. Ты же не знаешь, как и почему он произошел и какие побочные эффекты возникнут.

L>Бессмысленное занятие, в общем.
Если система толерантна к исключениям, то нет ничего сложного.

BFE>>Значит вам в любом случае, хотите вы этого или нет, придётся, хоть с бубном, хоть с плясками, но откатывать стек до какого-то уровня. И умение обрабатывать ошибочные ситуации определяет ваше умение писать качественный софт.

L>Ты только что сам наверху написал, что исправить сложившуюся ситуацию откатом стека нельзя. Юлить начинаешь.
L>Мы вообще исключения не обсуждаем, оставь их в покое.
Я говорю про решение, а не про что-то ещё.

L>Споришь. Система с дедлоками по определению некорректна.

Система, которая обнаруживает возможность deadlock'а на следующем шаге и бросает исключение до наступления состояния взаимной блокировки по определению не имеет deadlock'ов.

L>Ну не повезло тебе, видимо, встретиться с нормальными масштабируемыми системами.

Тут дело не в везении.

L>Я уже писал в этом чати, какой именно черный ящик я разрабатываю. Больше заниматься пенисометрией не намерен.

Ну так и не надо было начинать.
И каждый день — без права на ошибку...
Re[22]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 01.07.14 14:41
Оценка:
L>Наверное, дедлок где-то в проводке

Нет, просто система аварийного охлаждения сломалась и активная зона перегрелась, ничего страшного, придется управляющие стержни заменить и долить немного тяжелой воды

L>Ой. Все, что от нормальной библиотеки требуется — это четкая гарантия thread safety. Должно быть четко документировано, какие методы безусловно безопасны, какие — безопасны при условии того, что они работают с разными данными, а какие требуют блокировки. Еще необходимо четко описать, как именно и из какого потока вызываеются колбеки.

L>Как пользователь 100500 разных библиотек, насмотревшийся всякго, попрошу — пожалайста, не надо делать ничего больше! Для библиотеки крайне важно не совершать никаких ненаблюдаемых извне и не запрошенных явно действий.

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

L>>>Они как минимум убирают необходимость в 100500 явных блокировок.

L>>atomic-и это тоже синхронизация
L>В идеале atomic это просто объект данных, который гарантированно можно целиком записать за одну операцию и при этом у него не будет внешне наблюдаемого промежуточного "недозаписанного" состояния.

performance wise это примерно то же самое что и лок на короткое время, только тебе еще нужно думать о таких вещах как false sharing и выравнивание
с точки зрения корректности я с тобой не спорил, если что

L>>>Ты кагбы можешь мне это не рассказывать, для меня IEC61850, MODBUS, BACNET, DNP3 и прочие страшные слова — вовсе не пустой звук. Но мы подобных проблем почему-то не имеем.

L>>софт о котором я говорил не имеет подобных проблем, просто приведен в качестве примера сервиса, обрабатывающего столько данных, сколько по твоему не бывает он используется, например, в ЦДУ СО ЕЭС (судя по использованным аббревиатурам ты должен знать что это такое) для сбора всей телеметрии, какая только есть

L>Эээ, это тебе в СО ЕЭС показалось, что там много данных? А я-то уж было напрягся


В ЦДУ стекаются данные со всей энергосистемы РФ, я уже не помню детали, но у них там было очень много телеизмерений/телесигналов и менялись они довольно шустро.
Re[33]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 01.07.14 17:20
Оценка:
Здравствуйте, B0FEE664, Вы писали:

L>>То есть исходя из квалификации самого слабого члена команды?

BFE>Угу. Если человек не может исправить deadlock без изменения архитектуры, то тут уж ничего не поделаешь, приходится заранее под его особенности проектировать архитектуру, раз уж есть ограничения в понимании.

Правильная архитектура исключает образование дедлоков. При чем тут абстрактный человек в вакууме, который не в состоянии?

L>>Вы на вижуал васике пишете?

BFE>нет.

А судя по ответам ниже — таки да

L>>Классический пример дедлока. Откатывай/исправляй. Потом попробуем твое решение применить в софте.

BFE>Ну, раз вы настаиваете на демагогических приёмах, то пожалуйста. Очевидно, что стоимость работ и земли очень высока, иначе вместо короткого разъезда построили бы полноценную двухпутную дорогу. Согласны?

Вот это и есть демагогия. Есть много причин, по которых строят однопутную дорогу. Исправлять-то как будешь?

BFE>>>Очевидно, что один из локов лишний.

L>>Воооооот. То есть архитектура неправильная?
BFE>Как Java не спасает от ошибок программистов, так и обнаружение deadlock'ов не спасает от ошибок архитектуры.

О!

BFE>>>Оба действия должны лежать быть защищены одним синхронизирующим объектом. А как это относится к теме?

L>>Да вот так, что ты только что признался, что исправить дедлок в реалтайме нельзя. Ура, кажется, мы наконец-то куда-то продвинулись!
BFE>Что ж. Придётся повторить: если необходимо обеспечить согласованность данных и над первым из данных совершается необратимое действие, то операции над такими данными надо проводить под защитой одного мютекса.

Иными словами, нужно включить моск, а не надеяться, что чудо-либа отловит дедлок...

BFE>>>Так или иначе, но исключение произойти может.

L>>Еще раз. Дедлок — это не исключение.
BFE>А я этого и не утверждал.

А зачем притащил сюда исключения?

L>>Это ошибка в коде программы.

BFE>Зависит исключительно от точки зрения.

Дедлоки возникают исключительно в результате ошбики в коде программы. Независимо от точек зрения.

L>>Как AV или PVFC.

BFE>Расшифруйте, пожалуйста.

А ты точно на с++ пишешь?

L>>Попытка некоторых его задетектить и обратить в исключение ошибку в коде программы исправить не может.

BFE>Ошибку не может, а вот deadlock — может.

Ты уже исправил дедлоки, которые я в пример привел?

L>>Причем, в отличие от той же AV или PVFC, для того, чтобы гарантированно обнаруживать дедлоки, да еще и без ложных срабатываний, нужно реализовать весьма сложную систему только для этого.

BFE>Если бы это было просто, это давно бы уже сделали и использовали. Это как с указателями. Сначала все ловили баги с не инициализированными указателями, потом придумали smart указатели и ценой "чудовищного" оверхеда сегодня эта проблема ушла в небытие.

Отучаемся говорить за "всех". Не "все" ловили баги. Те, кто ниасилил голые указатели, так же прекрасно сегодня ловят NullPointerException. И умные указатели вообще не для того придумали, к слову.

L>>Вроде того, что Lazin пытается сделать. И даже в таком случае она окажется абсолютно бесполезной в случае внешнего для этой системы лока. Затем, даже если предположить, что такая система есть и работает, нужно также гарантировать, что ты можешь безопасно откатить все возможные локи. Я для этого нужно их всех без исключения найти и идентифицировать. И тут возникает дилемма — если ты знаешь, где у тебя может возникнуть дедлок, то почему бы просто не изменить код так, чтобы дедлок не возникал?

BFE>Потому, что это может оказаться невыгодно с точки зрения скорости.

Кто о чем, а ты о скорости. Ты, кстати, в курсе оверхеда от сработавшего исключения? Что там от твоей скорости останется? Ну и опять же, понятие скорости применимо лишь к корректно работающей системе. Система, допускающая дедлоки — некорректна.

L>>Причем, на исправление ошибки в коде у тебя уйдет ну 2% времени, нужного для реализации системы обнаружения и обезвреживания дедлоков.

BFE>Это зависит от сложности проекта. К тому же, если уже есть готовая библиотека...

То есть у тебя настолько сложная система, что ты не знаешь, есть там дедлоки или нет? И ты планируешь это резко пофиксить за счет магической либы?

L>>А неизвестный дедлок откатывать нельзя. Ты же не знаешь, как и почему он произошел и какие побочные эффекты возникнут.

L>>Бессмысленное занятие, в общем.
BFE>Если система толерантна к исключениям, то нет ничего сложного.

Дедлок — это не исключение. Это ошибка в программе.

BFE>>>Значит вам в любом случае, хотите вы этого или нет, придётся, хоть с бубном, хоть с плясками, но откатывать стек до какого-то уровня. И умение обрабатывать ошибочные ситуации определяет ваше умение писать качественный софт.

L>>Ты только что сам наверху написал, что исправить сложившуюся ситуацию откатом стека нельзя. Юлить начинаешь.
L>>Мы вообще исключения не обсуждаем, оставь их в покое.
BFE>Я говорю про решение, а не про что-то ещё.

Решение — это писать правильно, без дедлоков.

Я так понимаю, ты хочешь что-то вроде этого.

while (true)
try
{
  DoSomethingThatMayDeadlock();
  break;
}
catch (const DeadlockException& )
{
  Log << cat::WARNING << "Deadlocked. Try again.";
}


То есть ты заранее спланировал систему так, что ты знаешь, что DoSomething may deadlock indeed.
Вопросы:

  1. Почему бы не перепроектировать систему так, чтобы дедлока не было, т.к ты о нем знаешь?
  2. Кто-то что-то о "производительности" говорил. Ну-ну.
  3. В дедлоке всегда виноваты двое, кроме совсем уж вырожденных случаев, когда тред лочит сам себя. (Кстати, в этом случае вышеприведенный код прекрасно будет крутить бесконечный цикл. Бугагага) Что ты со вторым тредом делать будешь? А вдруг он надолго забрал лок? А вдруг там очередь из таких же тредов и все крутят исключения в цикле? Отличная получится производительность, ничего не скажешь
  4. DoSomethingThatMayDeadlock обязана уметь полностью откатить состояние абсолютно всех затронутых объектов данных при вообще любом (не только DeadlockException) исключении, которая она выпускает наружу. А в общем случае, особенно когда все треды активно взаимодействуют друг с другом, это невозможно в принципе.

L>>Споришь. Система с дедлоками по определению некорректна.

BFE>Система, которая обнаруживает возможность deadlock'а на следующем шаге и бросает исключение до наступления состояния взаимной блокировки по определению не имеет deadlock'ов.

Что-то мне это напоминает. А! Это как отделение милиции, в котором не регистрируют мелкие карманные кражи, чтобы статистику не портить
www.blinnov.com
Re[33]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 01.07.14 18:07
Оценка:
BFE>Угу. Если человек не может исправить deadlock без изменения архитектуры, то тут уж ничего не поделаешь, приходится заранее под его особенности проектировать архитектуру, раз уж есть ограничения в понимании.

Есть такая штука — try_lock называется. Не решение всех проблем, однако в описанном тобой случае, с помощью этого метода можно обрабатывать такие ошибки (try_lock + exponential backoff или yield с исключением по таймауту).
Re[2]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 01.07.14 18:09
Оценка:
MZ>Про графы: если делать по уму, то по идее граф должен выродиться
MZ>в линейный список мьютексов. Т.е. я хочу сказать, что то, что вы
MZ>делаете, обычно делают (видимо) проще -- лочат мьютексы в строго
MZ>определённом порядке.

Это и нужно для того, чтобы проверить, что лочат в строго определенном порядке.
Re[23]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 02.07.14 05:59
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Как пользователь 100500 разных библиотек, насмотревшийся всякго, попрошу — пожалайста, не надо делать ничего больше! Для библиотеки крайне важно не совершать никаких ненаблюдаемых извне и не запрошенных явно действий.


L>а) у меня не нормальная библиотека а библиотека синхронизации



Дело в том, что я прячу отдельные мьютексы за интерфейсом и говорю что объекты можно лочить так и так. Пользователь может решить (ошибочно), что он лочит разные объекты и дедлок невозможен в принципе, даже если он это делает в разном порядке, так как объекты реально разные и его архитектура это гарантирует, это и было бы так, если бы он использовал мьютексы напрямую, но у меня может получиться конфликт и возникнет дедлок.


А это тогда о чем?

L>б) я не собираюсь включать это по умолчанию, это механизм, который можно врубить дефайном, в рамках специального билда

L>г) твой аргумент — инвалид

Валид-валид.

L>>В идеале atomic это просто объект данных, который гарантированно можно целиком записать за одну операцию и при этом у него не будет внешне наблюдаемого промежуточного "недозаписанного" состояния.


L>performance wise это примерно то же самое что и лок на короткое время, только тебе еще нужно думать о таких вещах как false sharing и выравнивание


Это весьма implementation (platform) specific. в контексте данной дискуссии иррелевантно.

L>>Эээ, это тебе в СО ЕЭС показалось, что там много данных? А я-то уж было напрягся


L>В ЦДУ стекаются данные со всей энергосистемы РФ, я уже не помню детали, но у них там было очень много телеизмерений/телесигналов и менялись они довольно шустро.


Это ты по циферкам на экранах оценил?
www.blinnov.com
Re[24]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 02.07.14 09:40
Оценка:
L>

L>Дело в том, что я прячу отдельные мьютексы за интерфейсом и говорю что объекты можно лочить так и так. Пользователь может решить (ошибочно), что он лочит разные объекты и дедлок невозможен в принципе, даже если он это делает в разном порядке, так как объекты реально разные и его архитектура это гарантирует, это и было бы так, если бы он использовал мьютексы напрямую, но у меня может получиться конфликт и возникнет дедлок.


L>А это тогда о чем?


Я не вижу противоречий. Есть описание того, как это нужно использовать и определенное заранее поведение.


L>>>Эээ, это тебе в СО ЕЭС показалось, что там много данных? А я-то уж было напрягся


L>>В ЦДУ стекаются данные со всей энергосистемы РФ, я уже не помню детали, но у них там было очень много телеизмерений/телесигналов и менялись они довольно шустро.


L>Это ты по циферкам на экранах оценил?


Я работал в компании, сделавший для них scada систему. Лол.
Re[25]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 02.07.14 21:14
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>А это тогда о чем?


L>Я не вижу противоречий. Есть описание того, как это нужно использовать и определенное заранее поведение.


Вот именно. Либа должна делать только это и ничего больше.

L>>>>Эээ, это тебе в СО ЕЭС показалось, что там много данных? А я-то уж было напрягся


L>>>В ЦДУ стекаются данные со всей энергосистемы РФ, я уже не помню детали, но у них там было очень много телеизмерений/телесигналов и менялись они довольно шустро.


L>>Это ты по циферкам на экранах оценил?


L>Я работал в компании, сделавший для них scada систему. Лол.


Тогда должен представлять себе количество тегов и нагрузочную способность по алармам, а также частоту обновления. Там на самом деле ничего из ряда вон выходящего нет.
www.blinnov.com
Re[26]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 03.07.14 10:40
Оценка:
L>>Я не вижу противоречий. Есть описание того, как это нужно использовать и определенное заранее поведение.

L>Вот именно. Либа должна делать только это и ничего больше.


Ну так она это и делает, я правда не понимаю что тебе не нравится! Есть lock layer, явно сказано, что его нельзя использовать рекурсивно никогда Можно собрать дебаг версию, которая будет детектить дедлоки.

L>>Я работал в компании, сделавший для них scada систему. Лол.


L>Тогда должен представлять себе количество тегов и нагрузочную способность по алармам, а также частоту обновления. Там на самом деле ничего из ряда вон выходящего нет.


Каких еще тегов?
Re[27]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 03.07.14 11:12
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Вот именно. Либа должна делать только это и ничего больше.


L>Ну так она это и делает, я правда не понимаю что тебе не нравится! Есть lock layer, явно сказано, что его нельзя использовать рекурсивно никогда


Этого достаточно.

L>Можно собрать дебаг версию, которая будет детектить дедлоки.


А это лишнее. Если архитектура допускает дедлоки, то гарантированно отловить их все во время тестирования невозможно. Особенно в дебаг версии. Более того, рискуешь огрести "Ваша либа прохлопала дедлок, который вылез в продакшене и в результате у нас убытков на 100500 денег".

L>>>Я работал в компании, сделавший для них scada систему. Лол.


L>>Тогда должен представлять себе количество тегов и нагрузочную способность по алармам, а также частоту обновления. Там на самом деле ничего из ряда вон выходящего нет.


L>Каких еще тегов?


Понятно, проехали.
www.blinnov.com
Re[28]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 03.07.14 12:23
Оценка:
L>>Можно собрать дебаг версию, которая будет детектить дедлоки.
L>А это лишнее. Если архитектура допускает дедлоки, то гарантированно отловить их все во время тестирования невозможно. Особенно в дебаг версии. Более того, рискуешь огрести "Ваша либа прохлопала дедлок, который вылез в продакшене и в результате у нас убытков на 100500 денег".

Ты вообще смотрел как это все работает? Syncope не ищет дедлоки между отдельными локами (так как, как ты правильно заметил, это мало что даст), вместо этого, либа пытается искать некорректные паттерны использования, которые могут привести к дедлоку. Т.е. deadlock detector в syncope на самом деле просто проверяет — допускает ли архитектура дедлоки или нет. Для того, чтобы выполнить свою задачу, ему не нужно чтобы дедлок реально произошел. Ты можешь залочить что-нибудь в одном потоке, а потом, через 10 минут залочить что-нибудь еще в другом потоке в неправильном порядке и библиотека это обнаружит, а традиционный deadlock detector — нет, так как реальный дедлок в этом случае не произойдет.

L>>>>Я работал в компании, сделавший для них scada систему. Лол.


L>>>Тогда должен представлять себе количество тегов и нагрузочную способность по алармам, а также частоту обновления. Там на самом деле ничего из ряда вон выходящего нет.


L>>Каких еще тегов?


L>Понятно, проехали.


В нашей SCADA системе теги назывались по другому, если я конечно правильно понял о чем ты. Таких "тегов" было порядка 10К, точные цифры — сколько раз в секунду менялся каждый из них я, само собой, не помню но уж точно чаще 10-ти
Даже в вебе часто получаютются нагрузки порядка 100К req/sec. Всякие редисы/монги по твоему для чего еще нужны?
Re[29]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 03.07.14 13:06
Оценка:
Здравствуйте, Lazin, Вы писали:

L>Ты вообще смотрел как это все работает?


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

L>>>>>Я работал в компании, сделавший для них scada систему. Лол.


L>>>Каких еще тегов?


L>>Понятно, проехали.


L>В нашей SCADA системе теги назывались по другому, если я конечно правильно понял о чем ты. Таких "тегов" было порядка 10К, точные цифры — сколько раз в секунду менялся каждый из них я, само собой, не помню но уж точно чаще 10-ти


Там нет показателей, которые меняются так часто. Особенно при съеме таких интересных индикаторов, как счетчики энергии. Есть показатели, которые могут меняться с миллисекундным разрешением, но их скада считывает несколько другими механизмами, нежели поллингом.

10К тегов — для скады масштаб маленький, почти игрушечный.
www.blinnov.com
Re[29]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 03.07.14 13:37
Оценка:
Здравствуйте, Lazin, Вы писали:

L>Ты вообще смотрел как это все работает? Syncope не ищет дедлоки между отдельными локами (так как, как ты правильно заметил, это мало что даст), вместо этого, либа пытается искать некорректные паттерны использования, которые могут привести к дедлоку. Т.е. deadlock detector в syncope на самом деле просто проверяет — допускает ли архитектура дедлоки или нет. Для того, чтобы выполнить свою задачу, ему не нужно чтобы дедлок реально произошел. Ты можешь залочить что-нибудь в одном потоке, а потом, через 10 минут залочить что-нибудь еще в другом потоке в неправильном порядке и библиотека это обнаружит, а традиционный deadlock detector — нет, так как реальный дедлок в этом случае не произойдет.


Погоди, она ж все равно требует, чтобы все эти события произошли в рантайме. Как ты отметил, необязательно вызвать лок, важно, чтобы были зафискированы опасные пересечения векторов блокировок.
Это, действительно, лучше, чем сидеть и ждать наступления дедлока, но все равно дает недостаточные гарантии.
Кроме того, мне кажется, что возможны false positives.
www.blinnov.com
Re[30]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 03.07.14 14:13
Оценка:
L>Погоди, она ж все равно требует, чтобы все эти события произошли в рантайме. Как ты отметил, необязательно вызвать лок, важно, чтобы были зафискированы опасные пересечения векторов блокировок.

Да, все фиксируется в рантайме. Но тут фишка в том, что все это не должно обязательно пересекаться во времени. Все может вообще в одном потоке выполняться и неправильный порядок захвата lock layer-ов будет зафиксирован. В этом то и была идея изначально. Уровни блокировок нужны для того, чтобы обозначить эти самые уровни блокировок в приложении явным образом + это позволяет реализовать мой R/W мьютекс + упрощает проверки в рантайме. Текущая реализация по мотивам того, что предложил sokel (еще незакомиченая) для 16 уровней вложенности блокировок добавляет примерно 20% накладных расходов на проверки. В реальной программе должно быть 1-2 уровня, едва ли больше. Так что эти проверки можно считать практически бесплатными.

По поводу статических проверок — я не могу это реализовать по понятным причинам. TMP использовать не получится, так как стек вызовов не существует в compile time. И вообще, статический анализ vs рантайм анализ, это ложная дихотомия. Не все возможно проверить статически, какая-то информация есть только в рантайме, с другой стороны — в рантайме может не вызываться какой-то код, содержащий ошибку, поэтому, статический и динамический анализ могут хорошо дополнять друг друга, но уж никак не противопоставляться.

L>Это, действительно, лучше, чем сидеть и ждать наступления дедлока, но все равно дает недостаточные гарантии.

L>Кроме того, мне кажется, что возможны false positives.

Смотря что считать false positive. Если, допустим, есть 2 уровня и приложение лочит эти уровни сначала в одном порядке один раз и больше никогда так не делает (допустим во время инициализации), а потом все время лочит в другом порядке, то это false positive, да. Но я сомневаюсь в том, что защищаться от таких вот false positives хоть сколько нибудь практично.
Re[31]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 03.07.14 18:27
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Погоди, она ж все равно требует, чтобы все эти события произошли в рантайме. Как ты отметил, необязательно вызвать лок, важно, чтобы были зафискированы опасные пересечения векторов блокировок.


L>Да, все фиксируется в рантайме. Но тут фишка в том, что все это не должно обязательно пересекаться во времени. Все может вообще в одном потоке выполняться и неправильный порядок захвата lock layer-ов будет зафиксирован. В этом то и была идея изначально.


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

L>>Это, действительно, лучше, чем сидеть и ждать наступления дедлока, но все равно дает недостаточные гарантии.

L>>Кроме того, мне кажется, что возможны false positives.

L>Смотря что считать false positive. Если, допустим, есть 2 уровня и приложение лочит эти уровни сначала в одном порядке один раз и больше никогда так не делает (допустим во время инициализации), а потом все время лочит в другом порядке, то это false positive, да. Но я сомневаюсь в том, что защищаться от таких вот false positives хоть сколько нибудь практично.


Я вполне могу допустить валидные случаи, когда одни и те же пары мьютексов лочатся в разных порядках в одном и том же или разных тредах, но не вызывают дедлока, т.к. такое поведение является предусмотренным архитектурой. Будет честный false positive.
www.blinnov.com
Re[32]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 04.07.14 09:09
Оценка:
L>Опасность дедлоков в том, что в теплице при тестировании они могут не вылезать. То есть может быть такое условие, но вылезает оно только после свистка членистоногово после непродолжитльных осадков жидкой формы в четвертый день недели. То есть неправильный порядок блокировки может никак себя не выдать даже с этой либой, что бы кто ни делал.

И решения у этой проблемы все равно нет.

L>Я вполне могу допустить валидные случаи, когда одни и те же пары мьютексов лочатся в разных порядках в одном и том же или разных тредах, но не вызывают дедлока, т.к. такое поведение является предусмотренным архитектурой. Будет честный false positive.


Ну я же явно оговариваю, что так делать нельзя. Так что здесь все ок.
Re[10]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 04.07.14 09:58
Оценка:
Здравствуйте, SkyDance, Вы писали:

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

SD>Для этого потоки не нужны. Reactor-based model украдена задолго до нас. У вас классический пример сервера (просто взгляните на ваш "одноядерный ARM" как на сервер, клиентами которого выступают моторы и приборные панели).

Если бы у нас была реалтайм система, то так бы и пришлось делать. Да, сначала так и пытались, но оказалось, что это довольно сложный патерн в плане добавления в него новых событий: один loop должен знать о всех событиях системы, что оказалось очень неудобным, так как у нас несколько раз поменялись подключаемые устройства и их характеристики. Программа должна работать в разных конфигурациях оборудования. Оказалось проще под каждое устройство написать свой программный модуль со своими циклами обработки данных, чем иметь один динамический изменяемый event loop.

PS Ходить по воде и разрабатывать программы, следуя спецификации, очень просто… если они заморожены.
И каждый день — без права на ошибку...
Re[34]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 04.07.14 10:45
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>То есть исходя из квалификации самого слабого члена команды?

BFE>>Угу. Если человек не может исправить deadlock без изменения архитектуры, то тут уж ничего не поделаешь, приходится заранее под его особенности проектировать архитектуру, раз уж есть ограничения в понимании.
L>Правильная архитектура исключает образование дедлоков. При чем тут абстрактный человек в вакууме, который не в состоянии?
Вот поэтому "правильная" архитектура исключает образование дедлоков.

L>>>Вы на вижуал васике пишете?

BFE>>нет.
L>А судя по ответам ниже — таки да
Двенадцать лет назад я закончил писать на Visual Basic тестовые примеры для OCX Active X контролов, что сам же и разрабатывал. Они, кстати, хорошо продавались.

L>>>Классический пример дедлока. Откатывай/исправляй. Потом попробуем твое решение применить в софте.

BFE>>Ну, раз вы настаиваете на демагогических приёмах, то пожалуйста. Очевидно, что стоимость работ и земли очень высока, иначе вместо короткого разъезда построили бы полноценную двухпутную дорогу. Согласны?
L>Вот это и есть демагогия.
Разуется этот демагогия: искать решения для компьютерной программы в мире реальных вещей.
L>Есть много причин, по которых строят однопутную дорогу. Исправлять-то как будешь?
Так вы согласны с утверждением о стоимости?

BFE>>Что ж. Придётся повторить: если необходимо обеспечить согласованность данных и над первым из данных совершается необратимое действие, то операции над такими данными надо проводить под защитой одного мютекса.

L>Иными словами, нужно включить моск, а не надеяться, что чудо-либа отловит дедлок...
Я бы советовал вообще не отключать "моск" при программировании.

L>А зачем притащил сюда исключения?

Для примера и конкретики.

L>>>Это ошибка в коде программы.

BFE>>Зависит исключительно от точки зрения.
L>Дедлоки возникают исключительно в результате ошбики в коде программы. Независимо от точек зрения.
Какие ваши доказательства?

L>>>Как AV или PVFC.

BFE>>Расшифруйте, пожалуйста.
L>А ты точно на с++ пишешь?
Да.

L>>>Попытка некоторых его задетектить и обратить в исключение ошибку в коде программы исправить не может.

BFE>>Ошибку не может, а вот deadlock — может.
L>Ты уже исправил дедлоки, которые я в пример привел?
Это вы о физическом мире?

L>Отучаемся говорить за "всех". Не "все" ловили баги. Те, кто ниасилил голые указатели, так же прекрасно сегодня ловят NullPointerException. И умные указатели вообще не для того придумали, к слову.

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

L>Кто о чем, а ты о скорости. Ты, кстати, в курсе оверхеда от сработавшего исключения? Что там от твоей скорости останется?

Да. От задачи зависит.
L>Ну и опять же, понятие скорости применимо лишь к корректно работающей системе. Система, допускающая дедлоки — некорректна.
Система не допускающая попадания в состояние deadlock'а не допускает deadlock'ов.

L>То есть у тебя настолько сложная система, что ты не знаешь, есть там дедлоки или нет? И ты планируешь это резко пофиксить за счет магической либы?

Я говорю о системе, которая обнаруживает состояние близкое к deadlock'у во время исполнения.

L>Дедлок — это не исключение. Это ошибка в программе.

Система не допускающая попадания в состояние deadlock'а не допускает deadlock'ов.

L>Я так понимаю, ты хочешь что-то вроде этого.

да.
L>
L>while (true)
L>try
L>{
L>  DoSomethingThatMayDeadlock();
L>  break;
L>}
L>catch (const DeadlockException& )
L>{
L>  Log << cat::WARNING << "Deadlocked. Try again.";
L>}
L>


L>То есть ты заранее спланировал систему так, что ты знаешь, что DoSomething may deadlock indeed.

L>Вопросы:
L>

    L>
  1. Почему бы не перепроектировать систему так, чтобы дедлока не было, т.к ты о нем знаешь?
    Т.к. перепроектирование системы может потребовать неоправданных затрат.

    L>
  2. Кто-то что-то о "производительности" говорил. Ну-ну.
    О какой производительности говорите вы, сведя задачу к однопоточному исполнению?

    L>
  3. В дедлоке всегда виноваты двое, кроме совсем уж вырожденных случаев, когда тред лочит сам себя. Что ты со вторым тредом делать будешь? (Кстати, в этом случае вышеприведенный код прекрасно будет крутить бесконечный цикл. Бугагага)
    А вот это реальная бага.

    L>А вдруг он надолго забрал лок?

    От однопоточного исполнения отличия не будет.

    L>А вдруг там очередь из таких же тредов и все крутят исключения в цикле? Отличная получится производительность, ничего не скажешь

    При правильном проектировании проблема livelock'а не возникнет.

    L>
  4. DoSomethingThatMayDeadlock обязана уметь полностью откатить состояние абсолютно всех затронутых объектов данных при вообще любом (не только DeadlockException) исключении, которая она выпускает наружу. А в общем случае, особенно когда все треды активно взаимодействуют друг с другом, это невозможно в принципе.
    L>
Т.е. вы пишите программы, которые не бросают исключений?

L>>>Споришь. Система с дедлоками по определению некорректна.

BFE>>Система, которая обнаруживает возможность deadlock'а на следующем шаге и бросает исключение до наступления состояния взаимной блокировки по определению не имеет deadlock'ов.
L>Что-то мне это напоминает. А! Это как отделение милиции, в котором не регистрируют мелкие карманные кражи, чтобы статистику не портить
Опять демагогия.
И каждый день — без права на ошибку...
Re[34]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 04.07.14 10:48
Оценка:
Здравствуйте, Lazin, Вы писали:

BFE>>Угу. Если человек не может исправить deadlock без изменения архитектуры, то тут уж ничего не поделаешь, приходится заранее под его особенности проектировать архитектуру, раз уж есть ограничения в понимании.

L>Есть такая штука — try_lock называется. Не решение всех проблем, однако в описанном тобой случае, с помощью этого метода можно обрабатывать такие ошибки (try_lock + exponential backoff или yield с исключением по таймауту).

Вообще-то локальной информации не достаточно для обнаружения deadlock'а. Чем тут try_lock поможет?
И каждый день — без права на ошибку...
Re[30]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 04.07.14 11:14
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

BFE>>>>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.

EP>>>Во-первых, какие-то специальные телодвижения не нужны (разве что поставить assert ) — там же в корне std::exception.
BFE>>assert — это исключительно средство разработки. В дебаг версии на него можно вообще никогда не наткнуться.

EP>1. По хорошему тесты и equivalence partitioning должны были запустить assert у которого существуют пути запуска. Этого конечно проще достичь когда низкоуровневый многопоточный код не раскидан по всей системе, а локализован в отдельном модуле.

Мне кажется или это высказывание равносильно предложению: давайте отловим все баги в системе?

EP>2. При необходимости, assert'ы можно включить и в release'е (но нужно помнить о том, что некоторые проверки могут поменять postconditions, например алгоритмическую сложность). Видел подобные assert'ы в release в одной очень популярной программе, с сотнями миллионов установок.

Количество установок не говорит о качестве когда. Может оттого они и оставили assert'ы, что отладить не смогли.

EP>3. Необязательно использовать стандартный assert, например некоторые библиотеки специально используют собственный assert, чтобы дать пользователю больший контроль, например SGI STL.

Да и? А я вот видел, как предлагали assert в релизе заменить бросанием эксепшена.

Я же не про это говорю. Я говорю, что если есть код, который содержит в себе throw, то должен быть и catch, который его ловит даже если разработчик думает, что такое никогда не произойдёт. Если вы не собираетесь выкинуть ваш код, то будьте готовы, что завтра ваш код кто-то подправит и исключение, таки, будет брошено.

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


"в знаменателях повылазили значения близкие к нулю из-за вырожденности системы"
— разве это не баг в программе?

Если входные данные в неправильном формате приводят к состоянию deadlock'а, то это ошибка в программе?
И каждый день — без права на ошибку...
Re[30]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 04.07.14 11:30
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

BFE>>А что можно сказать про незапланированный deadlock?

EP>Это ошибка в коде
BFE>>Всё. Висим. Точка. Речь о том, чтобы в принципе избежать такой ситуации.
EP>Избежать deadlock'а или чего?
Т.е. вы считаете, что можно избежать всех deadlock'ов во всех возможных системах и исправить такие ошибки?

EP>Так мы же и так рассматриваем ситуацию, которой в корректной программе быть не должно. И соответственно раз произошёл deadlock, "то и не такое может произойти"

Качественный код должен корректно обрабатывать ошибочные ситуации, даже если этой ситуации "в корректной программе быть не должно".

EP>>>или что ещё хуже — livelock.

BFE>>livelock возможен, но и с ним понятно как бороться — отдавать время другим ниткам из той нитки, что deadlock обнаружила (после снятия всех блокировок). Если так действовать, то livelock возможен только при перегрузке системы.
EP>Незапланированный deadlock возможен только в коде с багом, точно также как и livelock.
Не бывает сложных систем без ошибок.
DoS-атака возможна даже на сервер работающий без ошибок. Чем DoS не livelock?

EP>Раз уж мы получили deadlock, то почему при раскрутке стэка не может вылезти livelock или ещё что-нибудь?

Что-нибудь всегда может "вылезти".

EP>Если требуется fault tolerance, не считая какие-то совсем тривиальные случаи, то придётся планировать аварийное завершение с освобождением всех ресурсов.

согласен. Но это другая тема.
И каждый день — без права на ошибку...
Re[33]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 04.07.14 17:17
Оценка:
Здравствуйте, Lazin, Вы писали:

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


L>И решения у этой проблемы все равно нет.


Есть — планирование и геноцид, дискриминация, ковровые бомбардировки и обычновенный фашизм во время code review.

L>>Я вполне могу допустить валидные случаи, когда одни и те же пары мьютексов лочатся в разных порядках в одном и том же или разных тредах, но не вызывают дедлока, т.к. такое поведение является предусмотренным архитектурой. Будет честный false positive.


L>Ну я же явно оговариваю, что так делать нельзя. Так что здесь все ок.


Ну то есть использовать эту либу правильно все равно смогут только те, кто прекрасно понимает все тонкости многопоточного программирования и без того. остальные будут напоминать обезьяну с гранатой.
www.blinnov.com
Re[11]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 04.07.14 17:19
Оценка:
Здравствуйте, B0FEE664, Вы писали:

SD>>Для этого потоки не нужны. Reactor-based model украдена задолго до нас. У вас классический пример сервера (просто взгляните на ваш "одноядерный ARM" как на сервер, клиентами которого выступают моторы и приборные панели).


BFE>Если бы у нас была реалтайм система, то так бы и пришлось делать. Да, сначала так и пытались, но оказалось, что это довольно сложный патерн в плане добавления в него новых событий: один loop должен знать о всех событиях системы, что оказалось очень неудобным, так как у нас несколько раз поменялись подключаемые устройства и их характеристики.


Какие проблемы предусмотреть соответствующее архитектурное решение? Оно ж простое как пробка.

BFE>PS Ходить по воде и разрабатывать программы, следуя спецификации, очень просто… если они заморожены.


This excuse is lame, bro.
www.blinnov.com
Re[31]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 04.07.14 18:40
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>>>Так что если пользоваться стандартом, то такие исключения надо обрабатывать.

EP>>>>Во-первых, какие-то специальные телодвижения не нужны (разве что поставить assert ) — там же в корне std::exception.
BFE>>>assert — это исключительно средство разработки. В дебаг версии на него можно вообще никогда не наткнуться.
EP>>1. По хорошему тесты и equivalence partitioning должны были запустить assert у которого существуют пути запуска. Этого конечно проще достичь когда низкоуровневый многопоточный код не раскидан по всей системе, а локализован в отдельном модуле.
BFE>Мне кажется или это высказывание равносильно предложению: давайте отловим все баги в системе?

Точнее: вместо раскидывания по всей системе, локализуем низкоуровневый многопоточный код в модуле, который легко протестировать/верифицировать.

EP>>2. При необходимости, assert'ы можно включить и в release'е (но нужно помнить о том, что некоторые проверки могут поменять postconditions, например алгоритмическую сложность). Видел подобные assert'ы в release в одной очень популярной программе, с сотнями миллионов установок.

BFE>Количество установок не говорит о качестве когда. Может оттого они и оставили assert'ы, что отладить не смогли.

Я ничего не говорил про качество кода, я всего лишь показал, что есть такая практика assert в release.

EP>>3. Необязательно использовать стандартный assert, например некоторые библиотеки специально используют собственный assert, чтобы дать пользователю больший контроль, например SGI STL.

BFE>Да и? А я вот видел, как предлагали assert в релизе заменить бросанием эксепшена.

Возможно и такое, но в целом, у assert и исключений разная семантика

BFE>Я же не про это говорю. Я говорю, что если есть код, который содержит в себе throw, то должен быть и catch, который его ловит даже если разработчик думает, что такое никогда не произойдёт. Если вы не собираетесь выкинуть ваш код, то будьте готовы, что завтра ваш код кто-то подправит и исключение, таки, будет брошено.


Это похоже на перевод темы с использования исключений при обнаружении deadlock'а, на использование исключений вообще.
Я же не против исключений в целом, и даже считаю что код должен быть к ним готов по-умолчанию.

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


BFE>"в знаменателях повылазили значения близкие к нулю из-за вырожденности системы"

BFE>- разве это не баг в программе?

Если такие значения возникли не из-за ошибки программиста, а например из-за ошибочного ввода (но всё же ожидаемого) — то нет, не баг.

BFE>Если входные данные в неправильном формате приводят к состоянию deadlock'а, то это ошибка в программе?


Зависит от. Я изначально говорил, что ошибка — это незапланированный deadlock.
Например, если у тебя некоторый хитрый алгоритм, который дедлочится на неправильных входных данных, и такое поведение полностью ожидаемо (мол предотвращение этого deadlock'а совсем, било бы по производительности в happy-case) — то нет, не ошибка, а ожидаемое состояние, из которого можно выбраться — так как известно каким образом мы туда попали.
Или другой вариант — если пользователь осведомлён, что данные в неправильном формате могут всё поломать, и несёт за это полную ответственность — то тут даже откат из deadlock'а можно не предусматривать, ибо ССЗБ.

Это всё вопрос pre/post-conditions.
Например у std::vector есть .operator[] и .at. Внутри первого assert (в некоторых реализациях), а внутри второго исключение.

Но, опять таки, вариант с неким хитрым алгоритмом, который на одних данных дедлочится, а на других нет — это скорее исключение чем правило.
В то же время, например, можно элементарно придумать ситуацию, в которой плохие входные данные приведут к выходу за границы массива.
Re[31]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 04.07.14 19:36
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>А что можно сказать про незапланированный deadlock?

EP>>Это ошибка в коде
BFE>>>Всё. Висим. Точка. Речь о том, чтобы в принципе избежать такой ситуации.
EP>>Избежать deadlock'а или чего?
BFE>Т.е. вы считаете, что можно избежать всех deadlock'ов во всех возможных системах и исправить такие ошибки?

Нет, я так не считаю.
Выброс исключения прям перед deadlock'ом, в который неизвестно как попали — не гарантирует выход в какое-то стабильное состояние, более того — может привести к ещё большим потерям.
Если нужен нормальный fault tolerance, то всё-равно придётся планировать аварийное завершение.

EP>>Так мы же и так рассматриваем ситуацию, которой в корректной программе быть не должно. И соответственно раз произошёл deadlock, "то и не такое может произойти"

BFE>Качественный код должен корректно обрабатывать ошибочные ситуации, даже если этой ситуации "в корректной программе быть не должно".

Не вижу оснований для данного утверждения.
Например, у алгоритмов STL есть определённые preconditions, при нарушении которых всё может пойти в разнос, вплоть до расстрела памяти или зависания — тем не менее, некачественным кодом он от этого не становится.

Опять таки, если по характеру задачи нужен fault tolerance, то нужно принимать соответствующие меры, например watchdog, дублирование, вплоть до использования разных алгоритмов и т.д и т.п.
Но прежде всего, имхо, нужно иметь корректный, оттестированный и верифицированный код — от этого никуда не деться в любом случае, никакие свистульки тут не помогут.

EP>>>>или что ещё хуже — livelock.

BFE>>>livelock возможен, но и с ним понятно как бороться — отдавать время другим ниткам из той нитки, что deadlock обнаружила (после снятия всех блокировок). Если так действовать, то livelock возможен только при перегрузке системы.
EP>>Незапланированный deadlock возможен только в коде с багом, точно также как и livelock.
BFE>Не бывает сложных систем без ошибок.

Выброс исключения из совершенно неизвестного состояния, не только не решит это ошибку, но ещё и может всё усугубить.

BFE>DoS-атака возможна даже на сервер работающий без ошибок. Чем DoS не livelock?


DoS это скорее starvation.

EP>>Раз уж мы получили deadlock, то почему при раскрутке стэка не может вылезти livelock или ещё что-нибудь?

BFE>Что-нибудь всегда может "вылезти".

При незапланированном deadlock'е, как неоднократно замечали ранее, находишься в неизвестном состоянии. Если кинуть исключение, то есть некоторый шанс вернуться в известное и стабильное состояние. Если же прибить процесс и начать заново, то шанс возврата в стабильное состояние намного больше, да и к тому же для fault tolerance это всё-равно понадобиться.
Re[34]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 06.07.14 09:44
Оценка:
L>>И решения у этой проблемы все равно нет.
L>Есть — планирование и геноцид, дискриминация, ковровые бомбардировки и обычновенный фашизм во время code review.

Я не могу вынести это в библиотеку

L>Ну то есть использовать эту либу правильно все равно смогут только те, кто прекрасно понимает все тонкости многопоточного программирования и без того. остальные будут напоминать обезьяну с гранатой.


Никто и не утверждал обратного.
Re[11]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 07.07.14 00:25
Оценка:
BFE>Да, сначала так и пытались, но оказалось, что это довольно сложный патерн в плане добавления в него новых событий: один loop должен знать о всех событиях системы, что оказалось очень неудобным, так как у нас несколько раз поменялись подключаемые устройства и их характеристики.

Почему-то это не остановило разработчиков kqueue/epoll. Может "характеристики подключаемых устройств" ничуть не хуже впишутся в стандартную модель open/read/write/close/ioctl?

BFE>Программа должна работать в разных конфигурациях оборудования. Оказалось проще под каждое устройство написать свой программный модуль со своими циклами обработки данных, чем иметь один динамический изменяемый event loop.


Возможно, я невнимательно читал весь тред, но — вы не могли бы еще раз написать, используется ли в вашем продуте какая-то ОС? Или всё полностью самописное, включая потоки?
Re[35]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 07.07.14 06:36
Оценка:
Здравствуйте, Lazin, Вы писали:

L>>Есть — планирование и геноцид, дискриминация, ковровые бомбардировки и обычновенный фашизм во время code review.


L>Я не могу вынести это в библиотеку


Было бы неплохо... но не в этой вселенной, к сожалению

L>>Ну то есть использовать эту либу правильно все равно смогут только те, кто прекрасно понимает все тонкости многопоточного программирования и без того. остальные будут напоминать обезьяну с гранатой.


L>Никто и не утверждал обратного.


Ну, лично для меня полезность изобретения в данном случае не очень понятна.
Хотя, если на ее основе сделать библиотеку для юнит-тестирования, которая подменяет примитивы синхронизации и выявляет все места, в которых порядок синхронизации нарушен, то можно было бы получить неплохую тулзу для возюканья нерадивых программеров мордой по асфальту.
Правда, опять же есть риск эпидемии простреленных конечностей, вызванных тем самым "нам над архитектурой думать не надо, у нас есть супер-пупер-либа"
www.blinnov.com
Re[34]: Deadlock-prevention алгоритм
От: Erop Россия  
Дата: 07.07.14 09:09
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Есть — планирование и геноцид, дискриминация, ковровые бомбардировки и обычновенный фашизм во время code review.

Это не даёт никаких гарантий, к сожалению.
Кроме того есть ещё такой аспект, как стоимость разработки против стоимости эксплуатации...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[35]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 07.07.14 09:15
Оценка:
Здравствуйте, Erop, Вы писали:


L>>Есть — планирование и геноцид, дискриминация, ковровые бомбардировки и обычновенный фашизм во время code review.

E>Это не даёт никаких гарантий, к сожалению.

А полные гарантии в этом мире вообще только в одном месте возможны.

E>Кроме того есть ещё такой аспект, как стоимость разработки против стоимости эксплуатации...


Естетственно. А еще есть стоимость поддержки и дальнейшего развития. А также есть потерянные для сбыта направления.
www.blinnov.com
Re[32]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 07.07.14 11:08
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Точнее: вместо раскидывания по всей системе, локализуем низкоуровневый многопоточный код в модуле, который легко протестировать/верифицировать.

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

EP>Если такие значения возникли не из-за ошибки программиста, а например из-за ошибочного ввода (но всё же ожидаемого) — то нет, не баг.

А разве не является первейшей задачей программиста проверить вводимые значения на валидность?

EP>В то же время, например, можно элементарно придумать ситуацию, в которой плохие входные данные приведут к выходу за границы массива.

С таким подходом я согласиться никак не могу.
И каждый день — без права на ошибку...
Re[12]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 07.07.14 11:17
Оценка:
Здравствуйте, SkyDance, Вы писали:

BFE>>Да, сначала так и пытались, но оказалось, что это довольно сложный патерн в плане добавления в него новых событий: один loop должен знать о всех событиях системы, что оказалось очень неудобным, так как у нас несколько раз поменялись подключаемые устройства и их характеристики.

SD>Почему-то это не остановило разработчиков kqueue/epoll. Может "характеристики подключаемых устройств" ничуть не хуже впишутся в стандартную модель open/read/write/close/ioctl?
Это задачи разного масштаба и требований.

BFE>>Программа должна работать в разных конфигурациях оборудования. Оказалось проще под каждое устройство написать свой программный модуль со своими циклами обработки данных, чем иметь один динамический изменяемый event loop.

SD>Возможно, я невнимательно читал весь тред, но — вы не могли бы еще раз написать, используется ли в вашем продуте какая-то ОС? Или всё полностью самописное, включая потоки?
Debian.
И каждый день — без права на ошибку...
Re[32]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 07.07.14 11:42
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

BFE>>Т.е. вы считаете, что можно избежать всех deadlock'ов во всех возможных системах и исправить такие ошибки?

EP>Нет, я так не считаю.
EP>Выброс исключения прям перед deadlock'ом, в который неизвестно как попали — не гарантирует выход в какое-то стабильное состояние, более того — может привести к ещё большим потерям.
Вот этого я не понимаю. Давайте рассмотрим систему, где нет deadlock'ов. В такой систем, очевидно, может встретится ситуация в которой перед очередным захватом мютекса выбрасывается эксепшен, например, просто потому, что какая-нибудь функция вызванная прямо перед захватом мютекса выбросила исключение. Это состояние, в которое неизвестно как попали, не гарантирует выход в какое-то стабильное состояние? Более того — может привести к ещё большим потерям? Именно это вы утверждаете?

EP>Если нужен нормальный fault tolerance, то всё-равно придётся планировать аварийное завершение.

Согласен. Придётся.

EP>>>Так мы же и так рассматриваем ситуацию, которой в корректной программе быть не должно. И соответственно раз произошёл deadlock, "то и не такое может произойти"

BFE>>Качественный код должен корректно обрабатывать ошибочные ситуации, даже если этой ситуации "в корректной программе быть не должно".
EP>Не вижу оснований для данного утверждения.
EP>Например, у алгоритмов STL есть определённые preconditions, при нарушении которых всё может пойти в разнос, вплоть до расстрела памяти или зависания — тем не менее, некачественным кодом он от этого не становится.
Согласен. Но речь не об этом. Программист должен обеспечить preconditions. Но если STL функция может бросить исключение, то обработка такой возможности должна быть предусмотрена.

EP>Опять таки, если по характеру задачи нужен fault tolerance, то нужно принимать соответствующие меры, например watchdog, дублирование, вплоть до использования разных алгоритмов и т.д и т.п.

EP>Но прежде всего, имхо, нужно иметь корректный, оттестированный и верифицированный код — от этого никуда не деться в любом случае, никакие свистульки тут не помогут.
Согласен. И тестирование, например, должно включать в себя работу с некорректными данными.

BFE>>Не бывает сложных систем без ошибок.

EP>Выброс исключения из совершенно неизвестного состояния, не только не решит это ошибку, но ещё и может всё усугубить.
Тем не менее это обычное дело: верхний уровень поймавший исключение очень мало знает о том, что "там в низу" случилось.

EP>При незапланированном deadlock'е, как неоднократно замечали ранее, находишься в неизвестном состоянии. Если кинуть исключение, то есть некоторый шанс вернуться в известное и стабильное состояние. Если же прибить процесс и начать заново, то шанс возврата в стабильное состояние намного больше, да и к тому же для fault tolerance это всё-равно понадобиться.

Расскажите мне, почему шанс возврата в стабильное состояние намного больше? Я не вижу причин для этого. При поимке исключения у нас есть хоть какая-то информация, когда же убивается процесс, то даже такой информации нет.
И каждый день — без права на ошибку...
Re[33]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 07.07.14 13:02
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>Т.е. вы считаете, что можно избежать всех deadlock'ов во всех возможных системах и исправить такие ошибки?

EP>>Нет, я так не считаю.
EP>>Выброс исключения прям перед deadlock'ом, в который неизвестно как попали — не гарантирует выход в какое-то стабильное состояние, более того — может привести к ещё большим потерям.
BFE>Вот этого я не понимаю. Давайте рассмотрим систему, где нет deadlock'ов. В такой систем, очевидно, может встретится ситуация в которой перед очередным захватом мютекса выбрасывается эксепшен, например, просто потому, что какая-нибудь функция вызванная прямо перед захватом мютекса выбросила исключение. Это состояние, в которое неизвестно как попали, не гарантирует выход в какое-то стабильное состояние? Более того — может привести к ещё большим потерям? Именно это вы утверждаете?

Нет, абсолютно точно не это.
Если эта функция по контракту может кидать исключение на определённое входное состояние X, и при наступлении этого самого состояния кидает исключение — то мы попадаем в конкретное состояние Y, из которого есть выход.
Если же, например, по логике системы передача состояния X в эту функцию никак не могла случится (является багом), но каким-то образом всё же произошло — то мы уже находимся непонятно где, и попали сюда непонятно как, без всяких гарантий на выход в стабильное состояние.

BFE>>>Качественный код должен корректно обрабатывать ошибочные ситуации, даже если этой ситуации "в корректной программе быть не должно".

EP>>Не вижу оснований для данного утверждения.
EP>>Например, у алгоритмов STL есть определённые preconditions, при нарушении которых всё может пойти в разнос, вплоть до расстрела памяти или зависания — тем не менее, некачественным кодом он от этого не становится.
BFE>Согласен. Но речь не об этом. Программист должен обеспечить preconditions.

Верно.

BFE>Но если STL функция может бросить исключение, то обработка такой возможности должна быть предусмотрена.


Если STL функция может обнаружить нарушение preconditions, то лучше всего это assertion, и именно так поступают многие реализации.
А те состояния, которые приводят к исключениям, входят в preconditions. И, соответственно, выброс исключения — это вполне легальный postcondition.

EP>>Опять таки, если по характеру задачи нужен fault tolerance, то нужно принимать соответствующие меры, например watchdog, дублирование, вплоть до использования разных алгоритмов и т.д и т.п.

EP>>Но прежде всего, имхо, нужно иметь корректный, оттестированный и верифицированный код — от этого никуда не деться в любом случае, никакие свистульки тут не помогут.
BFE>Согласен. И тестирование, например, должно включать в себя работу с некорректными данными.

Может в некоторых случаях, но не обязано в общем.

BFE>>>Не бывает сложных систем без ошибок.

EP>>Выброс исключения из совершенно неизвестного состояния, не только не решит это ошибку, но ещё и может всё усугубить.
BFE>Тем не менее это обычное дело: верхний уровень поймавший исключение очень мало знает о том, что "там в низу" случилось.

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

EP>>При незапланированном deadlock'е, как неоднократно замечали ранее, находишься в неизвестном состоянии. Если кинуть исключение, то есть некоторый шанс вернуться в известное и стабильное состояние. Если же прибить процесс и начать заново, то шанс возврата в стабильное состояние намного больше, да и к тому же для fault tolerance это всё-равно понадобиться.

BFE>Расскажите мне, почему шанс возврата в стабильное состояние намного больше? Я не вижу причин для этого.

Шанс успешного прибития процесса (находящегося в неизвестном состоянии) намного выше шанса того, что исключение (вылетевшее неизвестно откуда и как) доберётся до обработчика и ничего не сломает по пути.

BFE>При поимке исключения у нас есть хоть какая-то информация, когда же убивается процесс, то даже такой информации нет.


По поводу наличия информации ситуация практически одинаковая — и при выбросе исключения и при terminate/kill можно собрать всё необходимое.
Re[4]: Deadlock-prevention алгоритм
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 07.07.14 15:23
Оценка:
Здравствуйте, sokel, Вы писали:

S>Здравствуйте, Lazin, Вы писали:


S>Ещё вот такая штука — не лучше ли при расчёте хэшей завязываться на sizeof? Т.е. сейчас хэш по адресу считается с шагом адреса в 64 байта. Допустим у нас размер наиболее часто блокируемых объектов 128 байт, причем аллокируются объекты на страницах с выравниванием не меньше чем 128 байт. Т.о. из всего набора мьютексов для них будет использоваться только половина. При размере 256 байт будет использоваться только четверть мьютексов, ну и так далее.


S>Такой хэш будет давать лучшее распределение блокировок:

S>
S>        //! Simple hash - simply returns it's argument
S>        struct SimpleHash {
S>            template<typename T>
S>            size_t operator() (const T* value) const {
S>                return reinterpret_cast<size_t>(value)/sizeof(T);
S>            }
S>        };
S>


Мне не нравится здесь деление. Если размер объекта меньше 64х байт — то объекты, расположенные внутри одной кэш линии будут хэшироваться на разные мьютексы, что имеет мало смысла. К тому же, деление — довольно медленная операция и не факт, что компилятор сможет оптимизировать его для всех размеров. Возможно, имеет смысл считать какой-нибудь более сложный хэш от указателя, а не просто накладывать маску.
Re[35]: Deadlock-prevention алгоритм
От: landerhigh Пират  
Дата: 08.07.14 06:21
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Т.е. вы пишите программы, которые не бросают исключений?


Дедлок — не исключение.
www.blinnov.com
Re[14]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 08.07.14 11:12
Оценка:
Здравствуйте, SkyDance, Вы писали:

BFE>>Debian.

SD>Т.е. у вас УЖЕ БЫЛ механизм epoll, и всё, что вам требовалось — реализовать драйвер мотора/экрана? Чтобы иметь /dev/engine1, /dev/screen1. И дальше пользовать системный epoll.
Это сейчас Debian, а подключили меня к проекту, когда использовали какую-то старую версию Linux собранную весьма специфическим образом.
И я подозреваю, что при использовании epoll придётся часть работы перекладывать на драйвера, которые придётся писать под каждое из устройств... и это при том, что можно обойтись стандартными средствами.
Кстати, если использовать epoll, то как там с timer событиями? Если запустить два timerfd_settime на 10 ms и на 17 ms, то они нормально работают? Интерференции не будет?
Ну и потом, сама идея, обрабатывать все события в одном цикле мне кажется неверным подходом: никакой модульности, всё монолитное
И каждый день — без права на ошибку...
Re[15]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 08.07.14 23:50
Оценка:
BFE>Это сейчас Debian, а подключили меня к проекту, когда использовали какую-то старую версию Linux собранную весьма специфическим образом.

epoll в ядре Linux появился где-то в 2003 году. Неужто настолько старую версию у вас использовали?

BFE>И я подозреваю, что при использовании epoll придётся часть работы перекладывать на драйвера, которые придётся писать под каждое из устройств... и это при том, что можно обойтись стандартными средствами.


Не распарсил. Что за "стандартные средства" и драйвера? Сейчас вы как со своими моторами общаетесь? Случаем, не через файловые дескрипторы?

BFE>Кстати, если использовать epoll, то как там с timer событиями? Если запустить два timerfd_settime на 10 ms и на 17 ms, то они нормально работают?


Отлично они работают. Хоть сто таймеров запускай. Современные ядра вообще чудесны и почти realtime — уж всяко лучше home made изделий. Всего-то нужно mlockall(MCL_CURRENT | MCL_FUTURE) для памяти да sched_setscheduler(0, SCHED_FIFO, ...) с нужным уровнем приоритета. Джиттер в пределах 2-3 микросекунд при умеренной загрузке процессора. Если принять еще некоторые меры, у меня получалось добиться 1 мкс. (но без гарантий).

BFE>Ну и потом, сама идея, обрабатывать все события в одном цикле мне кажется неверным подходом: никакой модульности, всё монолитное


Давайте конкретнее. Что значит "никакой модульности"? Кто мешает сделать модуль "мотор типа ZZZ"? Который из зависимостей будет тянуть только интерфейс вашей обёртки над epoll. Что тут "монолитного"? А ваш существующий код, думаете, менее монолитен? Тогда откуда дедлоки?
Re[33]: Deadlock-prevention алгоритм
От: Erop Россия  
Дата: 11.07.14 05:47
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>А разве не является первейшей задачей программиста проверить вводимые значения на валидность?

Это не всегда просто и не всегда быстро.
Скажем обрабатываем мы какой-то очень большой файл в несколько потоков, на какой-то момент концы не сошлись с концами. Что делаем?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[16]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 11.07.14 09:26
Оценка:
Здравствуйте, SkyDance, Вы писали:

BFE>>Это сейчас Debian, а подключили меня к проекту, когда использовали какую-то старую версию Linux собранную весьма специфическим образом.

SD>epoll в ядре Linux появился где-то в 2003 году. Неужто настолько старую версию у вас использовали?

Была какая-то странная сборка. Может там и был epoll, но вот даже стандартных средств доступа к последовательным портам не было.

BFE>>И я подозреваю, что при использовании epoll придётся часть работы перекладывать на драйвера, которые придётся писать под каждое из устройств... и это при том, что можно обойтись стандартными средствами.

SD>Не распарсил. Что за "стандартные средства" и драйвера? Сейчас вы как со своими моторами общаетесь? Случаем, не через файловые дескрипторы?
Да, как обычно: open, cfsetospeed, poll, write...

BFE>>Ну и потом, сама идея, обрабатывать все события в одном цикле мне кажется неверным подходом: никакой модульности, всё монолитное

SD>Давайте конкретнее. Что значит "никакой модульности"?

Ну, например. Для работы с сетью мы используем стороннюю библиотеку с callback'ами и функцией ожидания. Как предлагаете подключить её к epoll?

SD>Кто мешает сделать модуль "мотор типа ZZZ"? Который из зависимостей будет тянуть только интерфейс вашей обёртки над epoll. Что тут "монолитного"?

Эээ... Может я чего-то не понимаю? Когда вы говорите об epoll, то сколько ниток исполнения вы предполагаете?

SD>А ваш существующий код, думаете, менее монолитен?

Да. У нас 15 независимых библиотек.

SD> Тогда откуда дедлоки?

Но у нас нет deadlock'ов.
И каждый день — без права на ошибку...
Re[34]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 11.07.14 10:07
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>>Точнее: вместо раскидывания по всей системе, локализуем низкоуровневый многопоточный код в модуле, который легко протестировать/верифицировать.

BFE>>Далеко не всегда можно локализовать многопоточный код в отдельном модуле. Скорее наоборот, обычной является ситуация, когда взаимодействие отдельных модулей является синхронизацией потоков.
BFE>>И почему, кстати, взаимодействие потоков вы называете низкоуровневым кодом?
EP>Не взаимодействие потоков, а использование низкоуровневых примитивов типа mutex, condition_variable, atomic, etc.

ok. Допустим. Расшифруйте подробнее, что значит "локализуем низкоуровневый многопоточный код в модуле"? Запуск всех ниток программы должен происходить в одном модуле?

EP>Проверил на значение близкое к нулю и выбросил исключение, об этом и речь выше. (иногда дешевле делать проверки в ходе вычислений, а не сразу при получении данных, но это другой вопрос)

ok. А почему взаимодействия потоков не попадают под эту концепцию?

EP>>>В то же время, например, можно элементарно придумать ситуацию, в которой плохие входные данные приведут к выходу за границы массива.

BFE>>С таким подходом я согласиться никак не могу.
EP>С каким подходом?
Когда входные данные позволяют обратится по адресам за пределами массива.

EP>Вполне жизненный пример выхода за границы массива из-за плохих данных: в начале файла идут значения некоторых узлов, а в конце ссылки на них в виде индексов, неправильный индекс может выйти за границы массива.

Если нет проверки в момент обращения по индексу, то вас ожидают проблемы поддержки.

EP>Теперь можно пример хитрого алгоритма, который дедлочится на плохих данных?

Нет в этом ничего хитрого. Из практики. Программа занимается корректированием пары изображений. Пользователь задаёт списки файлов на обработку. Если пользователь в одной обработке задаёт пару file1.tif file2.tif, а в другой file2.tif file1.tif, то иногда два потока блокируют друг друга: каждый из потоков открывает первый список из файла, а второй открыть не может, так как он открыт другим потоком. Дальше всё зависит от обработки такой ситуации, если первый файл остаётся открытым...
И каждый день — без права на ошибку...
Re[34]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 11.07.14 10:20
Оценка:
Здравствуйте, Erop, Вы писали:

BFE>>А разве не является первейшей задачей программиста проверить вводимые значения на валидность?

E>Это не всегда просто и не всегда быстро.
E>Скажем обрабатываем мы какой-то очень большой файл в несколько потоков, на какой-то момент концы не сошлись с концами. Что делаем?

Вариантов много, но вот надёжный путь к краху — игнорировать.
Если заранее нельзя проверить целостность данных, то необходимо такую проверку делать в момент обработки, что чревато проблемами, если в ходе обработки программа меняет другие данные.
Ну и потом, обработка ошибочных ситуаций — это существенный процент кода. Сколько — не считал, но в строках, думаю, процентов 30 будет.
И каждый день — без права на ошибку...
Re[35]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 11.07.14 11:12
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>И почему, кстати, взаимодействие потоков вы называете низкоуровневым кодом?

EP>>Не взаимодействие потоков, а использование низкоуровневых примитивов типа mutex, condition_variable, atomic, etc.
BFE>ok. Допустим. Расшифруйте подробнее, что значит "локализуем низкоуровневый многопоточный код в модуле"? Запуск всех ниток программы должен происходить в одном модуле?

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

EP>>Проверил на значение близкое к нулю и выбросил исключение, об этом и речь выше. (иногда дешевле делать проверки в ходе вычислений, а не сразу при получении данных, но это другой вопрос)

BFE>ok. А почему взаимодействия потоков не попадают под эту концепцию?

Я же говорю, что могут — если мы действительно видим, что на определённых данных может задедлочиться, и намеренно кидаем исключение. А не так, что "тут не должно быть deadlock'ов, но всё-таки кинем исключение".

EP>>>>В то же время, например, можно элементарно придумать ситуацию, в которой плохие входные данные приведут к выходу за границы массива.

BFE>>>С таким подходом я согласиться никак не могу.
EP>>С каким подходом?
BFE>Когда входные данные позволяют обратится по адресам за пределами массива.
EP>>Вполне жизненный пример выхода за границы массива из-за плохих данных: в начале файла идут значения некоторых узлов, а в конце ссылки на них в виде индексов, неправильный индекс может выйти за границы массива.
BFE>Если нет проверки в момент обращения по индексу, то вас ожидают проблемы поддержки.

Так я же говорю, что для таких случаев vector::at вполне оправдан, так как в этом случае выход за пределы — вполне ожидаемый вариант.

EP>>Теперь можно пример хитрого алгоритма, который дедлочится на плохих данных?

BFE>Нет в этом ничего хитрого. Из практики. Программа занимается корректированием пары изображений. Пользователь задаёт списки файлов на обработку. Если пользователь в одной обработке задаёт пару file1.tif file2.tif, а в другой file2.tif file1.tif, то иногда два потока блокируют друг друга: каждый из потоков открывает первый список из файла, а второй открыть не может, так как он открыт другим потоком. Дальше всё зависит от обработки такой ситуации, если первый файл остаётся открытым...

Обработку открытия уже открытого файла нужно предусматривать даже если у нас только один файл.
Пример более-менее валидный, но всё же — тут скорее не "хитрость", а недоработка. Интересует deadlock, который оставили преднамеренно, ради каких-то выгод.
Re[34]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 11.07.14 11:23
Оценка:
Здравствуйте, Erop, Вы писали:

E>Мало того, когда мы кидаем и ловим исключение, мы можем дать таки какие-то гарантии на целостность данных.


Так да, но где гарантия что что мы его поймаем? Код уже скомпрометирован, в нём баг приведший в неизвестное состояние.
Ты летишь на самолёте, автопилот сильно и очень резко отклонился от заданного курса, решаешь сесть на ближайший аэродром — будешь сам рулить, или попросишь автопилот?

E>А если грубо убиваем процесс, то вообще никаких гарантий дать не можем. Даже того, что внутри-программные кэши все сбросились на диск


Если этот deadlock детектится изнутри, то в нашем assert'е можно точно также сбросить все критичные кэши.
Re[35]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 11.07.14 11:25
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Речь-то как раз о том, что исключение вылетевшее из неизвестного состояния далеко не факт что вообще сможет дойти до обработчика на верхнем уровне.

EP>>Шанс успешного прибития процесса (находящегося в неизвестном состоянии) намного выше шанса того, что исключение (вылетевшее неизвестно откуда и как) доберётся до обработчика и ничего не сломает по пути.
E>Это очень зависит от стратегии использования исключений и обработки ошибок в программе...

Покажи пример, где процесс убить не получилось, а исключение может успешно долететь до обработчика.
Re[35]: Deadlock-prevention алгоритм
От: Erop Россия  
Дата: 13.07.14 19:30
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

E>>А если грубо убиваем процесс, то вообще никаких гарантий дать не можем. Даже того, что внутри-программные кэши все сбросились на диск


EP>Если этот deadlock детектится изнутри, то в нашем assert'е можно точно также сбросить все критичные кэши.


В своём потоке -- да...

Вообще не понятно, чем тут дедлок специфичен.
Приход в дедлок -- это провал assert'а. А уж как в вашей программе принято обрабатывать провал assert'а, -- вопрос отдельный.
Есть вполне удачные программы, в которых провал assert'а приводит к броску исключения...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[36]: Deadlock-prevention алгоритм
От: Erop Россия  
Дата: 13.07.14 19:34
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Покажи пример, где процесс убить не получилось, а исключение может успешно долететь до обработчика.

Ну, например, если ты даёшь гарантии готовности к исключениям, и ловишь исключения только на самом верхнем уровне, то всё получится...

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

Но это не значит, что подход с раии и исключениями неправильный...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[36]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 13.07.14 23:45
Оценка:
Здравствуйте, Erop, Вы писали:

E>>>А если грубо убиваем процесс, то вообще никаких гарантий дать не можем. Даже того, что внутри-программные кэши все сбросились на диск

EP>>Если этот deadlock детектится изнутри, то в нашем assert'е можно точно также сбросить все критичные кэши.
E>В своём потоке -- да...

Так этот вариант и обсуждается — исключение ведь тоже сам поток и выбрасывает.

E>Вообще не понятно, чем тут дедлок специфичен.

E>Приход в дедлок -- это провал assert'а. А уж как в вашей программе принято обрабатывать провал assert'а, -- вопрос отдельный.

Это уже перевод дискуссии в терминологическое поле, в то время как речь изначально шла про аварийное завершение (как его не назови) vs выброс исключения.

E>Есть вполне удачные программы, в которых провал assert'а приводит к броску исключения...


Да, например
Re[37]: Deadlock-prevention алгоритм
От: Evgeny.Panasyuk Россия  
Дата: 14.07.14 00:12
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Покажи пример, где процесс убить не получилось, а исключение может успешно долететь до обработчика.

E>Ну, например, если ты даёшь гарантии готовности к исключениям, и ловишь исключения только на самом верхнем уровне, то всё получится...

Во-первых, чем же это надёжней убийства процесса?
Во-вторых, это не даёт никаких гарантий что исключение долетит до обработчика. Например, во время раскрутки стэка нетривиальный деструктор (а в более-менее большом приложении трудно дать гарантии того, что все деструкторы тривиальны) может залезть в live-lock, или другой поток (участвовавший в этом самом межпоточном взаимодействии, приведшем к deadlock'у) может начать расстреливать всё подряд — код скомпрометирован, мы в неизвестном состоянии, может произойти всё что технически возможно.

E>При этом я согласен, что если вообще забить на раии и готовность к исключениям, а критичные ресурсы и кэши регать в статическом регистраторе, и в случае чего сбрасывать и освобождать централизовано, то будет более надёжная конструкция.

E>Но это не значит, что подход с раии и исключениями неправильный...

Опять таки, я не говорил про то, что нужно забить на RAII и исключения.
Мой поинт в том, что если в программе откровенный баг (коим незапланированный deadlock и является), приведший в неизвестное состояние — то "всё пропало", и при его обнаружении runtime нужно склеить ласты ASAP. В тех же редких случаях, когда нужна некоторая отказоустойчивость, проброс исключения через скомпрометированный код — далеко не самый надёжный вариант.
При этом отказ от использования исключений для выхода из неизвестного состояния — не означает отказ от исключений вообще.
Re[17]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 14.07.14 00:48
Оценка:
BFE>Была какая-то странная сборка. Может там и был epoll, но вот даже стандартных средств доступа к последовательным портам не было.
BFE>Да, как обычно: open, cfsetospeed, poll, write...

В двух рядом стоящих предложениях — противоречие. Раз вы открываете fd, и делаете poll — значит, нет никаких противопоказаний к epoll. Более чем уверен, банальным незнанием вы сами создали себе проблемы. Впрочем, это для джуниоров нормально. Шишки надо же где-то набивать.

BFE>Ну, например. Для работы с сетью мы используем стороннюю библиотеку с callback'ами и функцией ожидания. Как предлагаете подключить её к epoll?


Что за "сторонняя библиотека", и зачем вы ее используете? Опять же, "функция ожидания" сторонней библиотеки — это что-то из epoll/poll/select (другие науке малоизвестны).

BFE>Эээ... Может я чего-то не понимаю? Когда вы говорите об epoll, то сколько ниток исполнения вы предполагаете?


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

BFE>Да. У нас 15 независимых библиотек.


Да хоть 25. Количество библиотек не является показателем монолитности проекта. Более того, реализация паттерна "реактор" (тот самый epoll) явно будет сидеть где-то в библиотеке base/common/utils или как у вас там называется общая библиотека примитивов. Наличие паттерна "рекатор" не означает монолитность проекта.
Re[18]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 15.07.14 10:08
Оценка:
Здравствуйте, SkyDance, Вы писали:

BFE>>Была какая-то странная сборка. Может там и был epoll, но вот даже стандартных средств доступа к последовательным портам не было.

BFE>>Да, как обычно: open, cfsetospeed, poll, write...
SD>В двух рядом стоящих предложениях — противоречие. Раз вы открываете fd, и делаете poll — значит, нет никаких противопоказаний к epoll. Более чем уверен, банальным незнанием вы сами создали себе проблемы. Впрочем, это для джуниоров нормально. Шишки надо же где-то набивать.
Здесь нет противоречия. Разница между тем, что было, и тем, что сейчас — несколько лет.

BFE>>Ну, например. Для работы с сетью мы используем стороннюю библиотеку с callback'ами и функцией ожидания. Как предлагаете подключить её к epoll?

SD>Что за "сторонняя библиотека", и зачем вы ее используете?
Да какая разница? Библиотеки мы используем ровно для того, для чего их пишут.

SD>Опять же, "функция ожидания" сторонней библиотеки — это что-то из epoll/poll/select (другие науке малоизвестны).

Разумеется. И что это меняет? Вы предлагаете переписывать сторонюю библиотеку под свои нужды?

BFE>>Эээ... Может я чего-то не понимаю? Когда вы говорите об epoll, то сколько ниток исполнения вы предполагаете?

SD>Столько, сколько необходимо для обеспечения требуемой latency. Если, к примеру, ваш код ответа на какие-то входные данные работает 5 мс, и на такое долгое время нельзя выключать какие-то еще обработчики, потребуется запустить более одного потока.
Ок. А теперь вернёмся к теме топика: как обеспечить передачу данных между этими нитками?

BFE>>Да. У нас 15 независимых библиотек.

SD>Да хоть 25. Количество библиотек не является показателем монолитности проекта.
А что является?
И каждый день — без права на ошибку...
Re[19]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 15.07.14 23:18
Оценка:
BFE>Здесь нет противоречия. Разница между тем, что было, и тем, что сейчас — несколько лет.

Окей, несколько — это сколько? 12 лет? Впрочем, даже 12 лет назад уже вовсю был select — и, раз уж вы применяли poll, reactor pattern был более чем применим.

BFE>Да какая разница? Библиотеки мы используем ровно для того, для чего их пишут.


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

BFE>Разумеется. И что это меняет? Вы предлагаете переписывать сторонюю библиотеку под свои нужды?


Вот чтобы понять, что предложить, мне и нужно знать конкретику — что за библиотека-то?

BFE>Ок. А теперь вернёмся к теме топика: как обеспечить передачу данных между этими нитками?


Поскольку все нитки сидят в цикле реактора, передача данных будет организована путём пересылки сообщений. Благо, необходимость в синхронизации отсутствует.

SD>>Да хоть 25. Количество библиотек не является показателем монолитности проекта.

BFE>А что является?

А это надо у вас спросить. Вы же сетуете, мол, "применение паттерна reactor сделает проект монолитным". В традиционном понимании "монолитный" проект — это такой, из которого нельзя вынуть модуль и заменить на его апгрейднутую версию. Иными словами, монолит — это нечто с сильными связями и завязкой на внутреннюю логику работы.
Re[20]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 16.07.14 10:31
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Окей, несколько — это сколько? 12 лет? Впрочем, даже 12 лет назад уже вовсю был select — и, раз уж вы применяли poll, reactor pattern был более чем применим.

Изначально poll не применялся.

BFE>>Да какая разница? Библиотеки мы используем ровно для того, для чего их пишут.

SD>Да такая разница. Название в студию, благо, сетевых библиотек не так и много, и все из них достаточно гибкие, чтобы их можно было встроить в нужный софт без костылей типа десятка потоков.
Я не уверен в том, что имею право разглашать названия используемых библиотек.

BFE>>Ок. А теперь вернёмся к теме топика: как обеспечить передачу данных между этими нитками?

SD>Поскольку все нитки сидят в цикле реактора, передача данных будет организована путём пересылки сообщений. Благо, необходимость в синхронизации отсутствует.
ok. И сколько таких очередей будет?

SD>А это надо у вас спросить. Вы же сетуете, мол, "применение паттерна reactor сделает проект монолитным". В традиционном понимании "монолитный" проект — это такой, из которого нельзя вынуть модуль и заменить на его апгрейднутую версию. Иными словами, монолит — это нечто с сильными связями и завязкой на внутреннюю логику работы.


В любом проекте можно переписать его существенный кусок. Модульным проект можно назвать только если его компоненты используются в нескольких проектах.
И каждый день — без права на ошибку...
Re[21]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 17.07.14 00:00
Оценка:
BFE>Изначально poll не применялся.

Все равно что-то применялось для ожидания. В самом деле, не active wait же.

BFE>Я не уверен в том, что имею право разглашать названия используемых библиотек.


Даже не знаю, плакать или смеяться. Нельзя разглашать название библиотеки, использованной в неизвестном продукте неизвестных поставщиков. Не иначе как редкая хакерская либа libssl...

BFE>ok. И сколько таких очередей будет?


В точности равное количеству уровней приоритета в вашей системе. Чтобы запросы от более приоритетных устройств могли вытеснять обработку менее приоритетных задач.
Если, конечно, у вас вообще есть приоритеты. У запускаемых вашим нынешним софтом потоков приоритеты у всех одинаковые, или различаются? Если различаются, то почему?

BFE>В любом проекте можно переписать его существенный кусок. Модульным проект можно назвать только если его компоненты используются в нескольких проектах.


И? Продолжай мысль, объясняй, почему применение reactor pattern сделает проект "монолитным". И заодно ответь — ведь есть же у вас некая базовая библиотека (utils, base, common, ...).
Re[22]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 17.07.14 09:36
Оценка:
Здравствуйте, SkyDance, Вы писали:

BFE>>Изначально poll не применялся.

SD>Все равно что-то применялось для ожидания. В самом деле, не active wait же.
sleep(..) ну и блокирующие операции io, конечно же.

BFE>>Я не уверен в том, что имею право разглашать названия используемых библиотек.

SD>Даже не знаю, плакать или смеяться. Нельзя разглашать название библиотеки, использованной в неизвестном продукте неизвестных поставщиков. Не иначе как редкая хакерская либа libssl...
Узнать, кто я и где я работаю, как два байта переслать. Но не в этом дело.

BFE>>ok. И сколько таких очередей будет?

SD>В точности равное количеству уровней приоритета в вашей системе. Чтобы запросы от более приоритетных устройств могли вытеснять обработку менее приоритетных задач.
И все они будут проходить через основную нитку?
SD>Если, конечно, у вас вообще есть приоритеты. У запускаемых вашим нынешним софтом потоков приоритеты у всех одинаковые, или различаются? Если различаются, то почему?
Есть, но это не очень важно.

BFE>>В любом проекте можно переписать его существенный кусок. Модульным проект можно назвать только если его компоненты используются в нескольких проектах.

SD>И? Продолжай мысль, объясняй, почему применение reactor pattern сделает проект "монолитным".
Может я чего-то не знаю, но разве для использования библиотеки с reactor pattern, библиотека не должна представлять специальный интерфейс для такой работы? Ну, например, легко ли Boost.Asio использовать с этим патерном?

SD> И заодно ответь — ведь есть же у вас некая базовая библиотека (utils, base, common, ...).

нет такой. В utils'ах у нас только логи и всякая мелочь, типа boost.enum.
И каждый день — без права на ошибку...
Re[23]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 18.07.14 00:05
Оценка:
BFE>Узнать, кто я и где я работаю, как два байта переслать. Но не в этом дело.

Никто даже и пытаться не будет. Тем более что выяснить, какие библиотеки используются в продукте, особого труда не составит.
А дело, по-видимому, лишь в том, что библиотека уже использует что-то reactor-based

BFE>И все они будут проходить через основную нитку?


Вы вообще понимаете, как работает epoll и аналоги? Ниток (и дескрипторов epoll) будет столько, сколько у вас разных приоритетов. Среди них нет "главной" или "основной". Просто, в соответствии с приоритетом потока, обработка более приоритетных вещей сможет прерывать что-то менее приоритетное. Если вам такие прерывания не нужны, тогда не нужны и потоки — все можно обрабатывать в одном.

BFE>Есть, но это не очень важно.


Это очень важно. При отсутствии приоритетных задач вам многозадачность (и многопоточность) вообще не нужны.

BFE>Может я чего-то не знаю, но разве для использования библиотеки с reactor pattern, библиотека не должна представлять специальный интерфейс для такой работы? Ну, например, легко ли Boost.Asio использовать с этим патерном?


Как бы так помягче... boost::asio реализует этот паттерн их io_service есть прямая реализация.
Re[24]: Deadlock-prevention алгоритм
От: B0FEE664  
Дата: 18.07.14 08:39
Оценка:
Здравствуйте, SkyDance, Вы писали:

BFE>>И все они будут проходить через основную нитку?

SD>Вы вообще понимаете, как работает epoll и аналоги?
Примерно.

SD>Ниток (и дескрипторов epoll) будет столько, сколько у вас разных приоритетов. Среди них нет "главной" или "основной". Просто, в соответствии с приоритетом потока, обработка более приоритетных вещей сможет прерывать что-то менее приоритетное. Если вам такие прерывания не нужны, тогда не нужны и потоки — все можно обрабатывать в одном.

All reactor systems are single threaded by definition, but can exist in a multithreaded environment.


Значит, если есть несколько ниток с разными приоритетами, значит это уже не reactor pattern.
Но вопрос не в этом. Допустим у вас несколько приоритетов => несколько ниток. Ранее вы писали, что хотите организовать передачу данных с помощью сообщений. Положим, для примера, что ниток 3 штуки и всем им нужна информация друг друга. Опишите способ обмена этой информацией.

BFE>>Есть, но это не очень важно.

SD>Это очень важно. При отсутствии приоритетных задач вам многозадачность (и многопоточность) вообще не нужны.
Ну хорошо. Приоритет работы с сетью не равен приоритету работы с устройствами.

BFE>>Может я чего-то не знаю, но разве для использования библиотеки с reactor pattern, библиотека не должна представлять специальный интерфейс для такой работы? Ну, например, легко ли Boost.Asio использовать с этим патерном?

SD>Как бы так помягче... boost::asio реализует этот паттерн их io_service есть прямая реализация.
Я знаю, что boost::asio реализует этот паттерн. Но вопрос-то не в этом!
Давайте, покажите как использовать boost::asio совместно со своим reactor pattern.
И каждый день — без права на ошибку...
Re[25]: Deadlock-prevention алгоритм
От: SkyDance Земля  
Дата: 21.07.14 04:27
Оценка:
BFE>All reactor systems are single threaded by definition, but can exist in a multithreaded environment.

Вот я про то и писал, что если вам нужно больше одного реактора — запускайте больше одного.

BFE>Значит, если есть несколько ниток с разными приоритетами, значит это уже не reactor pattern.


Не угадал: это значит, что у вас два отдельных процесса (или потока, это не суть важно), и в каждом из них крутится свой реактор. Сие нужно для того (и только для того) чтобы обеспечить приоритетную обработку.

BFE>Но вопрос не в этом. Допустим у вас несколько приоритетов => несколько ниток. Ранее вы писали, что хотите организовать передачу данных с помощью сообщений. Положим, для примера, что ниток 3 штуки и всем им нужна информация друг друга. Опишите способ обмена этой информацией.


Надо рассматривать конкретную систему и конкретную информацию. Что за данные разделяются между ветками. Обязательно ли обеспечивать их согласованность, или можно, к примеру, делать так, чтобы более приоритетные задачи выполнялись безусловно, а затем посылали update message в менее приоритетные. Короче, нужно продумывать архитектуру. С учётом имеющихся ограничений. А то, может статься, нет никаких проблем и вовсе одним реактором обойтись.

BFE>Давайте, покажите как использовать boost::asio совместно со своим reactor pattern.


Может, наоборот? Использовать буст-овский реактор со своими задачами. Это намного логичнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.