El El (Автор тем)

Выбрать дату в календареВыбрать дату в календаре

Страницы: 1
Nil в полях таблицы classes
 
При вызове getItem для получения записей таблицы classes поля class_code, npars, nsecs могут содержать nil, что противоречит описанию. В описании таблицы "Классы" поле называется class_code, в примере этой же страницы - class.
Подвешивание info.exe через механизм межпоточного локинга
 
Цитата
tid = 0

events = {}
mp_counter = 0
qp_counter = 0

-- Predicates to be called from two threads
-- To hang Quik we will try to collide QLUA functions
-- Risk factors: we're using heavily CriticalSection (through ssort) and the events are fast enough

-- Predicate for script's dedicated thread
function main_predicate(a, b)
  mp_counter = mp_counter + 1;
  if ((mp_counter % 7) == 0) then Clear(tid) end
  SetWindowCaption(tid, "Just called script thread predicate " .. mp_counter .. " times")
  local n = InsertRow(tid, -1)
  SetCell(tid, n, 1, "Compared " .. a .. " and " .. b .. " with script thread predicate", 0);
  return b > a
end

-- Predicate for Quik's main thread
function quik_predicate(a, b)
  qp_counter = qp_counter + 1;
  if ((qp_counter % 13) == 0) then Clear(tid) end
  local n = InsertRow(tid, -1)
  SetCell(tid, n, 1, "Compared " .. a .. " and " .. b .. " with main thread predicate", 0);
  SetWindowCaption(tid, "Just called quik thread predicate " .. qp_counter .. " times")
  return a < b
end

function main()
  Subscribe_Level_II_Quotes("QJSIM", "LKOH");
  Subscribe_Level_II_Quotes("QJSIM", "SBER");
  Subscribe_Level_II_Quotes("QJSIM", "GMKN");
  Subscribe_Level_II_Quotes("QJSIM", "SBERP");
  Subscribe_Level_II_Quotes("QJSIM", "MGNT");
  Subscribe_Level_II_Quotes("QJSIM", "ROSN");
  Subscribe_Level_II_Quotes("QJSIM", "AFLT");
  Subscribe_Level_II_Quotes("QJSIM", "BANE");
  Subscribe_Level_II_Quotes("QJSIM", "MTLR");
  Subscribe_Level_II_Quotes("QJSIM", "BANEP");

  tid = AllocTable()
  AddColumn(tid, 1, "Event", true, QTABLE_STRING_TYPE, 80)
  CreateWindow(tid)
  local cycles = 0;
  while true do
     sleep(1)
     cycles = cycles + 1;
     if (cycles == 100) then
        table.ssort(events, main_predicate)
        if (table.getn(events) > 0) then
           local n = InsertRow(tid, -1)
           SetCell(tid, n, 1, "First item after sorting in main is: " .. events[1], 0)
        end
     end
  end
end

function OnQuote(class_code, sec_code)
  local q = getQuoteLevel2(class_code, sec_code)
  if ((q ~= nil) and (tonumber(q.bid_count) > 0)) then
     table.sinsert(events, q.bid[1].price)
     table.ssort(events, quik_predicate)
     if (table.getn(events) > 0) then
        local n = InsertRow(tid, -1)
        SetCell(tid, n, 1, "First item after sorting in main is: " .. events[1], 0)
     end
  end
end
В течение первой пары минут работы скрипт подвесит терминал. Придумывалось, как тестовый скрипт для иллюстрации этой проблемы (https://forum.quik.ru/forum10/topic3206/) на чистом Lua, но может быть связан и просто со слишком частым main.
ssort не проверяет первый аргумент и вешает терминал
 
Функция table.ssort (в отличие от родного sort) не проверяет первый аргумент. Если его не задать вообще, вешает терминал. Привожу пример скрипта :)
Цитата
function main
 table.ssort()
end
Синхронизация между потоком main и потоком, вызывающим OnXxx
 
