Перейти к содержимому
ProgramCrafter

Способы решения TLWY

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

Disclaimer:

1. Ниже в этой теме может встречаться код, который может нанести ущерб вашему серверу. Эти образцы предоставлены лишь в ознакомительных целях! Я не несу ответственности за положенные ими сервера Майнкрафта.

2. Я не залазил глубоко в исходники OpenComputers, поэтому могу где-то ошибаться.

 

Одна из часто встречающихся проблем в OpenComputers - превышение таймаута выполнения программы. При этом выбрасывается ошибка TLWY (too long without yielding).

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

 

Чтобы создать стабильную систему, проблему TLWY надо решить. У меня были такие идеи:

1. Обёртки из pcall. Это будет некрасиво выглядеть и понизит максимальную глубину рекурсии.

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

3. Принудительное прерывание кода по обращениям к глобальным переменным. Проблема - невозможность отследить вызов анонимной функции.

    Например, так не прервать код (function(f) return f(f) end)(function(f) return f(f) end).

4. Интерпретировать байткод Lua. Самое доступное решение, но надо знать формат байткода и обновлять с выходом каждой версии Lua.

5. Переписать виртуальную машину Lua так, чтобы она сама уступала управление другим потокам (тогда ошибка TLWY уйдёт в прошлое). Для этого надо договориться с разработчиками OpenComputers и уметь программировать на Java.

 

Есть ли какие-то варианты избежания TLWY, которые я упустил?

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


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

4. Интерпретировать байткод Lua

Как это может помочь с TLWY?

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


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

Как это может помочь с TLWY?

Идея состоит вот в чём: код запускаемой программы компилируется стандартной функцией load, а выполняется уже системой (то есть система выполняет роль виртуальной машины Lua). После интерпретирования каждой K-ой команды байткода система уступает управление с помощью coroutine.yield. Таким образом, прерывания будут происходить независимо от кода программы и достаточно часто, чтобы не вылетать в нормальных условиях.

 

Бонус - можно заново написать библиотеку debug, чтобы отлаживать пользовательские программы.

 

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

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


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

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

local function sleep()
	return coroutine.yield()
end

local function longCodePiece()
  --Тонна кода тут
  sleep()
  --Еше тонна тут
end

local thread = coroutine.wrap(longCodePiece)

while true do
	local event = {computer.pullSignal(0.05)}
	-- Обрабатывем сигнал тут
	coroutine.resume(thread, event)
end

А такое чудо от потери событий не спасет?
Вообще если ваша задача слишком жирна для одного тика почему-бы ее не выделить в отдельную сопрограмму?

Изменено пользователем ZO125

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


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

Переписать виртуальную машину Lua так, чтобы она сама уступала управление другим потокам (тогда ошибка TLWY уйдёт в прошлое). Для этого надо договориться с разработчиками OpenComputers и уметь программировать на Java.

TLWY не следует воспринимать как какую-то недоработку OpenComputers, эта механика намеренно добавлена разработчиками мода для защиты игрового сервера от зависших или злонамеренно зацикленных скриптов. Что касается злонамеренности, то эта защита легко обходится в несколько строк кода. В рабочих же программах, которым недостаточно окна, предоставляемого TLWY, эта проблема решается не столь красиво, а само решение требует адаптации под задачу и условия сервера. Но во всех известных мне случаях задача решаемая. Если не смотря на принятые меры, программа всё же завершается по TLWY, это может означать лишь то, что нагрузка на игровой сервер превысила все разумные пределы. В этих условиях лучшее, что можно сделать, это прекратить любые действия в программе, тем самым снизив нагрузку на сервер, и, переждав проблемы и не завершившись с ошибкой, возобновить работу.

 

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

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


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

а выполняется уже системой (то есть система выполняет роль виртуальной машины Lua).

Имеешь ввиду свою систему? Предлагаешь сделать среду выполнения луа внутри среды выполнения луа?

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


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

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

Почему механизм защиты от лагливого кода вообще должен вырубать компы 

