Size: a a a

2021 October 08

MB

Mikail Bagishov in Rust Async
Низкоуровные футуры обычно пишут примерно как
fn poll(&mut self, cx: &mut Context<'_>) -> ... {
   register_context(cx);
   if check_condition() {
      return Ready
   }
   return Pending
}

При таком стиле рейс кондишны вида "в начале исполнения второго poll() сработало событие, запущенное первым poll() в другом контексте" корректно обрабатываются
источник

VP

Vladislav Podporkin in Rust Async
Допустим у нас футура с типом T запускает поток, который по условной ссылке на нее записывает значение и пробуждает ее по сохраненному контексту.

Условно!:
fn poll(&mut self, cx: Context) -> Poll<T> {
self.saved_cx = cx;
if !self.started {
 self.started = true;
 thread::spawn(|| {
  /*work*/;
  self.value = Some(/*...*/)
  self.saved_cx.wake(); // (1)
 }
}
if value.is_some() { Ready(...) }
else { Pending }
}

Так вот. Если poll вызовется дважды до wake, то поток который пишет значение, может взять старый контекст. При этом другим потоком в этот же момент может вызваться poll (повторно до wake) с новым контекстом, который сохранится в футуре и вернётся Pending. А поток пишущий значение, будет использовать старый контекст, когда шедулер этого не знает
источник

VP

Vladislav Podporkin in Rust Async
Ноооо
источник

VP

Vladislav Podporkin in Rust Async
Это проблема, только если 1 пункт этого ответа верен
источник

VP

Vladislav Podporkin in Rust Async
Переслано от Vladislav Podporkin
Привет всем.
Пытаюсь разобраться в работе Future, и не могу найти информацию о:

1. Можно ли вызывать poll второй раз до вызова wake, если первый poll опрос вернул Pending
2. Можно ли вызывать wake повторно у последнего переданного Context изнутри Future до повторного вызова poll?
источник

VP

Vladislav Podporkin in Rust Async
Переслано от Mikail Bagishov
1. Да, но это просто лишняя работа.
2. Да, футура может завейкать себя еще во время работы poll()
источник

IT

Ilya Titkov in Rust Async
Ну ты же тут в самом начале сохраняешь контекст в saved_cx. Значит все должно быть ок, потому что условный self тут должен же быть под мутексом, чтобы ты мог его поменять и в poll и в потоке. А раз он под мутексом, то в треде не может оказаться более старый контекст.

Точнее может быть так, что тебе вызвали poll второй раз(но код еще не дошел до self.saved_cx = cx) и в этот момент в треде как раз держится мутекс и сетится значение, потом когда там мутекст отпускается, ты пересетишь self.saved_cx, но это не будет именть значения, т.к. футура вернет ready.
источник

MB

Mikail Bagishov in Rust Async
Ну, придется каким-то образом уведомить фоновый поток о том, что контекст поменялся. Например, с помощью https://docs.rs/futures/0.3.17/futures/task/struct.AtomicWaker.html
источник

VP

Vladislav Podporkin in Rust Async
Да. Я и говорю, что именно поэтому без синхронизации в виде мьютекса могут быть проблемы, если по контракту можно дергать poll повторно до пробуждения.
источник

VP

Vladislav Podporkin in Rust Async
Переслано от Mikail Bagishov
> синхронизировать обращение к нему сохраненному

Секундочку, а зачем? Если poll позвали с новым контекстом, то старый уже не нужен.
источник

VP

Vladislav Podporkin in Rust Async
Переслано от Ilya Titkov
Ну ты же тут в самом начале сохраняешь контекст в saved_cx. Значит все должно быть ок, потому что условный self тут должен же быть под мутексом, чтобы ты мог его поменять и в poll и в потоке. А раз он под мутексом, то в треде не может оказаться более старый контекст.

Точнее может быть так, что тебе вызвали poll второй раз(но код еще не дошел до self.saved_cx = cx) и в этот момент в треде как раз держится мутекс и сетится значение, потом когда там мутекст отпускается, ты пересетишь self.saved_cx, но это не будет именть значения, т.к. футура вернет ready.
источник

IT

Ilya Titkov in Rust Async
Ну так без мутекса и не скомпилируется
источник

VP

Vladislav Podporkin in Rust Async
О, такой даже есть.
источник

VP

Vladislav Podporkin in Rust Async
Я эту модель в паре на F# переписываю. Там нет таких фокусов. Нужно все продумать!
Поэтому я и спросил про альтернативный дизайн с уменьшением на синхронизацию во фьючах в простых случаях.
источник

MB

Mikail Bagishov in Rust Async
Ну просто фактически ты предлагаешь переложить синхронизацию на экзекутор. Теперь уже ему придется ввести дополнительный уровень индирекции, чтобы контекст оставался стабильным при переезде футур по его очередям и потокам
источник

VP

Vladislav Podporkin in Rust Async
Ну, я все равно не могу представить экзекьютор без синхронизации, мне сложно оценить насколько дополнительная работа для него будет дороже.
Но в целом да. Именно это.
источник
2021 October 09

IL

Ilya Lakhin in Rust Async
Привет!

Задача заключается в следующем. В некоторых случаях программа может запрашивать у пользователя ввести какую-то строку через терминал, которая будет ответом на заданный через терминал вопрос. Как правило данная проблема решается синхронным образом, просто блокирующим чтением строки из stdin. Тем не менее в моём случае возможны ситуации, в которых задавшая вопрос задача может быть отменена. В этом случае мы должны прекратить чтение из stdin.

Чтение можно "прекратить", например, если мы заведём отдельный тред, который будет блокирующим способом непрерывно читать строки из stdin, и просто рассылать их другим тредам(задачам), которые в свою очередь будут отменяемыми, и будут читать эти строки неблокирующим образом. Такое решение мне тоже не подходит, потому что у меня в программе могут быть другие процессы, которым может тоже понадобиться читать из stdin(например, escape-последовательности), а stdin будет всегда заблокирован.
источник

C

Cat in Rust Async
может чего-то не понимаю, но как в вашей программе стдин читается другими процессами?
источник

ft

fan tom in Rust Async
видимо имелись в виду другие потоки
источник

IL

Ilya Lakhin in Rust Async
Да, я не OS процесс имел ввиду, конечно.
источник