Как убить поток, остановить выполнение обещания в Раку - PullRequest
7 голосов
/ 13 марта 2020

Я жду, чтобы остановить (отправить исключение) текущее обещание на SIGINT. Примеры, приведенные в do c, завершают весь процесс, а не только одного работника.

Кто-нибудь знает, как "убить", "отменить планирование", "остановить" работающий поток?

Это для проблемы p6-jupyter-kernel или этой Проблема REPL .

Текущее решение - перезапустить repl, но не убить заблокированный поток

await Promise.anyof(
  start {
      ENTER $running = True;
      LEAVE $running = False;
      CATCH {
          say $_;
          reset;
      }
      $output :=
        self.repl-eval($code,:outer_ctx($!save_ctx),|%adverbs);
  },
  $ctrl-c
);

Ответы [ 2 ]

6 голосов
/ 15 марта 2020

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

Длинный ответ: во-первых, полезно прояснить небольшую путаницу в вопросе.

все, нет такой вещи как "бегущий Promise"; Promise - это структура данных для передачи результата асинхронной операции. Блок start действительно выполняет три вещи:

  1. Создание Promise (к которому он относится)
  2. Планирование некоторого кода для запуска
  3. Организация этого результат выполнения этого кода отражается в сохранении или нарушении Promise

Это может звучать немного академично c, но на самом деле имеет значение: Promise не знает, что в конечном итоге в конечном итоге он останется или сломается.

Во-вторых, блок start не - по крайней мере со встроенным планировщиком - не поддерживается потоком, а работает в пуле потоков. Даже если бы вы могли найти способ «убрать» поток, планировщик пула потоков не будет доволен тем, что один из потоков, которые он ожидает съесть из рабочей очереди при исчезновении. Вы можете написать свой собственный планировщик, который действительно каждый раз будет работать с потоком fre sh, но это еще не полное решение: что если часть кода, которую пользователь запросил, выполняет выполнение расписаний самостоятельно, и тогда await с? Тогда нет ни одного потока, который нужно убить, чтобы действительно остановить все.

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

Любой такой механизм, который хочет иметь возможность остановить поток, заблокированный на чем-либо (не только на вычислениях, но и на операциях ввода-вывода, блокировки и т. Д. c.), Потребовал бы глубокой интеграции и взаимодействия из базовой среды выполнения (такой как MoarVM). , Например, попытка отменить поток, который в данный момент выполняет сборку мусора, приведет к катастрофе (скорее всего, к блокировке виртуальной машины в целом). Другие неудачные времена отмены могут привести к повреждению памяти, если она была на полпути через операцию, которую небезопасно прерывать, взаимоблокировки в другом месте, если уничтоженный поток удерживал блокировки, и так далее. Таким образом, нужен механизм безопасного наведения. (У нас уже есть что-то в этом роде в MoarVM, чтобы знать, когда это безопасно для G C, однако отмена подразумевает разные требования. Вероятно, она пересекает многочисленные части кодовой базы VM.)

И это еще не все : та же ситуация повторяется и на уровне языка раку. Например, Lock::Async - это не та блокировка, о которой знает базовая среда выполнения. Вероятно, лучшее, что можно сделать, это попытаться разорвать стек вызовов и запустить все LEAVE фазеры; таким образом, есть некоторая надежда (если люди использовали метод .protect; если они просто вызвали lock и unlock явно, мы закончили). Но даже если нам удастся не утратить ресурсы (это уже большая проблема), мы все равно не будем знать - в целом - убил ли код, который мы убили, мир в каком-либо непротиворечивом состоянии. В контексте REPL это может привести к сомнительным результатам в последующих исполнениях, которые получают доступ к тому же глобальному состоянию. Это, вероятно, раздражает, но то, что действительно пугает меня, так это то, что люди используют такой механизм отмены в производственной системе - что они и сделают, если мы его реализуем.

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

5 голосов
/ 13 марта 2020

В настоящее время нет способа остановить поток, если он не хочет останавливаться.

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

...