Насколько я понимаю, логика выбрасывания TLWY находится тут: https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/src/main/resources/assets/opencomputers/lua/machine.lua#L45-L54

Почему бы вместо error(tooLongWithoutYielding) не возвращать управление? Таким образом управление будет возвращаться всегда, даже если в выполняющемся коде оно не предусмотрено

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


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

Почему механизм защиты от лагливого кода вообще должен вырубать компы

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

 

5 часов назад, hohserg сказал:

Почему бы вместо error(tooLongWithoutYielding) не возвращать управление? Таким образом управление будет возвращаться всегда, даже если в выполняющемся коде оно не предусмотрено

Куда возвращать управление? В зависшую программу while true do end?

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


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

Предлагаешь сделать среду выполнения луа внутри среды выполнения луа?

Или так, или вообще переписать OpenComputers и сделать среду выполнения Lua на Java. (Кстати, тогда не понадобится та dll, которая идёт в комплекте с модом.)

 

1 час назад, eu_tomat сказал:

Куда возвращать управление?

@hohserg предлагает возвращать управление джаве, но это невозможно из-за ограничений самого Lua: coroutine.yield нельзя вызывать из кода, запущенного библиотекой debug. Возможно, в этом есть и своя логика: во время выполнения той функции checkDeadline управление принадлежит не пользовательскому коду, и yield может прервать не тот поток.

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


Ссылка на сообщение
Поделиться на других сайтах
В 02.01.2021 в 13:36, ZO125 сказал:

А такое чудо от потери событий не спасет?

Спасёт, но с двумя оговорками:

1. События должны быть нужны только одной программе. Если доставлять события всем программам, то yield надо делать на каждую программу и на каждое событие (вдруг каждая программа обрабатывает нажатие клавиши по секунде?) Тогда и начнётся пропуск части событий.

2. Вредоносные программы по-прежнему будут нагружать сервер и получать TLWY, а при этом могут крашиться любые компы.

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


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

1. События должны быть нужны только одной программе. Если доставлять события всем программам, то yield надо делать на каждую программу и на каждое событие (вдруг каждая программа обрабатывает нажатие клавиши по секунде?) Тогда и начнётся пропуск части событий.

Ваши программы и вложенные в них сопрограммы должны соблюдать простое правило: не обрабатывать ничего дольше чем промежуток времени до следующего computer.pullSignal(), если-же вам необходимо сделать что-то что занимает времени больше вы просто делаете этот pullSignal() и считаете дальше. Чем чаще вы ставите yield-ы тем дольше (реального времени) вы считаете, но тем меньше шанс словить TLWY. Просто найдите компромисс между скоростью вычислений и опасностью словить TLWY в конкретной части кода.
А еше лагающий сервер это плохо. Не стоит возвращать каким-либо образом управление компьютеру отключившемуся от TLWY. В любом случае ваш сервер не должен лагать, или какой смысл вообще играть на таком сервере?

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


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

Куда возвращать управление? В зависшую программу while true do end?

Туда, куда возвращает управление computer.pullSignal и некоторые методы компонентов

  

7 часов назад, eu_tomat сказал:

злонамеренно зацикленные скрипты.

А можешь привести пример таких скриптов?

Изменено пользователем hohserg

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


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

Туда, куда возвращает управление computer.pullSignal и некоторые методы компонентов

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

 

24 минуты назад, hohserg сказал:

А можешь привести пример таких скриптов?

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

 

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

  • Как-то понять, что сервер лагает именно из-за компов;
  • Найти проблемные компы и их владельцев.

Администраторы серверов располагают таким инструментарием?

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


Ссылка на сообщение
Поделиться на других сайтах
В 01.01.2021 в 22:00, ProgramCrafter сказал:

Чтобы создать стабильную систему, проблему TLWY надо решить.

Здесь идёт речь о системе OpenOS, или в целом об OpenComputers?

 

В 01.01.2021 в 22:00, ProgramCrafter сказал:

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

Имеется в виду консоль, ожидающая ввода команды? Какой в это время TPS на сервере?

 

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


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

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

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

Гость
Ответить в тему...

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

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

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

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

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


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