LINUX.ORG.RU

корректное завершение многотредовых с++ приложений?

 , , , ,


0

4

какой лучший способ внезапно прервать приложение

так, чтобы вызывались все деструкторы и все треды килялись

я пробовал элементарное exit(1)

но оно вроде не вызывает никакие деструкторы, что не есть гуд

хочется что-то более правильное

может в std:: что-то такое есть

★★★★★

Последнее исправление: smilessss (всего исправлений: 2)
Ответ на: комментарий от smilessss

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

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

у меня былая идея придумать какой-то универстальный механизм

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

но так похоже в плюсах не работает

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

Забей, ты так свою задачу не решишь. Даже если ты получишь способ убивать треды, то как будешь освобождать ресурсы, которые они потенциально взяли у ОС? Если тебя это не волнует, то можно покопать в сторону перехвата pthread вызовов, регистрировать в этом перехватчики все треды, дальше делай чего хочешь с ними. Правильней - форкать приложуху, и потом просто убивать форкнутый процесс вместе с тредами и ресурсами. Но всё это выглядит каким-то адовым г-м все равно, что у тебя за либа, которая не подчищает за собой? Может ты доки не дочитал и там нужно что-то вызвать для деинициализации?

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

почитаю про форки

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

там при работе вызываются треды webrtc

там множество разных task_queue_(task_queue_factory->CreateTaskQueue

так вот в очередь они становятся и после завершения работы просто висят

13	"rtc-low-prio"			epoll_wait () from /lib/x86_64-linux-gnu/libc.so.6
14	"TaskQueuePacedS"			epoll_wait () from /lib/x86_64-linux-gnu/libc.so.6
15	"rtp_send_contro"			epoll_wait () from /lib/x86_64-linux-gnu/libc.so.6
16	"ModuleProcessTh"			pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib/x86_64-linux-gnu/libpthread.so.0
17	"AudioEncoder"			epoll_wait () from /lib/x86_64-linux-gnu/libc.so.6
18	"DecodingQueue"			epoll_wait () from /lib/x86_64-linux-gnu/libc.so.6
19	"IncomingVideoSt"			epoll_wait () from /lib/x86_64-linux-gnu/libc.so.6



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

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

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

это общий подход по тредам, вне зависмости от того, как их называют или языка программирования.

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

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

треды эти не мои - их создаёт другой компонент софта

ковырять эти треды хоть и можно но изменять их код особого смысла нет - это внутрянка самого webrtc корретно их завершать нужно корректным кодом завершения, там где это всё создавалось

такого кода у нас пока нет

я пока ничего лучше не придумал, чем делать

exit(1)

и перезапускать бинарник башем

может есть какие-то лучшие практики

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

такого кода у нас пока нет

раз нет, значит делайте.

я пока ничего лучше не придумал, чем делать exit(1)

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

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

Ну вообще твоя хотелка решение имеет - заставить другой тред убиться и разрушить все стековые объекты. Но учти, что это тоже такое себе, никто не знает чем убиваем тред занимался, также может что-то «подтекать», если тред брал какой-то ресурс, не закончил, а его прервали. Здесь решение. Т.е. шлем треду сигнал, обработчик сигнала генерит исключение, стек разматывается. Если исключение не было поймано в каком-нибудь обработчике, то будет вызван terminate() (ожидаемо, нжно повесить свою заглушку на обработчик terminate’a).

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

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

как прибить webrtc треды при этом сохранив главный процесс?


я здесь пробовал цепляться к бинарнику и килять треды извне - это не работает т.к. после килла треда нормальная работа бинарника нарушалась

там нужно как-то завершать их, но как я хз

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

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

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

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

доступы все есть, но там сложно для меня это всё(

я вот смотрю что все треды стоят в epoll_wait

а только ModuleProcessTh - в pthread_cond_timedwait

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


void Call::DestroyAudioSendStream(webrtc::AudioSendStream* send_stream) {
  TRACE_EVENT0("webrtc", "Call::DestroyAudioSendStream");
  RTC_DCHECK_RUN_ON(worker_thread_);
  RTC_DCHECK(send_stream != nullptr);

  send_stream->Stop();

  const uint32_t ssrc = send_stream->GetConfig().rtp.ssrc;
  webrtc::internal::AudioSendStream* audio_send_stream =
      static_cast<webrtc::internal::AudioSendStream*>(send_stream);
  suspended_audio_send_ssrcs_[ssrc] = audio_send_stream->GetRtpState();

  size_t num_deleted = audio_send_ssrcs_.erase(ssrc);
  RTC_DCHECK_EQ(1, num_deleted);

  // TODO(bugs.webrtc.org/11993): call AssociateSendStream and
  // UpdateAggregateNetworkState asynchronously on the network thread.
  for (AudioReceiveStream* stream : audio_receive_streams_) {
    if (stream->local_ssrc() == ssrc) {
      stream->AssociateSendStream(nullptr);
    }
  }

  UpdateAggregateNetworkState();

  delete send_stream;
}

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

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

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

std::terminate. Если тебе нужно внезапно прервать, это самое то.

std::terminate вызывает std::abort, а std::abort генерирует корку. Отличный совет: засрать корками всю систему. Следующий вопрос от ТС будет: а почему у меня свободное место на диске кончилось?

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

А ещё кто-то переопределит std::terminate_handler и ничего не завершится).

По умолчанию, хендлер - std::abort() aka просто abort(), который в случае юниксов вынудит ядро послать процессу SIGABRT.

По поводу exit(). Не хочешь звать exit()… вызови std::exit() :-D

Если коротко, то вызовутся деструкторы для всех объектов со статическим времени жизни, которые были созданы: std::exit().

Деструкторы объектов с динамическим временем жизни (грубо: созданными через new) вызваны не будут. Но ты можешь зарегистрировать функцию очтистки через std::atexit().

Сложнее с объектами, со автоматическим временем жизни, т.е. обычными объектами, созданными на стеке: у нас не будет выхода за пределы области видимости (возврата из функции std::exit() нет), а значит и условие вызова деструктора не выполняется.

С тредами тоже нюанс: если дойдёт дело до деструктора std::thread, а тред в работе, то вылетит исключение и тогда уже будет или обработка оного с твоей стороны или std::terminate().

И всё выше верно для всех вариантов выхода, минуя выход через return из main(). Отсюда и варианты:

  1. коды ошибок и возвращаться наверх в main() и там делать return.
  2. вместо exit() - бросать исключение и ловить его на самом верху (в треде - в функции треда), при исключении стек раскручивается и деструкторы гарантированно вызываются

Это про решения в лоб, а дальше задаться вопросом:

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

  • Если открыт файл - его закроет файловая система
  • Аналогично с сокетом
  • Память тоже после смерти процесса система вернёт

Если же есть какие-то чувствительные данные (затереть ключи и пароли в памяти перед выходом, SHM почистить/освободить, всякие gpio с state-full реализацией (через /sys) вернуть в нужное состояние, etc), которые нужно гарантированно чистить, то тут однозначно нужен хендлер через std::atexit() или через RAII объект со статическим временем жизни.

С тредами тоже: нужно сигналить и завершаться. А может и не нужно, и просто быстро выходить. Всё зависит от твоих данных.

В общем, как видно - безопасности, далеко не всегда про скорость :)

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

