|
Luajit неочевидные особенности. |
|
|
DarkMaster |
8.6.2024, 9:28
|
          
Модератор UOPilot
Сообщений: 9.735
Регистрация: 2.12.2008 Группа: Супермодераторы Наличность: 29635
Пользователь №: 11.279

|
Тема будет оченя вялотекущая, тем не менее иногда сталкиваюсь с неожиданным поведением среды. Подобные заметки могут быть кому-то полезны, например, мне, склероз не дремлет. Код local kernel32 = ffi.load("kernel32") local threadId = ffi.new("DWORD[1]") local threadHandle = kernel32.CreateThread(nil, 0, ffi.cast("THREAD_START_ROUTINE", async), ffi.cast("LPVOID", HintStruct), 0, threadId) Вызов kernel32.CreateThread и C.CreateThread на самом деле не эквивалентен. В частности в этом случае вызов через C.CreateThread шансово приводит к крашу. Причины я не знаю, но факт на лицо. В качесте домыслов через C.CreateThread подтягивается функция принадлежащая самому lua, а не конкретной либе. Zerobane отладка. Код while 1 do local a = false if a then print("a == true") end end Очевидно, что цикл будет крутиться вечно, а условие if a then никогда не выполнится. Ставим brake, меняем через отладчик значение a = true. Условие if a then все равно не будет выполнятся. При том, что а действительно будет равна true. Дело в том, что jit создал bypass, т.к. посчитал, что a константа и никогда не будет изменена. Фактически он никогда и не проверял ее значение, а просто делал безусловный переход пропуская блок if end. В качестве решения можно использовать конструкцию: Код local f = function(new_var) end do local var = false f = function(new_var) if new_var then var = new_var end return var end end
while 1 do local a = f(false) if a then print("a == true") end end Данная заглушка позволит выполнить отладку с минимальными вмешательствами в код. Сообщение отредактировал DarkMaster - 8.6.2024, 9:29
--------------------
Скрипты UOPilot под заказ. Консультации по UOpilot 15$/час. Услуги Lua разработчика (не пилот, проекты, постоянка) Disсоrd: Kov____
|
|
|
|
|
  |
Ответов
DarkMaster |
25.6.2024, 1:46
|
          
Модератор UOPilot
Сообщений: 9.735
Регистрация: 2.12.2008 Группа: Супермодераторы Наличность: 29635
Пользователь №: 11.279

