Мы обращаемся к либе в потоке 1. Она, в свою очередь, сама создает поток 2 и делит с ним данные. Когда наш код в потоке 1 дергает код либы, тот приостанавливает поток 2, делает что-то с данными, и вновь разрешает выполнение потоку 2. Типа синхронизация, дешево и сердито.
Проблема:
Иногда поток 2 выделяет память. Это происходит через функцию рантайма __lock(). Если поток приостанавливают в этот момент, то он так и остается в лочке. Затем приостановивший его тред сам пытается выделить память, попадает в ту же __lock() и всё виснет намертво.
Вопрос:
Как этого можно избежать? Доступ к коду либы есть. Но сделать либе нормальную синхронизацию не представляется возможным, там много обмена данными и еще куча слипов по коду. Поэтому нельзя просто выкинуть SuspendThread. Отказаться от выделения памяти в потоках тоже нельзя, т.к. скриптовый язык и там куча этих выделений.
Единственное, что я вижу как решение — SuspendThread используется всего в двух используемых нами функциях. То есть если бы был способ проверить, что __lock() встала на лочку, можно было бы попробовать обождать с саспендом, пока та не освободиться.
Есть ли такой способ?
Может есть более удачные решения?
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Basil2, Вы писали:
B>Мы обращаемся к либе в потоке 1. Она, в свою очередь, сама создает поток 2 и делит с ним данные. Когда наш код в потоке 1 дергает код либы, тот приостанавливает поток 2, делает что-то с данными, и вновь разрешает выполнение потоку 2. Типа синхронизация, дешево и сердито.
Вот тут уже "ахтунг". Такой подход не обеспечивает синхронизацию совсем. Suspend-ом вы останавливаете код в произвольной точке, а не четко на критической секции, следовательно, у вас гонка по данным, есть Suspend или нет. Все остальное это следствие отсутствия синхронизации, и lock это только частный случай.
Здравствуйте, Basil2, Вы писали:
B>Ситуация:
B>Есть наш код и сторонняя либа, оба С++.
B>Мы обращаемся к либе в потоке 1. Она, в свою очередь, сама создает поток 2 и делит с ним данные. Когда наш код в потоке 1 дергает код либы, тот приостанавливает поток 2, делает что-то с данными, и вновь разрешает выполнение потоку 2. Типа синхронизация, дешево и сердито.
Очень сердито
B>т.к. скриптовый язык
Какой?
B>Есть ли такой способ?
Вызывайте suspend внутри lock
B>Может есть более удачные решения?
навалом
Здравствуйте, Basil2, Вы писали:
B>Как этого можно избежать? Доступ к коду либы есть. Но сделать либе нормальную синхронизацию не представляется возможным, там много обмена данными и еще куча слипов по коду. Поэтому нельзя просто выкинуть SuspendThread. Отказаться от выделения памяти в потоках тоже нельзя, т.к. скриптовый язык и там куча этих выделений.
Здравствуйте, Basil2, Вы писали:
B>Может есть более удачные решения?
Если я правильно понял, что происходит, и если позволяет устройство библиотеки, то можно делать так:
останавливать поток 2 не через SuspendThread, а через внутреннюю проверку, выполняемую в потоке 2. Т.е. там время от времени проверяется состояние некой переменной, и если она установлена, то производится остановка. Первый поток должен после отдачи команды на остановку (установки переменной) проверять, остановлен ли поток 2 на самом деле.
Судя по использованию SyspendThread, ты ничего не знаешь про std::mutext, std::lock_guard и std::conditional_variable, а это нужно в этом случае знать.
std::mutex mutex;
// команда остановить потокbool stop_thread;
std::conditional_variable suspend;
// поток действительно остановленbool thread_is_stopped;
std::conditional_variable resume;
// поток 1.
{ // останавливаем поток 2
std::lock_guard lock(mutex);
stop_thread = true;
// ожидаем сообщения об остановке потока 2while(!thread_is_stopeed)
suspend.wait(lock);
}
// ...
// здесь поток 2 гарантированно остановлен
// делаем всё что нужно
// ...
{ // запускаем поток 2
std::lock_guard lock(mutex);
stop_thread = false;
resume.notify_one();
}
// поток 2.while(true) // какой-то цикл обработки сообщений
{
// проверяем нет ли команды на остановку
{
std::lock_gruard lock(mutex);
if (stop_thread)
{
// сообщаем, что поток остановлен
thread_is_stopped = true;
suspend.notify_one();
// ожидаем команды на запускwhile(stop_thread)
resume.wait(lock);
}
}
}
Здравствуйте, kov_serg, Вы писали:
_>Очень сердито
+100.
B>>т.к. скриптовый язык _>Какой?
AutoHotKey
B>>Есть ли такой способ? _>Вызывайте suspend внутри lock
В смысле с защитой в виде мьютекса? Но где их тогда расставить в потоке два — везде?
Если же вы про __lock(), это это функция ntdll.
B>>Может есть более удачные решения? _>навалом
Например?
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, Basil2, Вы писали:
B>>Как этого можно избежать? Доступ к коду либы есть. Но сделать либе нормальную синхронизацию не представляется возможным, там много обмена данными и еще куча слипов по коду. Поэтому нельзя просто выкинуть SuspendThread. Отказаться от выделения памяти в потоках тоже нельзя, т.к. скриптовый язык и там куча этих выделений.
Pzz>А что делает поток 2, когда он ничего не делает?
Посапывает Он крутится в самописном цикле обработки внутренних сообщений.
Да, это же мысль! Воткнуть туда точку синхронизации, чтобы приостанавливать поток только между сообщениями; обычной лочкой вместо саспенда. Спасибо!
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, RedApe, Вы писали:
RA>Если я правильно понял, что происходит, и если позволяет устройство библиотеки, то можно делать так: RA>останавливать поток 2 не через SuspendThread, а через внутреннюю проверку, выполняемую в потоке 2. Т.е. там время от времени проверяется состояние некой переменной, и если она установлена, то производится остановка.
От этого решения меня останавливало то, что неизвестно в каком месте кода проверять эту переменную. Если везде, где идет обмен данными между потоками, так это десятки мест! Я не хочу так капитально править в чужой библиотеке... Я так понимаю, сама идея с SuspendThread именно поэтому у авторов и возникла — типа одним махом всё сразу.
RA>Первый поток должен после отдачи команды на остановку (установки переменной) проверять, остановлен ли поток 2 на самом деле.
Почему кстати переменной? Мьютекса наверное? Или намекаете, что его не хватит и нужна будет условная переменная?
RA>Судя по использованию SyspendThread, ты ничего не знаешь про std::mutext, std::lock_guard и std::conditional_variable, а это нужно в этом случае знать.
Я?! Это авторы библиотеки не знают.
RA>
RA>// команда остановить поток
RA>bool stop_thread;
RA>
Здесь точно нужна переменная? Разве статуса лочки не достаточно?
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Basil2, Вы писали:
B>>>т.к. скриптовый язык _>>Какой? B>AutoHotKey
Этот https://github.com/Lexikos/AutoHotkey_L
B>>>Есть ли такой способ? _>>Вызывайте suspend внутри lock B>В смысле с защитой в виде мьютекса? Но где их тогда расставить в потоке два — везде? B>Если же вы про __lock(), это это функция ntdll.
Его же кто-то вызывает. Где stack tarace?
B>>>Может есть более удачные решения? _>>навалом B>Например?
Нужно больше деталей иначе будет слишком не в тему.
Здравствуйте, kov_serg, Вы писали:
B>>AutoHotKey _>Этот https://github.com/Lexikos/AutoHotkey_L
Нет, этот: https://github.com/HotKeyIt/ahkdll/
B>>>>Есть ли такой способ? _>>>Вызывайте suspend внутри lock B>>В смысле с защитой в виде мьютекса? Но где их тогда расставить в потоке два — везде? B>>Если же вы про __lock(), это это функция ntdll. _>Его же кто-то вызывает. Где stack tarace?
Его все вызывают Это же выделение памяти! (если вы про __lock())
Если вы про suspend, то он в двух местах вызывается, и это внушает надежду что это можно как-то обыграть.
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Basil2, Вы писали:
B>Да, это же мысль! Воткнуть туда точку синхронизации, чтобы приостанавливать поток только между сообщениями; обычной лочкой вместо саспенда. Спасибо!
Suspend, это вообще такая штука, которую я не представляю, как можно применить в мирных целях в своем собственном процессе.
Здравствуйте, Basil2, Вы писали:
B>Ситуация:
B>Есть наш код и сторонняя либа, оба С++.
B>Мы обращаемся к либе в потоке 1. Она, в свою очередь, сама создает поток 2 и делит с ним данные. Когда наш код в потоке 1 дергает код либы, тот приостанавливает поток 2, делает что-то с данными, и вновь разрешает выполнение потоку 2. Типа синхронизация, дешево и сердито.
B>Проблема:
B>Иногда поток 2 выделяет память. Это происходит через функцию рантайма __lock(). Если поток приостанавливают в этот момент, то он так и остается в лочке. Затем приостановивший его тред сам пытается выделить память, попадает в ту же __lock() и всё виснет намертво.
B>Вопрос:
B>Как этого можно избежать? Доступ к коду либы есть. Но сделать либе нормальную синхронизацию не представляется возможным, там много обмена данными и еще куча слипов по коду. Поэтому нельзя просто выкинуть SuspendThread. Отказаться от выделения памяти в потоках тоже нельзя, т.к. скриптовый язык и там куча этих выделений.
B>Единственное, что я вижу как решение — SuspendThread используется всего в двух используемых нами функциях. То есть если бы был способ проверить, что __lock() встала на лочку, можно было бы попробовать обождать с саспендом, пока та не освободиться.
B>Есть ли такой способ?
B>Может есть более удачные решения?
А нельзя перехватить SuspendThread и __lock проверять? Скажем, в __lock выставлять флаг, в suspend его проверять, если выставлен, то немного подождать и снова проверить. когда флаг сброшен, может вызывать suspend. Хотя тут наверное возможна гонка. Хм...
Здравствуйте, Basil2, Вы писали:
RA>>Первый поток должен после отдачи команды на остановку (установки переменной) проверять, остановлен ли поток 2 на самом деле.
B>Почему кстати переменной? Мьютекса наверное? Или намекаете, что его не хватит и нужна будет условная переменная? B>Здесь точно нужна переменная? Разве статуса лочки не достаточно?
То решение, которое я предложил, заменяет собой SuspendThread. Т.е. те участки кода "замораживают" второй поток и "размораживают" его, но делают это именно тогда, когда второй поток не держит никаких блокировок. Во время этой заморозки через conditional_variable никакие блокировки долго не держатся, а значит ничего не будет дедлока.
Твоя проблема в том, что SuspendThread использовать категорически нельзя.
Мой код достаточно вставить _вместо_ SuspendThread в потоке 1, и где-то в цикле обработки сообщений в потоке 2. Всё. Весь остальной доступ к совместным ресурсам работает как раньше (или не работает, не знаю, как там всё сделано)
Здравствуйте, Lonely Dog, Вы писали:
B>>Может есть более удачные решения? LD>А нельзя перехватить SuspendThread и __lock проверять? Скажем, в __lock выставлять флаг, в suspend его проверять, если выставлен, то немного подождать и снова проверить. когда флаг сброшен, может вызывать suspend. Хотя тут наверное возможна гонка. Хм...
У меня была такая мысль. Но __lock — это функция из ntdll! Максимум что можно, если у нее именованный мьютекс, попробовать докопаться до него. Но это недокументированно и грязно...
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, RedApe, Вы писали:
B>>Почему кстати переменной? Мьютекса наверное? Или намекаете, что его не хватит и нужна будет условная переменная? B>>Здесь точно нужна переменная? Разве статуса лочки не достаточно?
RA>Твоя проблема в том, что SuspendThread использовать категорически нельзя.
Это да.
RA>Мой код достаточно вставить _вместо_ SuspendThread в потоке 1, и где-то в цикле обработки сообщений в потоке 2. Всё. Весь остальной доступ к совместным ресурсам работает как раньше (или не работает, не знаю, как там всё сделано)
Мне интересно с точки зрения кода. У тебя три сущности: мьютекс, условная переменная и просто переменная. Вопрос, все ли три нужны? Я навскидку решение с SuspendThread заменил одним единственным мьютексом. Делаю один scoped lock внутри цикла с сообщениями, и еще один вместо SuspendThread. Что-то упускаю?
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Basil2, Вы писали:
B>Мне интересно с точки зрения кода. У тебя три сущности: мьютекс, условная переменная и просто переменная. Вопрос, все ли три нужны? Я навскидку решение с SuspendThread заменил одним единственным мьютексом. Делаю один scoped lock внутри цикла с сообщениями, и еще один вместо SuspendThread. Что-то упускаю?
Хм.
Правильно ли я тебя понимаю, что почти всё время, пока работает поток №2, он держит мьютекс захваченным. На короткое мгновение в конце/начале цикла мьютекс отпускается, и в этот момент первый поток получает шанс его захватить, изменить какие-то общие переменные и вернуть мьютекс на место?
Да, наверно мой вариант действительно переусложнён.
Здравствуйте, Basil2, Вы писали:
B>Иногда поток 2 выделяет память. Это происходит через функцию рантайма __lock(). Если поток приостанавливают в этот момент, то он так и остается в лочке. Затем приостановивший его тред сам пытается выделить память, попадает в ту же __lock() и всё виснет намертво.
Почему бы просто не делегировать выделение и освобождение памяти одному потоку, т.е. первому? Придётся написать клинап памяти от второго потока, но это и так надо делать после остановки потоков.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, RedApe, Вы писали:
RA>Правильно ли я тебя понимаю, что почти всё время, пока работает поток №2, он держит мьютекс захваченным. На короткое мгновение в конце/начале цикла мьютекс отпускается, и в этот момент первый поток получает шанс его захватить, изменить какие-то общие переменные и вернуть мьютекс на место?
Именно так.
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Pavel Dvorkin, Вы писали:
B>>Если же вы про __lock(), это это функция ntdll. PD>Ты уверен ? Я никогда не слышал про такую функцию в ntdll, и ее имя внушает сомнения, что она выделяет память.
Я тоже никогда не слышал.
PD>Можно ссылку на ее описание ?
Если б оно было, я бы наверное не спрашивал
Просто эта функция участвует в цепочке при просмотре Call Stack, и грузится она из ntdll. Вот и всё, что я про нее знаю...
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.