Quik исполняет Lua скрипт на двух потоках. То, что внутри скриптовой функции main, исполняется на выделенном треде. Коллбеки OnXxxx исполняются на основном потоке Quik. Задача - гарантировать правильность последовательности вычислений, которые происходят внутри main и коллбеков. То есть, чтоб когда случился, например OnQuote, была бы гарантия, что исполняющийся параллельно main закончил свою часть вычислений и общие данные были бы в готовом состоянии. Примерный скрипт такой:
Код
data = {};

function update_ui()
   Clear(table_id)
   for i,v = in ipairs(data) do
      InsertRow(table_id, ...)
   end
end

function calculate_data_item(v)
   -- Calculate new value based on v
   -- Long, thread unsafe operation in real code, depends on other data[..] items
   -- Adding 1 here only as example
   return v + 1;
end

function update_all_data_items()
   for i,v = in ipairs(data) do
      data[i] = calculate_data_item(v);
   end
end

function main()
   -- Request level 2 quotes for each security
   -- Init table, columns ... 
   while true do
      -- START atomic operation on data array
      update_all_data_items()
      update_ui()
      -- End atomic operation
      sleep(1000)   
   end
end

function OnQuote(p1, p2)
   -- START atomic operation on data[p2]
   data[p2] = calculate_data_item(...)
   update_ui()
   -- End atomic operation on data[p2]
end

Там где в коментарии START - должна начинаться часть эксклюзивного кода, там где End - заканчиваться. Если организовать ожидание одним потоком пока завершит вычисления другой, то удается добиться полной корректности. Однако, некоторые функции Qlua (по всей видимости связанные с UI) блокируют исполнение, пока им не ответит основной тред Quik. В примере вызывается InsertRow и он внутри реализован так, что будет блокировать исполнение, пока ему не ответит основной тред Quik.

Происходит следующее:
1. main в точке START эксклюзивно запирает исполнение
2. Случается OnQuote и блокирует ожиданием основной поток Quik, пока исполнение заперто
3. В то же время main продолжает испололнение, благополучно завершает вычисление данных, подходит к вызову InsertRow
4. Внутри InsertRow происходит нечто, блокирующе зависящее от основного потока Quik
5. Основной поток Quik не откликается, потому что он в пункте 2 и ждет пока замок будет разлочен в main
6. main не может дойти до точки End, где замок разлочивается, потому что он ждет, пока InsertRow вернет исполнение

Получается, что внутри синхронизированного кода нельзя использовать ряд Qlua функций.
Вопросы:
1. Почему так происходит: InsertRow и подобные используют блокирующую отправку сообщений Windows (типа SendMessage вместо PostMessage) или это из-за логики локинга Quik? Понимание причины помогло бы придумать обходную схему
2. Какие функции поименно обладают таким же поведением как InsertRow? Понимание этого помогло бы писать скрипты так, чтоб хотя бы просто не натыкаться на эту проблему (не использовать внутри синхронизированного кода)
Когда Unsubscribe_Level_II_Quotes(...) == false?
 
В каких технических случаях Unsubscribe_Level_II_Quotes(...) возвращает false, если вызван после заведомо IsSubscribed_level_II_Quotes(...) == true?
DataSource.Close()
 
По документации метод Close() возвращает значение типа BOOLEAN. У меня после вызова Close на стеке лежит nil, это ошибка у меня или в документации?
Расширение потокобезопасных функций в QLUA
 
В документации QLUA описано 8 потокобезопасных функций, которые выполняют конкретные действия после  выполнения действий по синхронизации main/callback-потоков. Предложение: добавить функцию Synchronized, которая бы исполняла бы только действия по синхронизации потоков, после чего выполняла бы внутри указанную пользователем функцию, не выполняя никаких других заранее определенных конкретных действий. Сейчас этого можно добиться используя "не по назначению" текущие потокобезопасные функции.
Порядок создания callback-обработчиков
 
В какой момент должны быть созданы обработчики callback-ов, чтобы они вызывались? Экспериментально, если создавать некоторые функции обработчиков (например, main) внутри OnInit, то они работают, некоторые (например, OnQuote) - не работают. Возможно, адрес каких-то обработчикаов кешируется в какой-то момент и его последующие изменения в адресах функцииLua не учитываются?
Страницы: 1
Наверх