Я с такой ситуацией пока не сталкивался.

Их масса. И (сейчас крамольность скажу) - даже чистить за собой память не всегда имеет смысл. Любые ресурсы которые outlive the process (SHM, SHM locks, file locks) - отдельная тема, очевидно.

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

Ну смотри, у меня в embedded исключений нет и даже некому SIGABRT послать, поэтому там, где кидается исключение, по сути, случается std::terminate() с последующим, ребутом в продакте. Дальше приправы по вкусу: репорт, логирование, или вообще ничего - быстро поднятое, упавшим не считается.

даже чистить за собой память не всегда имеет смысл

А чего крамола, я согласен. В портянке выше написал про это. Главное не принимать за догму и понимать, когда чистить остро необходимо (пароли, ключи, etc). Но секурити разработка это вообще отдельная песня.

Любые ресурсы которые outlive the process (SHM, SHM locks, file locks) - отдельная тема, очевидно.

…, Legacy User Space GPIO (через /sys, а не /dev/gpiochipX) и так далее.

У меня вопрос был в том, когда вот уж вот совсем нужно быстро упасть и вообще ничего не чистить, что бы специально звать _exit() вместо exit(). Я понимаю, что такие ситуации существуют, но вот примеров сходу придумать не могу, ровно как и поведения из своего опыта. Условно, даже логи желательно зафлушить, что бы дать представление, отчего упали то, дабы разобраться в причинах. В embedded тоже, там куча периферии, DSP, ADC, DAC, etc, которые бы неплохо остановить.

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

у меня в embedded исключений нет

Честно скажу - от этого мира я очень далёк…

и даже некому SIGABRT послать

… но готов «поверить на слово» :)

В портянке выше написал про это.

Я (к сожалению / как обычно) сначала прочитал ответ только на свой пост ;)

когда вот уж вот совсем нужно быстро упасть

Если софт «активно спускает денюжки» Вы захотите его остановить как можно скорее. Более прозаичный example - service downtime. У меня есть модули которые, если всё «честно» чистить, будут падать пару минут.

А дальше - больше. Наш стандартный «process scheduler» несколько раз попытается послать SIGINT, а потом пошлёт SIGKILL, со всеми вытекающими - потерей логов, статистики итд итп.

У меня вопрос в том, когда вот уж вот совсем нужно быстро упасть и вообще ничего не чистить, чтобы специально звать _exit() вместо exit().

Такого и у меня пока не было ;) Потому и спросил у «товарища»… Ждёмс ;)

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)

Для этого, код, выполняющийся в потоках должен быть написан так, чтобы прерываться по какому-то условию (напр. периодически просматриваемый флаг std::atomic<bool> isRunning). В случае, если потоки используют I/O (pselect, epoll, пр.), то после установки флага в false надо прерывать системные вызовы подачей потокам сигнала (pthread_kill()). Отсюда код трэдов должен это правильно обработать и выйти из цикла.

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