Из своей dll вызываю в цикле запрос getSecurityInfo " lua_getglobal(global_lua_state, "getSecurityInfo"); lua_pushstring(global_lua_state, class_code); lua_pushstring(global_lua_state, sec_code);lua_call(global_lua_state, 2, 1); "
собственно, по всем тикерам в одном цикле. И сейчас вдруг, иногда, в непредсказуемые моменты подвисает lua_call и, соответственно, подвешивает и сам Quik. Версия 5.3.5. Этом у меня тут что-то напутано или что еще может быть - куда копать ?
Разобрался, подвисало все иногда из-за включенной в dll обработке OnParam. Очевидно, в некоторые моменты происходит одновременные манипуляции со стэком в lua_State из-за чего и сбой. Тогда получается, что нужно в разных потоках обрабатывать свои команды и quik события ? Или еще какие варианты ?
Anton написал: Именно. Не надо сохранять стейт в глобальную переменную, в каждом колбеке (включая мейн) надо использовать тот стейт, что в эту функцию передан.
А как тогда правильно самому функции quik вызывать ? lua_newthread ?
Ну вообще-то длл это просто набор функций, своей жизнью она по дизайну не живет. Соответственно любая деятельность происходит в ответ на колбек, а в колбек стейт передается. Если заводится свой поток в длл, который будет дергать луа, нужно, да, создать для него стейт через lua_newthread, я показывал как-то как именно. Но тут имейте в виду, что ваш поток должен завершиться при любом раскладе (нажата кнопка стоп, завершился мейн по своей инициативе, квик прибил скрипт за ошибку) до того, как квик прибьет объект скрипта, иначе выхватите акцесс виолейшен и обвалите весь квик.
Anton написал: Если заводится свой поток в длл, который будет дергать луа, нужно, да, создать для него стейт через lua_newthread
А что, есть возможность без нового потока "дергать" lua_State ? Просто у меня общая dll и для связи с quik и для связи с моей прогой. И из своей проги просто отправлял команду в dll, где по WaitForMultipleObjects как раз через ранее сохраненный глобальный lua_State, dll отправляла команду в quik.
BVladimir написал: есть возможность без нового потока "дергать" lua_State ?
Конечно, вам уже квик создал поток для мейна и стейт для него (почти таким же куском кода, как по ссылке выше я показывал). WaitForMultipleObjects вполне может быть в мейне, и использовать для дерганья луа он должен стейт, переданный в мейн. Также можно завести событие (CreateEvent) и добавить его в список ожидаемых объектов в мейне, и в колбеке OnStop ставить его (SetEvent), мейн в этом случае пойдет на выход вместо очередной итерации ожидания. Может быть, как-то посигналив вашей проге сначала, что банкет окончен. А в обратную сторону, как понимаю, из колбеков, используйте стейты, передаваемые в эти колбеки. Есть еще вариант создать в мейне очередь сообщений и использовать виндовые сообщения вместо объектов синхронизации. Оно малость медленнее (несущественно), но может быть проще в некотором смысле, т.к. получается по сути готовый конечный автомат и запутаться в нем меньше шансов.
Anton написал: WaitForMultipleObjects вполне может быть в мейне
Не ожидал. Я как раз отдельный поток выделил - типа сервер где обрабатывается WaitForMultipleObjects. А если все в main, думал что quik зависнет в ожидании... Но в обычном порядке state в main попадает только один раз при запуске скрипта, как я понимаю ? А в callback-ах не тот де самый state приходит ?
BVladimir написал: Но в обычном порядке state в main попадает только один раз при запуске скрипта, как я понимаю ? А в callback-ах не тот де самый state приходит ?
Да, на входе в мейн вы получаете стейт отдельного потока и он жив, пока мейн не вернулся, то есть действительно стейт мейна передается один раз на входе (в мейн). Все остальные колбеки в текущей реализации получают другой стейт (для всех колбеков одного скрипта один и тот же), но тут никаких гарантий нет, что так будет всегда, лучше сразу предположить, что у каждого колбека может быть свой отдельный стейт и не создавать себе на будущее проблем.
Т.е. все команды шлются через global_lua_state полученный из main в момент запуска скрипта. И если убрать обработчика OnParam, то все работает, подключаю OnParam - начинает вешать quik, хотя там свой state должен быть...
Вот это надо убрать совершенно точно и внутри мейна использовать L напрямую. Уже убрав global_lua_state увидите, где еще этот стейт использовался. Затем, как понимаю, работал еще один поток, который дергал луа через global_lua_state, принадлежащий потоку мейна. На входе в любую lua_* функцию идет захват критической секции. Если вдруг она уже захвачена и мейн из своего потока еще раз пытается захватить, будет рекурсивный захват, что ок, а если мейн держит секцию и ее пытается захватить другой поток, будет дедлок. Возможно, отсюда ноги растут.
Побочно: некрасиво, чистим стек и return 1, т.е. "эй луа, тут на стеке один элемент возвращен". Хотя по идее луа должен бы это съесть, но может и не ест.
Anton написал: Если вдруг она уже захвачена и мейн из своего потока еще раз пытается захватить, будет рекурсивный захват, что ок, а если мейн держит секцию и ее пытается захватить другой поток, будет дедлок. Возможно, отсюда ноги растут.
Ну так пока не включен обработчик OnParam со своим стейтом, то все ок и за 1 сек. lua_call с этим global_lua_state успешно отрабатывает более 26 тыс.раз. Только включаю обработчика, то может зависнуть на 10 вызове, а может на 1000-м... Хотя стайты точно разные в main и в обработчике. Если в main стэйт уже пришел, то адрес его уже не меняется. Сейчас из main я его не трогаю он используется только из одного потока, типа моего эвент-сервера и все хорошо до подключения обработчика...Дедлока, по идее, не может быть !
BVladimir написал: Похоже, что разные стэйты не такие уж и независимые.
Вы их выведите в лог (printf("%p\n", state)) и посмотрите. Так-то они действительно не совсем независимые, второй произведен от первого и у них общее глобальное пространство. Но блочить это не должно.
Мне вот мысль пришла, а какой транспорт используется? Если пайп, там очень легко устроить дедлок, когда в две стороны передача идет. Т.к. пайп с сообщениями блокируется, когда вторая сторона не полностью прочитала сообщение. Если она в этот момент попытается что-то отправить, все встанет колом.
Anton написал: Мне вот мысль пришла, а какой транспорт используется?
На стороне приложения просто SetEvent (именной, ранее созданный), на стороне quik - сервер с бесконечным WaitForMultipleObject, сработал, выполнил функцию quik, данные в общую память и ответил приложению, там соответственно WaitForSingleObject принял ответ, данные забрал и по новой. Сбой только при включении OnParam...
Перенес все в main, передаю стэйт как параметр для вызова функции quik и все равно если перехват OnParam подключен в dll,то подвешивает quik в непредсказуемые моменты. В режиме отладки отловил тут акцесс виолэйшн в lua53.dll. Если OnParam не перехватывать, то все идеально. Что же еще может быть тогда ?
BVladimir написал: в новой версии появилась помимо lua54.lib еще и lua54.dll
Как это появилась. Собственно luaXX.dll это и есть луа в составе квика. Вот тут возникает вопрос, а откуда у вас была lua53.lib, может это был весь луа в виде статической библиотеки, а не просто список экспорта от длл, как должно быть?
BVladimir, сдается мне, вы собирали весь луа вместе со своим кодом (и в версии 5.3 у вас собирался статический луа, а в 5.4 динамический, поэтому получилась длл и более-менее правильная библиотека экспорта). От луа нужны только три заголовка luaconf.h, lua.h, lauxlib.h, все остальное включать в проект не надо, линковаться надо с luaXX.lib, полученной из luaXX.dll из состава квика с помощью dumpbin.
BVladimir написал: как тогда с 5.4.x правильно собираться, по пунктам
Ну давайте здесь напишем ) Для любой версии луа 1) качаете с lua.org нужную версию луа в зипе 2) из зипа берете lua.h, luaconf.h, lauxlib.h, остальной зип можно выбросить 3) в папке с квиком создаете файл makelib.bat со следующим содержимым
Код
@echo off
setlocal enabledelayedexpansion
for /f "tokens=1-4" %%1 in ('dumpbin /exports %1') do (
set /a ordinal=%%1 2>nul
set /a hint=0x%%2 2>nul
set /a rva=0x%%3 2>nul
if !ordinal! equ %%1 if !hint! equ 0x%%2 if !rva! equ 0x%%3 set exports=!exports! /export:%%4
)
for /f %%i in ("%1") do set dllpath=%%~dpni
start lib /out:%dllpath%.lib /machine:x64 /def: %exports%
4) запускаете Visual Studio Command Prompt x64 release, переходите в папку с квиком, выполняете команду (скажем, для луа 5.3)
Код
makelib lua53.dll
5) в папке квика появляются файлы lua53.lib и lua53.exp. Файл .exp выбрасываете, он нам не нужен 6) в папку своего проекта копируете файлы lua.h, luaconf.h, lauxlib.h и полученный luaXX.dll 7) в своем проекте инклюдите как обычно