В версии QUIK 12.2.1.2 (а, похоже, и в более ранних) при запуске любого коллбека отключается сборка мусора (collectgarbage('stop')), а после его отработки включается (collectgarbage('restart')). В общем, решение разумное, повышающее надежность, но не сохраняющее состояние сборки мусора скрипта. Это неправильно. Надо: 1) сохранять состояние сборки мусора скрипта перед выполнением коллбека; 2) отключать сборку сборку мусора; 3) выполнять коллбек; 4) восстанавливать состояние сборки мусора.
В версии QUIK 12.2.2.8 (а, похоже, и в более ранних) при запуске любого коллбека отключается сборка мусора (collectgarbage('stop')), а после его отработки включается (collectgarbage('restart')). Это ошибка. Коллбеки не должны менять состояние уборки мусора после своего выполнения, а должны восстанавливать то, которое было перед их выполнением. ----- Разработчик скрипта может в какой-то момент отключить на какое то время уборку мусора (имеет право), а тут прилетает любой коллбек и все портит.
TGB написал: Коллбеки не должны менять состояние уборки мусора после своего выполнения, а должны восстанавливать то, которое было перед их выполнением.
TGB написал: В версии QUIK 12.2.2.8 (а, похоже, и в более ранних) при запуске любого коллбека отключается сборка мусора (collectgarbage('stop')), а после его отработки включается (collectgarbage('restart')). Это ошибка. Коллбеки не должны менять состояние уборки мусора после своего выполнения, а должны восстанавливать то, которое было перед их выполнением.
Вроде, написано понятно, но если непонятно, то демонстрирую кодом:
Код
local OK = false
function OnParam()
OK = true
end
function main()
_RUN_ = true
collectgarbage('stop') -- Сборка мусора отключена --
while _RUN_ do
if not collectgarbage('isrunning') then
if OnParam and OK then
message(' Ошибка обработки коллбеков устранена. '
.. 'В скрипте где то был остановлен сбор мусора. '
.. 'Вызов коллбека OnParam не меняет состояние сборки мусора', 3)
end
end
sleep(3000)
end
end
function OnStop()
_RUN_ = false
return 10000
end
Если код выполняется при подключенном к серверу QUIKе и при этом нет сообщения: "Ошибка обработки коллбеков устранена. ............" , то понятно, что коллбек OnParam включил сборку сборки мусора, отключенную разработчиком в скрипте.
TGB, В качестве гипотезы, могу предположить следующее: Возможно это связано с тем, что Вы проверяете сборщик в main. Дело в том, что main - это корутина. В корутине свое адресное пространство для локальных переменных. Колбек это основная VM там свое. По логике должны действовать два различных сборщика мусора. -------------------- Если мое предположение верно, то Вы пытаетесь останавливать один, а проверять другой. --------------------- Но я не поверял эту гипотезу, так как меня пока не волнует как работает сборщик в скрипте.
Сборщик мусора - это функция и она одна. Но запускать ее можно с различными стеками VMLua. ------------- Все функции выполняются в одном стеке. ------------------ Корутина main отличается тем, что для нее выделяется отдельный стенк из стека основной VM. ------------------ Если функцию например колбек вызвать, то ее переменные будут в глобальном стеке основной VMLua. -------------- Если Вы вызываете функцию в Main, то ее переменные будут в стеке корутины. ------------------------ сборщик мусора собирает мусор в в стеке основном VM. но при этом он не видит стек коррутины, так как это один объект в основном стеке. --------------- Но тогда кто освобождает пространство от переменных внутри main? Я полагаю что делается запуск сборщика для области стека корутины main. ---------------------
nikolz написал: сборщик мусора собирает мусор в в стеке основном VM.но при этом он не видит стек коррутины
Кто вам это рассказал или где вы это прочитали ?
Цитата
nikolz написал: Я полагаю что делается запуск сборщика для области стека корутины main.
Зачем что то предполагать, когда можно узнать точно как это устроено? И для этого на сайте разработчика существуют свободно доступные исходники Lua. Из них вы бы узнали, что управление памятью в Lua общее для всех его стеков и нет специального запуска сборщика для области стека корутины main. В конце концов, можно почитать книгу Р. Иерузалимски.
TGB написал: Надо: 1) сохранять состояние сборки мусора скрипта перед выполнением коллбека; 2) отключать сборку сборку мусора; 3) выполнять коллбек; 4) восстанавливать состояние сборки мусора.
Если это непонятно, то в переводе на коды это, примерно, следующее (три строки не считая комментариев и пробелов):
Код
--- Перед выполнением коллбека ---
local isrunning = collectgarbage('isrunning')
if isrunning then collectgarbage('stop') end
--- Выполнить коллбек ---
--- После выполнения коллбека ---
if isrunning then collectgarbage('restart') end
Посмотрите на вызов функций в API C В качестве параметра в функцию передается указатель на стек. Когда Вы вызываете сборщик мусора в main, то сборщику будет передан указатель на локальный стек корутины. Когда вы вызываете сборщик мусора в колбеке, то сборщику передается указатель на глобальный стек основной VM Lua. ---------------- Т е в каком стеке Вы вызовите сборщик, тот стек он и будет чистить.
nikolz написал: Т е в каком стеке Вы вызовите сборщик, тот стек он и будет чистить.
Цитата
TGB написал: В конце концов, можно почитать книгу Р. Иерузалимски
Вы умеете читать? Или только пишите ? Цитата из книги 32.2 Сборщик мусора: "Фаза очистки обходит все объекты Lua." Вы понимаете, что в Lua объектами являются и его стеки? В конце концов, детали сборки мусора вы можете посмотреть в исходниках Lua.
Т е в каком стеке Вы вызовите сборщик, тот стек он и будет чистить.
Динамическая память - это не стек. Или имелся ввиду поток? Ну мы же можем передавать объекты между потоками. Не может быть разделения по потокам для сборки мусора.
Цитата
Each Lua state has one or more threads, which correspond to independent, cooperative lines of execution. The type lua_State (despite its name) refers to a thread. (Indirectly, through the thread, it also refers to the Lua state associated to the thread.)
Т е в каком стеке Вы вызовите сборщик, тот стек он и будет чистить.
Динамическая память - это не стек. Или имелся ввиду поток? Ну мы же можем передавать объекты между потоками. Не может быть разделения по потокам для сборки мусора.
Цитата
Each Lua state has one or more threads, which correspond to independent, cooperative lines of execution. The type lua_State (despite its name) refers to a thread. (Indirectly, through the thread, it also refers to the Lua state associated to the thread.)
Возможно путаница в терминологии. Глобальным стеком я называю lua_State. ----------------------- VM Lua - это стековая машина. Когда мы создаем VM Lua, то для нее выделяется область State -------------------------- Т е для нее из кучи выделяется кусок, в котором все размещается. Если вызвать функцию библиотечную, то этой функции на СИ передается всего один параметр. Это указатель на State VM Lua. --------------------------- Все функции и все переменные как глобальные так и локальные размещаются в этом State. Т е этот State и есть та динамическая память, с которой работает сборщик мусора. --------------------------- когда создаем корутину, то для нее из области State VMLua выделяется кусок памяти, который будет State корутины. ----------------------- "coroutine.create (f)Создает новый сопроцесс, с телом f. fдолжен быть функцией. Возвращает этот новый сопроцесс, как объект с типом "thread". ----------------------
lua_State *lua_newthread (lua_State *L);
Создает новый поток, ложит его на стек и возвращает указатель на lua_State, который представляет этот новый поток.
Новый поток использует одно глобальное окружение с оригинальным потоком, но имеет независимый стек исполнения.
Не существует явной функции для закрытия или уничтожения потока. Потоки это субъект для сборки мусора, как и любой Lua объект
-----------------------------
Если посмотреть внутри основной программы и внутри main, то увидим, что в вызываемые в них функции передается различный указатель на State.
lua_State это, как написано в документации и в комментарии в исходниках, "per thread state". Структура, одно из полей которой - указатель на global_State. Этот global_State общий для всех потоков, и именно в нем лежит все, связанное с с управлением динамически выделяемой памятью, сборкой мусора и глобальными переменными. У каждого потока есть свой стек, но на стеке живут только локальные переменные - их время жизни до выхода из функции, не сборщик мусора их освобождает.
TGB написал: nikolz забыл вас спросить: вы понимаете почему разработчики QUIK при выполнении коллбеков останавливают сборку мусора?
Сборка мусора не потокобезопасна? Ванильный lua вообще то однопоточный, threads там - это просто короутины, которые выполняются по очереди в одном реальном потоке ос, с явным переключением coroutine.resume()/coroutine.yield() Для поддержки многопоточности надо как минимум задефайнить свои макросы lua_lock()/lua_unlock(). Но блокировка идет только на время работы интерпретатора (то есть просто читать/писать в переменные параллельно можно), а вызовы С функций не блокируются. В том числе и встроенные функции insert/remove/sort не блокируются. Откуда и растут ноги у sinsert/sremove/ssort. На время сборки мусора видимо тоже нет блокировки. Так что если она запустится одновременно в main и колбеке - будет жопа. Так что лучше не трогать collectgarbage('stop')/collectgarbage('restart'). Ведь если вы его на время выключаете, то наверное и включаете потом? А если в этот момент выполняется колбек - сборщик мусора может запуститься в нем.
Да, не потокобезопасна в ванильном режиме. Но разработчик Lua предусмотрел (и, возможно, напрасно) вариант использования инстанции Lua в нескольких потоках в режиме разделения, когда коды Lua могут быть использованы только одним потоком. Для этого варианта надо вносить изменения в исходники Lua, обеспечивающие синхронизацию разделения. С чем разработчик QUIK немало "покувыркался". Если исходить из того, что синхронизация сейчас реализована корректно, то перед выполнением коллбеков мусорщик можно было бы не останавливать. Отсутствие сборки мусора при выполнении коллбека обеспечивает: 1) сокращение времени его выполнения; 2) и что не менее важно, дополнительную страховку от возможных ошибок синхронизации, запрещая работу коллектора, который обрабатывает все стеки инстанции Lua. Но после выполнения коллбека должно быть восстановлено то состояние коллектора, которое было перед выполнением коллбека. Вообще то, я уже пару раз описывал, как можно изменить схему взаимодействия скриптов Lua с QUIK, чтобы уйти от "интересных" проблем синхронизации потоков. Более того это несколько бы ускорило выполнение скриптов (освобожденных от выполнения кода синхронизации).
Кажется, следует предполагать, что синхронизации в gc нет. И поэтому gc останавливают при вызове коллбеков. А также поэтому категорически нельзя в main вызывать collectgarbage('restart').