LINUX.ORG.RU

Завершение потока с блокирующим вызовом внутри

 , , , ,


0

1

Уже всю голову сломал, Хотя проблема вроде тривиальная.

Предположим система предоставляет такие API:

 Receive(context); // blocking call until external event or Close
 Close(context); 

Close снимает с receive.

Есть функция потока и деструктор, который должен этот поток остановить :

thread() {
  while (!stop) {
    // ... (code 1)
    Receive(context);
    // ... (code 2)
  }
}

~() {
  stop = true;
  Close(context);
}

Деструктор должен дождаться завершения потока. Но как это сделать?

Ирония в том что если послать Close когда поток будет в состоянии (code1), то вызов Receive всё равно повиснет на бесконечном ожидании внешнего события.

Значит нужно послать Close или в состоянии Receive или в состоянии (code 2). Но как гарантировано дождаться входу в функцию Receive. Если вставить mutex и разблокировать его перед Receive, то состояние гонки всё ещё актуально ( между разблокировкой mutex и вызовом Receive может вызваться преславутый Close).

Найденный мной вариант решения проблемы - греть процессор :

thread() {
  Lock
  while (!stop) {
    // ... (code 1)
    Receive(context);
    Unlock
    // ... (code 2)
    Lock
  }
}

~() {
  stop = true;
  while !Try_lock() {
    Close(context);
  }
}

Таким образом из деструктора я буду долбить Close пока не попаду в code(2). И всё бы хорошо, но Close падает с segmentation fault на повторном вызове, и это ломает всю концепцию.

И алыверды - паттерн «долбить» не работает никогда, т.к. существует проблема стороннего наблюдателя - дескриптор могут закрыть и открыть в перерыве между долбёжками. Именно поэтому народ пользует inotify а не watch.

pon4ik ★★★★★ ()

если Receive() и Close() могут выполняться в разных тредах, то в «системе» должно быть подробное описание как реализуется тредсейфность, или хотя бы как ей пользоваться юзеру

без этого нечего обсуждать

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

я вообще вижу только один — сериализация действий через захват блокировки (может быть неявный), но в любом случае выполнение receive после close должно вываливаться с ошибкой «уже закрыто» или «неверный дескриптор» или типа того, а не зависать навечно, как сказал ТС выше

это просто тонкий намек почитать внимательно документацию на API

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

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

Вот её негативный сценарий(который в условии параллелизма будет выполняться не всегда):

Вход: поток A запускает поток B который засыпает на Receive.

Далее события разворачиваются так:

  • A вызывает delete для B
  • A вызывает деструктор B
  • Память B становится помеченна как освобождённая
  • Поток B продолжает работать до первого обращения к члену данных B который освобождён

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

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

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

а сегфолты ТС описал только именно внутри close() в случае повторного вызова, и здесь причина доступа к уже освобожденной памяти не просматривается, скорее причина в закрытии уже закрытого

anonymous ()