Перейти к содержимому
  • 0
Avevad

Странное поведение coroutine.yield

Вопрос

Я пишу EEPROMную программу, довольно запутанную, но вся ее суть сводится к тому, что она создает поток, который что-то там делает (неважно), а прога делает coroutine.resume(thread) до тех пор, пока не закончится. Выполнение потока долгое, поэтому в нем натыканы yieldы чтобы не было too long without yielding. К потоку претензий нет, но так как он выполняется долго, то и главная рутина, которая его запускает и ждет завершения тоже работает долго и в ней происходит TLWY, поэтому я после coroutine.resume(thread) (он крутится в цикле) поставил coroutine.yield(), чтобы главный поток компа уступил выполнение. Но и так тоже не сработало - управление ушло в coroutine.yield и не вернулось((. Т. е. coroutine.resume(thread) выполнился, часть дочернего потока выполнилась, управление было передано главному, он в свою очередь вызвал yield (уступил) и завис (ему никто обратно не уступил??). Как в таком случае избежать TLWY, если, судя по всему yield - не то, что нужно? Я знаю что некоторые из системных вызовов OC (i. e. component.invoke()) тоже уступают выполнение, но мне не нужно взаимодействовать с компонентами. Как быть? И почему yield зависает?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Рекомендуемые сообщения

Выполнение computer.pullSignal(0) обеспечит минимальную уступку времени.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Так, а сигнал случайно мы не вытащим? Просто нужно, чтобы во время выполнения этой части такого не произошло (хотя может и пофиг на самом-то деле). И еще - почему все-таки не работает yield?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
4 минуты назад, Avevad сказал:

Так, а сигнал случайно мы не вытащим? Просто нужно, чтобы во время выполнения этой части такого не произошло (хотя может и пофиг на самом-то деле). И еще - почему все-таки не работает yield?

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

 

yield, скорее всего, нужно вызвать с параметром времени ожидания yield(0), или же ждать сигнала. Как в этом случае обрабатывать само событие, я не знаю, не пробовал.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Спасибо, все понял

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Внутри песочницы coroutine.yield переопределён:

yield = function(...) -- custom yield part for bubbling sysyields
  return coroutine.yield(nil, ...)
end

Также переопределён и coroutine.resume.

resume = function(co, ...) -- custom resume part for bubbling sysyields
  checkArg(1, co, "thread")
  local args = table.pack(...)
  while true do -- for consecutive sysyields
    debug.sethook(co, checkDeadline, "", hookInterval)
    local result = table.pack(
      coroutine.resume(co, table.unpack(args, 1, args.n)))
    debug.sethook(co) -- avoid gc issues
    checkDeadline()
    if result[1] then -- success: (true, sysval?, ...?)
      if coroutine.status(co) == "dead" then -- return: (true, ...)
        return true, table.unpack(result, 2, result.n)
      elseif result[2] ~= nil then -- yield: (true, sysval)
        args = table.pack(coroutine.yield(result[2]))
      else -- yield: (true, nil, ...)
        return true, table.unpack(result, 3, result.n)
      end
    else -- error: result = (false, string)
      return false, result[2]
    end
  end
end

Работает это так:

  • Если корутина вернула true, nil, ... — отдать true, ... (поэтому внутри песочницы разница между обычными и переопределёнными функциями не видна).
  • Если корутина йелднулась без нила в начале, то это "системный вызов". Потому что доступ к непереопределённой функции coroutine.yield есть только внутри machine.lua. В таком случае йелдится и текущая корутина вверх по цепочке.
  • EEPROM запускается в корутине. Если корутина эта йелдится, случается следующее:
    • yield(false) — это выключить компьютер;
    • yield(true) — ребутнуть его;
    • yield(n), где type(n) == "number", — спать n секунд;
    • yield(f), где type(f) == "function", — это вызвать непрямую функцию.
    • yield с любым другим аргументом — спать до скончания века.
  • Дополнительно обрабатывается умирание корутины: return посреди еепрома — это error("computer halted", 0); другие ошибки прокидываются.

А теперь смотрим на наш вызов.

  • В EEPROM вызвана coroutine.yield(0).
  • Эта функция переопределена, чтобы вызвать нативный coroutine.yield(nil, 0).
  • Корутина тогда йелдится и отдаёт nil, 0.
  • В списке выше это пункт 3.5. Компьютер уйдёт в вечный сон.

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

Но вообще, незачем звать coroutine.yield напрямую. Есть computer.pullSignal, computer.shutdown, component.invoke(addr, nonDirectMethod, ...).

  • Нравится 5
  • Одобряю 1
  • Спасибо 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
5 часов назад, eu_tomat сказал:

Выполнение computer.pullSignal(0) обеспечит минимальную уступку времени.

 

 

Лучше вычислять примерную дельту времени неприрывного выполнения кода и при выходе за критическое значение отдавать управление. Вызывать каждую итерацию pullSignal - нерационально. Думаю стоит уточнить. Ибо драгоценное время тратится впустую на блокирующую операцию.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
14 минуты назад, NEO сказал:

Вызывать каждую итерацию pullSignal - нерационально. Думаю стоит уточнить. Ибо драгоценное время тратится впустую на блокирующую операцию.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

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

Гость
Ответить на вопрос...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...