Вопрос к разработчикам: можно ли вызывать getQuoteLevel2 в потоке main?
Пользователь
Сообщений: Регистрация: 27.12.2022
01.03.2026 21:13:49
В длл на Си сидят обработчики OnQuote и функция main. Чтобы не грузить поток Квика, хочется вызывать тяжёлую функцию getQuoteLevel2 в main, передавая ей через очередь событий, организованную в длл, тикер и его класс из обработчика события OnQuote. Не получится ли гонка между потоками, когда main будет вызывать getQuoteLevel2 через Lua state L static int forLua_main(lua_State *L) и когда какой-нибудь обработчик вызовется из Квика (в другом потоке) через этот же L static int forLua_OnQuote(lua_State *L) ведь в Квике L используется всегда один и тот же? Или нет, поясните, как создаётся и используется это состояние L.
Пользователь
Сообщений: Регистрация: 27.12.2022
01.03.2026 21:20:43
Вот чем меня пытается пугать большая языковая модель, обученная Гуглом:
Цитата
Почему main — это "ловушка" для getQuoteLevel2? Хотя потоки разные, экземпляр lua_State* L у них общий. Если вы одновременно вызовете функцию Lua API (например, getQuoteLevel2) из потока main в тот момент, когда поток колбэков записывает данные в стек через OnAllTrade, вы получите Race Condition (состояние гонки). Это приведет к повреждению внутренних структур Lua и падению терминала (Access Violation). Чтобы это работало, разработчики QUIK используют внутренний мьютекс (Lock). Когда выполняется колбэк, main "засыпает", и наоборот. То есть параллельности на уровне Lua-движка всё равно нет — они работают по очереди.
Что-то я сомневаюсь насчёт существования этого мьютекса и засыпания этих потоков по очереди...
Пользователь
Сообщений: Регистрация: 27.12.2022
01.03.2026 21:26:10
Вот ещё цитата от гугловской модели, прокомментируйте её:
Цитата
Как QUIK управляет потоками (факты)
Параллельность есть: Поток main и поток колбэков — это разные системные потоки. Они могут работать одновременно на разных ядрах процессора.
Гонка за L: Если ваш main вызывает getQuoteLevel2 (через API Lua), а в это же время Квик пытается запушить сделку в OnAllTrade, они оба обращаются к полю L->top. Без синхронизации это 100% краш.
Кто держит замок: Внутри qlua.dll (или самого терминала) вызовы к Lua API почти всегда обернуты в критическую секцию. Когда Квик вызывает ваш колбэк, он «захватывает» состояние L. Если в этот момент main хочет вызвать getQuoteLevel2, он будет ждать на уровне WinAPI (переключение контекста).
Пользователь
Сообщений: Регистрация: 27.12.2022
01.03.2026 21:35:39
И ещё вопрос: если в моей длл вызываются функции Квика через стек Луа (напр., main вызывет getQuoteLevel2), то правильно ли будет перед выходом из моего обработчика события (соответственно, после вызова из main getQuoteLevel2) быстро очистить стек с помощью
Код
lua_settop(L, 0);
Или надо перед вызовом функции Луа из длл сначала делать
Код
n = lua_gettop(L);
И после вызова (перед выходом из обработчика события) делать
Код
lua_settop(L, n);
?
Пользователь
Сообщений: Регистрация: 30.01.2015
02.03.2026 07:15:13
Цитата
Serge123 написал: И ещё вопрос: если в моей длл вызываются функции Квика через стек Луа (напр., main вызывет getQuoteLevel2), то правильно ли будет перед выходом из моего обработчика события (соответственно, после вызова из main getQuoteLevel2) быстро очистить стек с помощью
Код
lua_settop(L, 0 );
Или надо перед вызовом функции Луа из длл сначала делать
Код
n = lua_gettop(L);
И после вызова (перед выходом из обработчика события) делать
Код
lua_settop(L, n);
?
Для любого языка программирования правило входа и выхода функций , при использовании стека для передачи параметров , одно и оно указано в любом учебнике по программированию. Состояние стека после выхода из функции должно быть таким же как до входа в функцию. Иначе будет утечка емкости стека.
Запрета на вызов getQuoteLevel2 в функции main() нет. Каких-либо комментариев по реализации своего собственного скрипта или dll дать не сможем.
Пользователь
Сообщений: Регистрация: 27.12.2022
02.03.2026 23:55:43
Такое впечатление, что Квик в разных потоках может давать разный Lua State L... Ладно, завтра выведу адрес этого L в разных потоках и сравню.
Пользователь
Сообщений: Регистрация: 30.01.2015
03.03.2026 05:11:57
Цитата
Serge123 написал: Такое впечатление, что Квик в разных потоках может давать разный Lua State L... Ладно, завтра выведу адрес этого L в разных потоках и сравню.
Вы правы. Это так. Я об этом рассказывал на форуме.
Пользователь
Сообщений: Регистрация: 27.12.2022
03.03.2026 21:12:22
Причём, Квик во все таблицы Lua State прилежно копирует таблицу _G, а вот регистр не копирует (или копирует не всегда). В одной функции запишешь в регистр ссылку на глобальную функцию:
lua_State - это структура т е область памяти и она одна для VMLua.
Код
struct lua_State {
CommonHeader;
unsigned short nci; /* количество элементов в списке 'ci' */
lu_byte status;
StkId top; /* первый свободный слот в стеке */
global_State *l_G;
CallInfo *ci; /* информация о вызове текущей функции */
const Instruction *oldpc; /* последний отслеженный ПК */
StkId stack_last; /* последний свободный слот в стеке */
StkId stack; /* основание стека */
UpVal *openupval; /* список открытых значений в этом стеке */
GCObject *gclist;
struct lua_State *twups; /* список потоков с открытыми значениями */
struct lua_longjmp *errorJmp; /* текущая точка восстановления после ошибки */
CallInfo base_ci; /* CallInfo для первого уровня (вызов Lua из C) */
volatile lua_Hook hook;
ptrdiff_t errfunc; /* текущая функция обработки ошибок (индекс в стеке) */
int stacksize;
int basehookcount;
int hookcount;
unsigned short nny; /* количество невызываемых функций в стеке */
unsigned short nCcalls; /* количество вложенных вызовов C */
l_signalT маска подключения;
lu_byte allowhook;
};
линкер помещает туда адрес в т числе и указатель global_State *l_G; на глобальную таблицу.
Код
typedef struct global_State {
lua_Alloc frealloc; /* функция для перераспределения памяти */
void *ud; /* вспомогательные данные для 'frealloc' */
l_mem totalbytes; /* количество выделенных в данный момент байтов - GCdebt */
l_mem GCdebt; /* выделенные байты, которые еще не компенсированы сборщиком мусора */
lu_mem GCmemtrav; /* память, обработанная сборщиком мусора */
lu_mem GCestimate; /* оценка объема используемой памяти, не являющейся «мусором» */
stringtable strt; /* хеш-таблица для строк */
TValue l_registry;
unsigned int seed; /* случайное начальное значение для хеширования */
lu_byte currentwhite;
lu_byte gcstate; /* состояние сборщика мусора */
lu_byte gckind; /* тип запущенного сборщика мусора */
lu_byte gcrunning; /* true, если запущен сборщик мусора */
GCObject *allgc; /* список всех объектов, подлежащих сборке */
GCObject **sweepgc; /* текущая позиция в списке */
GCObject *finobj; /* список собираемых объектов с финализаторами */
GCObject *gray; /* список «серых» объектов */
GCObject *grayagain; /* список объектов, которые нужно пройти атомарно */
GCObject *weak; /* список таблиц со слабыми значениями */
GCObject *ephemeron; /* список эфемерных таблиц (слабых ключей) */
GCObject *allweak; /* список всех слабых таблиц */
GCObject *tobefnz; /* список пользовательских данных, подлежащих сборке мусора */
GCObject *fixedgc; /* список объектов, которые не подлежат сборке */
struct lua_State *twups; /* список потоков с открытыми upvalues */
unsigned int gcfinnum; /* количество финализаторов, которые нужно вызвать на каждом этапе сборки мусора */
int gcpause; /* размер паузы между последовательными сборками мусора */
int gcstepmul; /* «степень детализации» сборки мусора */
lua_CFunction panic; /* вызывается при незащищенных ошибках */
struct lua_State *mainthread;
const lua_Number *version; /* указатель на номер версии */
TString *memerrmsg; /* сообщение об ошибке при обращении к памяти */
TString *tmname[TM_N]; /* массив с именами методов тегов */
struct Таблица *mt[LUA_NUMTAGS]; /* метатаблицы для базовых типов */
TString *strcache[STRCACHE_N][STRCACHE_M]; /* кэш для строк в API */
} global_State;
В функцию на С передается указатель на это структуру
Пользователь
Сообщений: Регистрация: 20.03.2023
04.03.2026 08:42:24
lua_State свой а каждый поток (в main и колбеках разный), а global_State один на VM (на скрипт).
Цитата
Each Lua state has one or more threads, which correspond to independent, cooperative lines of execution. The type (despite its name) refers to a thread. (Indirectly, through the thread, it also refers to the Lua state associated to the thread.)
Пользователь
Сообщений: Регистрация: 27.12.2022
04.03.2026 15:18:37
Цитата
paluke написал: lua_State свой а каждый поток (в main и колбеках разный), а global_State один на VM (на скрипт).
Видимо, имелось в виду "на каждый поток". Я правильно понимаю, что registry одна? Как было процитировано в #10:
это и есть та самая registry. Раз l_registry сидит в global_State, то registry должна быть одна на все потоки (main, OnAllTrade, OnQuote, ...)? А каждый lua_State имеет доступ к registry через ссылку *l_G
Код
struct lua_State {
...
global_State *l_G;
...
?
Пользователь
Сообщений: Регистрация: 27.12.2022
04.03.2026 15:23:20
Поэтому в одном потоке (в обработчике OnAllTrade) можно "запечь" ссылку в registry, взяв её с вершины стека
Код
luaL_ref(L, LUA_REGISTRYINDEX)
А в другом потоке (main) registry будет содержать то, что туда "запёк" OnAllTrade?
Пользователь
Сообщений: Регистрация: 30.01.2015
04.03.2026 16:12:13
Цитата
Serge123 написал: Поэтому в одном потоке (в обработчике OnAllTrade) можно "запечь" ссылку в registry, взяв её с вершины стека
Код
luaL_ref(L, LUA_REGISTRYINDEX)
А в другом потоке (main) registry будет содержать то, что туда "запёк" OnAllTrade?
Поясню. В скриптах реализован такой механизм. Основной скрипт - это VMLua, a main - это корутина, которая создается в новом потоке Для корутины создается state внутри state основной VMLua. В итоге пространство основной VMLua доступно внутри корутины. Это библиотеки переменные и функции в т числе колбеки.
Пользователь
Сообщений: Регистрация: 27.12.2022
04.03.2026 19:42:53
Кстати, что делает getQuoteLevel2Ex в qlua.dll, а также в _G? Наверно, эта недокументированная функция выводит стакан в более удобоваримой форме, чем getQuoteLevel2? Пора создать такую функцию и задокументировать её.