|
Запускаем функцию в отдельном потоке и lua state. Код lua_state, func_pointer, thread_id = func_to_new_thread_and_state(my_function, data_to_function) lua_state - новый созданный lua_state func_pointer - указатель на функцию внутри нового lua_state. thread_id - id потока. my_function - функция либо дамп функци (string.dump(func)) для запуска. Ваша функция не должна ссыласться на upvalue. data_to_function - C указатель на данные которые будут переданы в функцию. Необходимо четко понимать, что lua_state полностью новый экземпляр lua. Никакие ранее объявленные переменные, подгруженные модули через require или ffi.load, глобальные переменные и т.д. не будут доступны в новом экземпляре (state). Если что-то нужно передать, то сделать это можно только в виде передачи указателя на данные. Передавать можно соответственно только C данные. Передача в качестве данных для функции в виде типов lua либо функций lua в новый поток недопустимы - lua не является потоко-безопасным - вы получите краш. При завершении основного потока (скрипта), новый поток так же будет завершен автоматически. func_to_new_thread_and_state.lua
Код local ffi = require("ffi") local C = ffi.C
ffi.cdef[[ typedef struct lua_State lua_State; lua_State *luaL_newstate(void); void luaL_openlibs(lua_State *L); int luaL_loadbuffer(lua_State *L, const char *buff, size_t sz, const char *name); int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc); const char *lua_tolstring(lua_State *L, int idx, size_t *len); unsigned long long int strtoull(const char* str, char** endptr, int base);
typedef int BOOL; typedef void* HANDLE; typedef void* LPVOID; typedef unsigned int DWORD; typedef int (__stdcall *THREAD_START_ROUTINE)(LPVOID lpThreadParameter); HANDLE __stdcall CreateThread( void* lpThreadAttributes, size_t dwStackSize, THREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, DWORD* lpThreadId ); BOOL __stdcall CloseHandle( HANDLE hObject ); void Sleep(unsigned int dwMilliseconds); ]]
local kernel32 = ffi.load("kernel32")
-- Входящие данные подразумеваются, как -- 32 битный или 64 битный указатель, -- т.е. вида: -- 0x12345678 либо 0x123456789abcdef -- префикс 0x не удалять! -- Основная задача данного кода - -- хранение полученных указателей. -- Встроенный tonumber невозможно -- корректно использовать, т.к. -- он возвращает double и произойдет -- потеря точности при получении x64. -- Подключать msvcrt ради _strtoui64 -- имхо перебор, а пачка вызовов -- tonumber существенно медленнее. local char_arr_to_int64 = function(str)end do ffi.cdef[[ typedef unsigned int size_t; size_t strlen(const char *s); ]] local char_values = { [48] = 0x0, -- Значение символа '0' [49] = 0x1, -- Значение символа '1' [50] = 0x2, -- Значение символа '2' [51] = 0x3, -- Значение символа '3' [52] = 0x4, -- Значение символа '4' [53] = 0x5, -- Значение символа '5' [54] = 0x6, -- Значение символа '6' [55] = 0x7, -- Значение символа '7' [56] = 0x8, -- Значение символа '8' [57] = 0x9, -- Значение символа '9' [65] = 0xA, -- Значение символа 'A' [66] = 0xB, -- Значение символа 'B' [67] = 0xC, -- Значение символа 'C' [68] = 0xD, -- Значение символа 'D' [69] = 0xE, -- Значение символа 'E' [70] = 0xF, -- Значение символа 'F' [97] = 0xA, -- Значение символа 'a' [98] = 0xB, -- Значение символа 'b' [99] = 0xC, -- Значение символа 'c' [100] = 0xD, -- Значение символа 'd' [101] = 0xE, -- Значение символа 'e' [102] = 0xF, -- Значение символа 'f' } char_arr_to_int64 = function(str) local len = C.strlen(str) local char_array = ffi.new("unsigned char[8]")
-- 18 символов (64 бита) if len == 18 then for i = 0, 7 do char_array[i] = char_values[str[16-i*2]]*0x10 + char_values[str[17-i*2]] end else -- 32 бита for i = 0, 3 do char_array[i] = char_values[str[8-i*2]]*0x10 + char_values[str[9-i*2]] end end
-- Получаем 64-битное целое число из массива unsigned char local ptr_uint64_t = ffi.cast("unsigned long long*", char_array)
return ptr_uint64_t[0] end end
local func_to_new_thread_and_state = function(f, p_opt)end do local init = string.dump( function(f) local ffi = require "ffi" ffi.cdef[[ typedef void* LPVOID; typedef int (__stdcall *THREAD_START_ROUTINE)(LPVOID lpThreadParameter); ]] f = tostring(ffi.cast("THREAD_START_ROUTINE", f)):gsub("^.-0x", "0x", 1) return f end )
func_to_new_thread_and_state = function(f, p_opt) local lua_state = C.luaL_newstate() C.luaL_openlibs(lua_state)
-- Передача функции возможна только в виде дампа. if type(f) == "function" then f = string.dump(f) end
C.luaL_loadbuffer(lua_state, init, #init, "=init") C.luaL_loadbuffer(lua_state, f, #f, "=f") C.lua_pcall(lua_state, 1, 1, 0)
local raw = C.lua_tolstring(lua_state, -1, nil) local pllu = char_arr_to_int64(raw)
f = ffi.cast("THREAD_START_ROUTINE", pllu)
local threadId = ffi.new("DWORD[1]") local threadHandle = kernel32.CreateThread(nil, 0, f, ffi.cast("LPVOID", p_opt), 0, threadId) kernel32.CloseHandle(threadHandle)
return lua_state, f, threadId[0] end end
return func_to_new_thread_and_state
пример вызова
Код local ffi = require("ffi") local C = ffi.C ffi.cdef[[ void lua_close (lua_State *L); ]]
-- Подгружаем модуль. local func_to_new_thread_and_state = require"func_to_new_thread_and_state"
-- Пример функции передаваемой в новый поток. local my_function = function(data_pointer) -- Все нужные require должны -- быть явно вызваны заново. local ffi = require("ffi") local C = ffi.C
-- cdef так же должны быть объявлены заново. -- Виндовый wait. Просто для примера. ffi.cdef[[ void Sleep(unsigned int dwMilliseconds); ]]
-- Приводим указатель к типу. -- Если передается указатель на структуру, -- то структура так же явно должна быть -- объявлена повторно в cdef. local data = ffi.cast("int*", data_pointer)
local f = io.open("d:\\1.txt", "wb") local timeout = os.clock() + 1 while timeout > os.clock() do C.Sleep(100) f:write(tostring(data[0]), "\r\n") end f:close() return 0 end
-- Объявляем C массив int на 1 элемент, -- присваиваем значение элементу равное 123. local data_to_function = ffi.new("int[1]", 123)
-- Пример вызова функции local lua_state, func_pointer, thread_id = func_to_new_thread_and_state(my_function, data_to_function)
-- Верент lua_state, ссылку на функцию новом потоке, id потока. print(lua_state, func_pointer, thread_id)
-- Пример дальнейшего кода который -- будет выполняться в основном потоке -- без ожидания завершения выполнения -- переданной функции. local timeout = os.clock() + 2 while timeout > os.clock() do C.Sleep(100) print"1" end
-- Завершаем существование lua_state, -- если он нам больше не нужен. C.lua_close(lua_state)
Сообщение отредактировал DarkMaster - 25.6.2024, 4:32
--------------------
Скрипты UOPilot под заказ. Консультации по UOpilot 15$/час. Услуги Lua разработчика (не пилот, проекты, постоянка) Disсоrd: Kov____
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|