Как грамотно сделать функцию OnStop() последней в теле скрипта?

Страницы: 1
RSS
Как грамотно сделать функцию OnStop() последней в теле скрипта?, Скудное описание функции обратного вызова OnStop() в официальной документации
 
Хочу, чтобы функция OnStop() прерывала работу скрипта и выполнялась последней после нажатия кнопки "Остановить", но никак не могу этого добиться.
В штатной документации написано, что если добавить оператор return с количеством миллисекунд то это определит "время жизни" функции main() после завершения функции OnStop()

Сказано, что оператору return следует указать число.

"0" - это число? Указываю return 0

Пытаюсь запускать вот такой простенький код:

Код
Stopped = false

-- для имени файла отладки
File_Name_Log_Debugger = "Log_test_OnStop.txt"

function OnStop()      

   Stopped = true   
   File_Log_Debugger:close() -- закрывается файл отладчика   

return 0   
end                  -- конец функции обратного вызова OnStop()

function main()

-- Открывается отладочный файл лога
   File_Log_Debugger = io.open(File_Name_Log_Debugger, "w")

   while not Stopped do   
   
      if File_Log_Debugger and tostring(File_Log_Debugger) ~= "file (closed)" then
         File_Log_Debugger:write("Тестовая строка\n")
         File_Log_Debugger:flush()
      end -- end if

      sleep(1)
   end -- end while конец обработки цикла ожидания робота выход по останову   
   
end -- Конец функции main()

Иногда это работает. Но чаще всего, после нажатия кнопки "Остановить" скрипт, вроде бы, штатно останавливается - но затем через пять-семь минут при нажатии на любой пункт меню QUIK намертво зависает.

Иногда выбрасывает такую ошибку:



То есть, не смотря на указание return 0 - управление все равно возвращается в main() и там чего-то и сколько-то пытается выполняться...

Впрочем, если я пробую в функции OnStop() использовать оператор return с любым допустимым значением - то рано или поздно QUIK все-равно зависает. Без этого оператора "чудес" при остановке одинакового набора скриптов не возникает.

Смена версии Lua с 5.3.5 на 5.4.1 ничего в ситуации не меняет. QUIK от брокера ВТБ версия 9.2.3.15


Буду благодарен, если кто-нибудь поделится опытом использования в скриптах функции OnStop(), который будет чуть шире скудного описания авторов:


Цитата
Функция вызывается терминалом QUIK при остановке скрипта из диалога управления и при закрытии терминала QUIK.

Формат вызова:

[NUMBER time_out] OnStop(NUMBER flag)

Функция возвращает количество миллисекунд, которое дается скрипту на завершение работы. Если функция не возвращает число, то таймаут завершения работы скрипта остается равным 5 секундам.

По истечении интервала времени, данного скрипту на завершение работы, функция main() завершается принудительно. При этом возможна потеря системных ресурсов.
При остановке или удалении работающего скрипта Lua из диалога управления «Доступные скрипты» параметр вызова flag принимает значение «1». При закрытии терминала QUIK – значение «2».

Особенно, вызывает недоумение фраза о принудительном завершении функции main()

Вставьте в функцию main() оператор while true do end и никакого принудительного завершения не произойдет. QUIK повиснет намертво из-за бесконечного цикла в main()
Существующее положение вещей никак нельзя назвать принудительным завершением. Это QUIK принудительно завершается из окна дисптечера задач Windows, вот это действительно принудительное завершение.
 
Цитата
Денис написал:
Вставьте в функцию main() оператор while true do end и никакого принудительного завершения не произойдет. QUIK повиснет намертво из-за бесконечного цикла в main()
А вставьте оператор while true do sleep(0) end и принудительное завершение таки произойдет. Чудеса да и только.

Цитата
Денис написал:
поделится опытом использования в скриптах функции OnStop()
Уже делился, там дальше по ветке разбросано несколько вопросов-ответов, почему так и зачем так. И в целом можно по форуму поискать всякого разного, как там потоки переключаются и прочая. В частности, вышеименованные чудеса перестанут таковыми быть.
 
Цитата
Anton написал:
Цитата
Денис написал:
Вставьте в функцию main() оператор while true do end и никакого принудительного завершения не произойдет. QUIK повиснет намертво из-за бесконечного цикла в main()
А вставьте оператор while true do sleep(0) end и принудительное завершение таки произойдет. Чудеса да и только.

Здравствуйте, уважаемый Anton!

Большое спасибо, что уделили время моему горю.
sleep(0) - я плакаль... Утирая слезу пытался понять, как это я догадался написать sleep(1) и не догадался про sleep(0)
Вот если бы авторы этот пассаж в штатную документацию тиснули - было бы просто здорово!

Цитата
Anton написал:
Цитата
Денис написал:
поделится опытом использования в скриптах функции OnStop()
Уже  делился , там дальше по ветке разбросано несколько вопросов-ответов, почему так и зачем так. И в целом можно по форуму поискать всякого разного, как там потоки переключаются и прочая. В частности, вышеименованные чудеса перестанут таковыми быть.

Ваши ответы требуют времени на изучение. Благодарю Вас заранее, авансом значится и отправляюсь изучать Ваш опыт.
Этот форум я периодически читаю, но прямого ответа на свой вопрос еще не встречал и решил его задать, по профессии я не программист и в силу возраста до меня туго доходят иногда очевидные вещи.
 
Цитата
Денис написал:
прямого ответа на свой вопрос еще не встречал
Словами кратко
1) из OnStop как-то посигнальте мейну, что пора завершаться
2) придержите OnStop с помощью цикла со слипом (обязательно со слипом), пока мейн не просигналит в ответ, что он вот-вот завершится
3) придержите OnStop еще чуть-чуть тем же способом, чтобы мейн точно уже завершился
4) вуаля, OnStop последний из могикан, мейна уже нет.
Но убедитесь, что мейн не выбросит ошибки, пока OnStop его ждет, иначе как раз и получится описанная ситуация с зависанием всего.
 
Цитата
Anton написал:
Цитата
Денис написал:
прямого ответа на свой вопрос еще не встречал
Словами кратко
1) из OnStop как-то посигнальте мейну, что пора завершаться

Вот, тут, я и поплыл... Как посигналить? Если Stopped = true, то этот самый флаг мониторить в каждом операторе main() ?
То есть, матчасть моя тут слаба и где поправить это бедствие - не знаю пока. Ведь, OnStop() прилетает в неизвестно-случайном месте main()

Цитата
Anton написал:
2) придержите OnStop с помощью цикла со слипом (обязательно со слипом), пока мейн не просигналит в ответ, что он вот-вот завершится

Это понятно. Спасибо. Даже не догадывался до такого приемчика.

Цитата
Anton написал:
3) придержите OnStop еще чуть-чуть тем же способом, чтобы мейн точно уже завершился

То есть еще один цикл со sleep(0)?

Цитата
Anton написал:
4) вуаля, OnStop последний из могикан, мейна уже нет.
Но убедитесь, что мейн не выбросит ошибки, пока OnStop его ждет, иначе как раз и получится описанная ситуация с зависанием всего.

Вот... Вопрос, который мог бы заполнить пробел в моих знаниях.

Правильно ли я понимаю, что в то время, пока в основном потоке OnStop() подвешен на цикле со слипом - операторы main() в дополнительном потоке продолжают выполняться?

Если это верно, тогда и непонимание по ответу 1) снято.

Ну, и это - теперь уже не авансом - большое спасибо и дай бог Вам - здоровья!
 
Цитата
Денис написал:
после нажатия кнопки "Остановить" скрипт, вроде бы, штатно останавливается - но затем через пять-семь минут при нажатии на любой пункт меню QUIK намертво зависает.
Проверил - да, так и есть.
Хоть с квиком работаю уже много лет, но на чём квик сейчас застопорился не пойму.
Надо делать так, как надо. А как не надо - делать не надо.
 
Цитата
Старатель написал:
Цитата
Денис написал:
после нажатия кнопки "Остановить" скрипт, вроде бы, штатно останавливается - но затем через пять-семь минут при нажатии на любой пункт меню QUIK намертво зависает.
Проверил - да, так и есть.
Хоть с квиком работаю уже много лет, но на чём квик сейчас застопорился не пойму.

Ну, я подозреваю, что из-за непредсказуемости происходящего - возникает конфликт с открытием-закрытием файла и записью в него буфера данных. Вот, выше по тексту, один грамотный человек, дай бог ему здоровья, рассказал, как можно этот процесс упорядочить, то есть синхронизировать между потоками. Еще не пробовал его лекарство, но рецепт выглядит внушительно.
 
Так зависает не во время работы скрипта. Сначала скрипт штатно останавливается, при этом никаких ошибок не выдаёт. Затем скрипт уже перестаёт запускаться. И при попытке вызове какого-нибудь меню квик вешается.
Надо делать так, как надо. А как не надо - делать не надо.
 
Цитата
Старатель написал:
Так зависает не во время работы скрипта. Сначала скрипт штатно останавливается, при этом никаких ошибок не выдаёт. Затем скрипт уже перестаёт запускаться. И при попытке вызове какого-нибудь меню квик вешается.

Все верно. Именно, такое горе и происходит. Функции закрытия файлов и выталкивания буфера - это системные функции. Вполне, допускаю, что они греховно остаются частично работать в системе после остановки скрипта.
 
Вероятно, я с таким не сталкивался, потому что никогда не использовал return 0 в OnStop. Минимальное значение не менее 100 указываю.
Надо делать так, как надо. А как не надо - делать не надо.
 
Цитата
Денис написал:
Как посигналить? Если Stopped = true
Да, этот момент уже в коде присутствует и упомянут для полноты. Мониторить не в каждом, но при работе с окнами - да, попытка что-то сделать с окном из мейна, когда OnStop подвешен на цикле - дедлок. Строго говоря, даже проверка перед вызовом такой функции небезопасна, флаг может встать между проверкой и актуальным вызовом (хотя маловероятно).

Цитата
Денис написал:
То есть еще один цикл со sleep(0)?
Да, как по ссылке выше показывал. Это по сути спинлоки за отсутствием нормальной синхронизации в луа, мьютекса или семафора или хоть чего-нибудь.

Цитата
Денис написал:
в то время, пока в основном потоке OnStop() подвешен на цикле со слипом - операторы main() в дополнительном потоке продолжают выполняться?
Да, но в силу устройства луа нужен какой-нибудь сишный вызов в цикле ожидания (в данном случае слип), иначе все встает.

А вообще я б лучше предложил сделать последним таки мейн, вот с этой всей синхронизацией на выходе, пусть вся логика отработает как требуется, в том числе последнее полезное действие в OnStop, но чтобы последнее слово было за мейном, даже просто голый return. Так надежнее, будет одна точка выхода на все случаи.
 
Итак, благодаря пользователю Anton (дай бог ему здоровья и хорошего настроения), который дал рецепт, кажется, лекарство мне помогло.

Добавил цикл в функцию обратного вызова OnStop(), который ждет сигнала от main(), что все функции цикла выполнены и процесс готов к завершению.
В начале цикла main() ставит сигнал красного светофора для OnStop(), а в конце цикла ставит сигнал зеленого светофора для функции OnStop()
То есть, если знак светофора - зеленый, то OnStop() может завершиться.

Вот так выглядит код с лекарством и у меня пока не получилось добиться зависания QUIK, как это случалось в прежнем варианте:

Код
Stopped = false

-- для имени файла отладки
File_Name_Log_Debugger = "Log_test_OnStop.txt"

-- для сигнала, что main() готово к завершению
Flag_Svetofor = "Green"

function OnStop()      

   Stopped = true
   while tostring(Flag_Svetofor) == "Red" do 
      sleep(0)
   end
   File_Log_Debugger:close() -- закрывается файл отладчика   

return 0   
end                  -- конец функции обратного вызова OnStop()

function main()

-- Открывается отладочный файл лога
   File_Log_Debugger = io.open(File_Name_Log_Debugger, "w")

   while not Stopped do   
   
      Flag_Svetofor = "Red"
      
      if File_Log_Debugger and tostring(File_Log_Debugger) ~= "file (closed)" then
         File_Log_Debugger:write("Тестовая строка\n")
         File_Log_Debugger:flush()
      end -- end if

      Flag_Svetofor = "Green"
      sleep(1)
   end -- end while конец обработки цикла ожидания робота выход по останову   
   
end -- Конец функции main()
 
Цитата
Старатель написал:
Сначала скрипт штатно останавливается, при этом никаких ошибок не выдаёт.
Дело в том, что нештатно останавливается. Если OnStop возвращает 0, сразу за ней выполняется WaitForSingleObject(main, 0), возвращаясь немедленно из-за нулевого таймаута, и сразу же TerminateThread. Там потом еще уведомление главному окну посылается. Видимо, какие-то сопли после этого всего остаются.
 
Денис,  все же в мейне конец чуть иначе предполагался
Код
function main()

-- Открывается отладочный файл лога
   File_Log_Debugger = io.open(File_Name_Log_Debugger, "w")

   while not Stopped do   
   
      Flag_Svetofor = "Red"
      
      if File_Log_Debugger and tostring(File_Log_Debugger) ~= "file (closed)" then
         File_Log_Debugger:write("Тестовая строка\n")
         File_Log_Debugger:flush()
      end -- end if

      sleep(1)
   end -- end while конец обработки цикла ожидания робота выход по останову

   Flag_Svetofor = "Green" -- вот сюда его родимого

end -- Конец функции main()
 
Цитата
Anton написал:
Денис,  все же в мейне конец чуть иначе предполагался

И туда вставил, и сюда вставил... Но QUIK все-таки умер. Не так сразу, как раньше но умер. Пока, попробую без оператора return

Код
Stopped = false

-- для имени файла отладки
File_Name_Log_Debugger = "Log_test_OnStop.txt"

-- для сигнала, что main() готово к завершению
Flag_Svetofor = "Green"

function OnStop()      

   Stopped = true
   while tostring(Flag_Svetofor) == "Red" do 
      sleep(0)
   end
   File_Log_Debugger:close() -- закрывается файл отладчика   

return 0   
end                  -- конец функции обратного вызова OnStop()

function main()

-- Открывается отладочный файл лога
   File_Log_Debugger = io.open(File_Name_Log_Debugger, "w")

   while not Stopped do   
   
      Flag_Svetofor = "Red"
      
      if File_Log_Debugger and tostring(File_Log_Debugger) ~= "file (closed)" then
         File_Log_Debugger:write("Тестовая строка\n")
         File_Log_Debugger:flush()
      end -- end if

      sleep(1)
   end -- end while конец обработки цикла ожидания робота выход по останову   

   Flag_Svetofor = "Green"
   
end -- Конец функции main()
 
Цитата
Денис написал:
попробую без оператора return
Ага, я не заметил, return 0 надо убрать, выше написал почему, квик видит ноль и прибивает мейн немедленно, хотя тот уже и сам хотел завершиться.
 
Цитата
Anton написал:
Цитата
Денис написал:
попробую без оператора return
Ага, я не заметил, return 0 надо убрать, выше написал почему, квик видит ноль и прибивает мейн немедленно, хотя тот уже и сам хотел завершиться.

Все, спасибо огромное! Прибил return вообще. Он и не нужен для лечения моего горя, я его как костыль прикручивал. А тут, все вроде, красиво получилось.
Что значит совет знающего человека! Я доволен, как слон. Могу двигаться дальше.
 
Anton,
Цитата
А вообще я б лучше предложил сделать последним таки мейн.
Дык я когда-то именно так и пытался делать, но терялось управление - почему, я понял только по Вашим же комментариям, и теперь у меня в OnStop выполняется целая куча действий - в основном, с файлами, а напоследок я убиваю открытые таблицы: получится, нет ли - уже пофиг, и sleep(500) ему в зубы, чтобы особо не раздумывал. Давно уже работает без нареканий.
 
В общем, последнее слово я все-таки оставил за OnStop(), но говорить она будет только тогда, когда main() все свои дела закончит.
Итоговый код для "Hello World" получился такой:

Код
Stopped = false

-- для имени файла отладки
File_Name_Log_Debugger = "Log_test_OnStop.txt"

-- для сигнала, что main() готово к завершению
Flag_Svetofor = "Green"

function OnStop()      

   Stopped = true
   while tostring(Flag_Svetofor) == "Red" do 
      sleep(0)
   end

end                  -- конец функции обратного вызова OnStop()

function main()

-- Запрещается функции OnStop() остановка скрипта в теле цикла
   Flag_Svetofor = "Red"

-- Открывается отладочный файл лога
   File_Log_Debugger = io.open(File_Name_Log_Debugger, "w")

   while not Stopped do   
      
      if File_Log_Debugger and tostring(File_Log_Debugger) ~= "file (closed)" then
         File_Log_Debugger:write("Тестовая строка\n")
         File_Log_Debugger:flush()
      end -- end if

      sleep(0)
   end -- end while конец обработки цикла ожидания робота выход по останову   

   File_Log_Debugger:close() -- закрывается файл отладчика      
   
   Flag_Svetofor = "Green"
   
end -- Конец функции main()
 
Владимир, тут речь именно про return 0 из OnStop, что форсирует прибитие мейна. Логически мы все делаем в OnStop, потом из него выходим и оставляем какой-то таймаут, чтобы мейн успел добежать до своего end, т.е. физически он свое "ничего" делает последним, а логически из ничего и выходит ничего.
 
Цитата
Anton написал:
Владимир, тут речь именно про return 0 из OnStop, что форсирует прибитие мейна. Логически мы все делаем в OnStop, потом из него выходим и оставляем какой-то таймаут, чтобы мейн успел добежать до своего end, т.е. физически он свое "ничего" делает последним, а логически из ничего и выходит ничего.


Anton, если Вы подключены к системе СБП от ЦБ и напишете мне в личку номер Вашего мобильника - готов выслать "на пиво" в честь великого праздника души моей, Вами устроенного!
 
Anton,  Я понимаю но меня, как старого программиста, просто корёжит от необходимости что-либо делать в прерывании. На то оно и прерывание, чтобы быстренько отреагировать, установить какие-то флаги и быстренько оттуда умотать. А потому и логически, и физически, завершающие операции нужно бы делать именно в мейне, а в OnStop просто сбрасывать флаг бесконечного цикла. Но тут имеем: потеря управления - раз, запрет обращаться к функциям основного потока - два. Файловые операции, слава Богу, ещё работают, а вот операции с таблицами Квика - это ещё бабушка надвое сказала.
 
Цитата
Anton написал:
Дело в том, что нештатно останавливается. Если OnStop возвращает 0, сразу за ней выполняется WaitForSingleObject(main, 0), возвращаясь немедленно из-за нулевого таймаута, и сразу же TerminateThread. Там потом еще уведомление главному окну посылается. Видимо, какие-то сопли после этого всего остаются.
Получается с той или иной долей вероятности можно налететь на такое при любом значении в return и даже без него?
Надо делать так, как надо. А как не надо - делать не надо.
 
Денис, Кстати, есть ещё одно решение - я даже когда-то хотел его реализовать: вообще отказаться от OnStop, а выход реализовать по собственному обработчику событий (по мышке или по клавише). В этом случае всё прекрасно отработает - и с файлами, и с таблицами. Отказался потому, что когда-нибудь рука всё равно даванёт на кнопку "остановить", и я останусь без результатов торговли последнего запуска. Потом, правда, я всё равно стал сбрасывать дамп каждые 5 минут (по другим причинам - Квик стал вылетать "без объявления войны"), так что можно бы сделать и так, но мне уже лень там что-либо перестраивать.
 
Цитата
Владимир написал:
Денис, Кстати, есть ещё одно решение - я даже когда-то хотел его реализовать: вообще отказаться от OnStop, а выход реализовать по собственному обработчику событий (по мышке или по клавише). В этом случае всё прекрасно отработает - и с файлами, и с таблицами. Отказался потому, что когда-нибудь рука всё равно даванёт на кнопку "остановить", и я останусь без результатов торговли последнего запуска. Потом, правда, я всё равно стал сбрасывать дамп каждые 5 минут (по другим причинам - Квик стал вылетать "без объявления войны"), так что можно бы сделать и так, но мне уже лень там что-либо перестраивать.

Решений может быть множество :) Кстати, возник вопрос. Функции обратного вызова перестают работать после окончания работы функции OnStop() или после ее вызова?
Возможно, без return на цикле со sleep(0) удерживая OnStop() от завершения - можно продолжать пользоваться всеми благами цивилизации?
 
Денис, Да, решений может быть множество, но такое - одно: только один коллбек на все случаи жизни, так что плевать, работают функции обратного вызова после окончания OnStop или нет - в этом варианте их просто нет, как и самого OnStop. OnTrade тоже загружен до безобразия всяким действиями - а что делать?

Я бы не стал полагаться на "блага цивилизации" Четверть, если не треть кода моего скрипта создана исключительно для борьбы с глюками, появляющимися при работе этих самых "благ". :smile:  
 
Цитата
Денис написал:
подключены к системе СБП от ЦБ
Пришлось гуглить, что это ) оказалось - не подключен. Ну да это и неважно.

Цитата
Владимир написал:
А потому и логически, и физически, завершающие операции нужно бы делать именно в мейне, а в OnStop просто сбрасывать флаг бесконечного цикла.
В идеале да. Тут, как в сях иногда, без break переходим на следующий кейс:

Цитата
Старатель написал:
Получается с той или иной долей вероятности можно налететь на такое при любом значении в return и даже без него?
Трудно сказать с уверенностью про все случаи. По логике, если TerminateThread не вызывается (т.е. когда WaitForSingleObject дождалась завершения мейна), все происходит как ожидалось. Ну с поправкой на то, что цикл сообщений стоит. Вообще я бы не так сделал, как сделано. Сейчас оно примерно так
Код
void OnScriptStopButtonClick()
{
    int timeout = ScriptOnStop();
    if(WAIT_TIMEOUT == WaitForSingleObject(scriptmainthread, timeout))
        TerminateThread(scriptmainthread);
    ScriptCleanup();
    NotifyMainWindow(script_stopped);
}
а лучше бы было как-то типа
Код
void OnScriptStopButtonClick()
{
    int timeout = ScriptOnStop();
    ScriptDisallowCallbacks();
    SetTimer(timeout, ScriptStopProcedure);
    NotifyMainWindow(script_stopping);
}

void ScriptStopProcedure()
{
    if(WAIT_TIMEOUT == WaitForSingleObject(scriptmainthread, 0))
        TerminateThread(scriptmainthread);
    ScriptCleanup();
    NotifyMainWindow(script_stopped);
}
Однако есть подозрение, что при закрытии квика с работающими скриптами они останавливаются в WM_DESTROY, и оттуда вот так выйти и ждать таймеров не выйдет, т.к. окно будет прибито виндой раньше, чем дождемся таймеров. В итоге, если все это верно, это улучшение потребует вообще весь квик переделывать, что вряд ли.

Цитата
Денис написал:
Функции обратного вызова перестают работать после окончания работы функции OnStop() или после ее вызова?
Правильней будет сказать, что перестает работать скрипт. Колбеки не могут быть вызваны, пока окружающий OnStop код в квике не закончится, а когда он закончится - скрипта уже не существует. Пока обрабатывается OnStop (и любой другой колбек), другой колбек вызван быть не может. Мы так считаем, хотя Владимир вроде находил какой-то случай, когда колбек оказывался вложенным, но списали на случайный глюк.
 
По-моему, код в первом сообщении специально написан так, чтобы максимально пройтись по граблям.

1. Открываем файл в main(), а закрываем зачем-то в другом потоке в OnStop().
Это и само по себе некрасиво просто-напросто (открывать в одном месте, в закрывать в другом, совершенно не связанном ни визуально, ни логически), да еще и ожидаемое может приводить (и приводит) к появлению сообщения "attempt to use a closed file", механизм появления которого вполне понятен. (нет? уточните, мне не сложно, напишу, но потом, не буду это сообщение разбухать).2.
 
(это рука-лицо а не форум)
 
2. Из OnStop() возвращаем 0
Зачем? принуждаем QUIK мгновенно по выходу из OnStop() прибиваь поток main() - чтобы что? чтобы не дать функции потока корректно доработать (хотя доработает она очень быстро в приведенном слуучае), т.к. взводим флаг Stopped
Делаем всё, чтобы насильно прибить потоковую функцию, от нее ожидаемо повисают сопли незакрытых ресурсов, куски незафинализированных вызовов Lua.
А после - удивительно! - оставшаяся часть софта начинает глючить (и я подозреваю, что QUIK эти 5..7 минут не просто сам по себе живет, наверняка выполняются другие Lua-скрипты)

Ну т.е. сделано все, чтобы ожидаемо получить глюки.
Но зачем?? В чем ценность этой демонстрации?
 
Цитата
:lol: swerg написал:
Открываем файл в main(), а закрываем зачем-то в другом потоке в OnStop().
Здравствуйте, уважаемый swerg !
Прежде всего, низкий Вам поклон за Ваш форум, если бы не он, я бы бросил изучать Lua ровно там же, где и начал. К сожалению, на Вашем форуме рассматриваются более глубокие вопросы и там я не решился спрашивать советов.

Теперь, позволю себе пояснить откуда родился такой "кракозябр". Я столкнулся с проблемой, что прерывание OnStop() прилетает в любом операторе main() и после своего завершения вызывает ошибки в операторах, в которых ошибок быть не должно.

Например, в скрипте получены значения переменных и он должен просто их сложить. Все прекрасно до тех пор, пока не нажата кнопка "Остановить" - тут скрипт вылетает со словами "Не могу сложить а и б, потому что б равно nil.
С какого перепугу, имеющая определенное значение переменная б, вдруг, его теряет после отработки функции OnStop?

Первое, что пришло в голову - это сделать функцию OnStop последней, так как поведение скрипта в main() после ее запуска теряет штатную предсказуемость. Ну, не могу же я каждый оператор "обернуть" if not Stopped then... Да и бесполезно это, есть ведь еще и функции QLUA, там, уж, точно ничего не обернешь.

А раз она (OnStop()) последняя - то она и должна, снять все заявки, закрыть все файлы и сохранить данные, подлежащие хранению.

Но, чтобы я не делал, управление после завершения работы OnStop() все-равно передавалось в main() и он эпизодически вылетал с руганью на nil различных переменных.

Собственно, простое лечение этой беды подсказал уважаемый Anton - достаточно запретить OnStop() завершаться раньше, чем закончится main() и вуаля, проблема решена. Мне, на шестом десятке лет, решившему, вдруг, изучать программирование "себе на дому", да еще с Lua, не зная ни одного диалекта C, конечно, такое простое и изящное решение в голову прийти не могло.


Цитата
swerg написал:
2. Из OnStop() возвращаем 0
Зачем? принуждаем QUIK мгновенно по выходу из OnStop() прибиваь поток main() - чтобы что? чтобы не дать функции потока корректно доработать (хотя доработает она очень быстро в приведенном слуучае), т.к. взводим флаг Stopped
Делаем всё, чтобы насильно прибить потоковую функцию, от нее ожидаемо повисают сопли незакрытых ресурсов, куски незафинализированных вызовов Lua.
А после - удивительно! - оставшаяся часть софта начинает глючить (и я подозреваю, что QUIK эти 5..7 минут не просто сам по себе живет, наверняка выполняются другие Lua-скрипты)

Ну т.е. сделано все, чтобы ожидаемо получить глюки.
Но зачем?? В чем ценность этой демонстрации?
Вот, именно, отсюда и родился return 0, чтобы после завершения OnStop() не было глюков при завершении скрипта main() Мне нужно было, чтобы после завершения функции OnStop() ни один оператор main() не выполнялся, чтобы скрипт не вылетал по ошибке со всеми "грязными" вытекающими последствиями в виде незакрытых файлов и недосохраненных данных.

Но мне и в голову не приходило, что можно "притормозить" OnStop() от завершения, а в это время спокойно и без глюков доработать до оператора end

Цитата
swerg написал:
Ну т.е. сделано все, чтобы ожидаемо получить глюки.
Но зачем?? В чем ценность этой демонстрации?

Это была не демонстрация, а просьба о помощи  :lol:  
 
swerg, Ну, вот он я - открываю файл в main, а закрываю в OnStop. Мало того: я ещё его и ПЕРЕоткрываю в обработчике событий от юзера! Все свои действия считаю безусловно правильными. Да, иногда выскакивает "attempt to use a closed file" - абсолютно непонятно, чего ему надобно? Да и не очень интересно.

И я тоже "мгновенно по выходу из OnStop прибиваю поток main" - чтобы что? На это подробно отвечал Антон ещё в самые первые дни после моего появления здесь.

А "оставшаяся часть софта" начинает глючить ВСЕГДА И ВЕЗДЕ! Я что-то вообще не могу припомнить ни единой "части", которая бы НЕ глючила!

В чем ценность этой демонстрации? В разумно заданном вопросе, на который был дан профессиональный ответ - чрезвычайно редкая ситуация на этом форуме!
 
Цитата
Денис написал:
Прежде всего, низкий Вам поклон за Ваш форум, если бы не он, я бы бросил изучать Lua ровно там же, где и начал.
Спасибо Вам на добром слове  :oops:  :smile:

Цитата
Например, в скрипте получены значения переменных и он должен просто их сложить. Все прекрасно до тех пор, пока не нажата кнопка "Остановить" - тут скрипт вылетает со словами "Не могу сложить а и б, потому что б равно nil.
С какого перепугу, имеющая определенное значение переменная б, вдруг, его теряет после отработки функции OnStop?
Тут бы пример.
Честно говоря не верится мне, что если OnStop() содержит только
{
  Stopped = true;
  return 5;
}
то переменные, используемые только в main() внезапно становятся равными nil после этого.
Ну а по примеру уже можно было бы и подсказать что там не так.
 
Цитата
swerg написал:
Тут бы пример.Честно говоря не верится мне, что если OnStop() содержит только{   Stopped = true;   return 5;}то переменные, используемые только в main() внезапно становятся равными nil после этого.Ну а по примеру уже можно было бы и подсказать что там не так.

Пример привести сложно, ибо, он боевой и его долго "чистить" перед выкладкой "на показ". А моделировать ситуацию искусственно - боюсь не сумею.


Дело в том, что код мой разбит на модули, которые, может, подключены некорректно. Хотя, сам принцип брал из кусков Вашего кода на Вашем форуме и QLUA не ругался на меня ни разу.

Код
-- получается адрес рабочей папки
Adress_Work_DIR = getScriptPath()

-- Добавляется к переменной поиска окружения адрес папки скрипта,
-- чтобы интерпретатор знал, где искать модули
package.path = package.path..";"..Adress_Work_DIR.."\\?.lua"

require "stand_mod"
require "init_mod"
require "scan_mod"
require "quik_mod"


В общем, цель поста была не в том, чтобы разобраться почему приведенный пример не работает, а в том, чтобы сделать так, чтобы оно работало.
Меня вовсе не устраивало, когда скрипт завершал свою работу в любом месте, которое я не могу предсказать.

Я сделал логгирование в файл и писал туда после завершения OnStop() системное время с микросекундами.
Без использования return в файл писалось как раз на пять секунд записей. QUIK работал стабильно.

После использования return 5000 тоже несколько запусков писалось на пять секунд записей, но после некоторого времени (минут пять или семь) QUIK умирал напрочь при нажатии любого пункта меню.
Собственно, именно этот эксперимент и побудил меня просить помощи на форуме.

А сообщением "attempt to use a closed file" я лишь хотел подчеркнуть, что управление все-равно возвращается в main(), даже не взирая на return 0 и там что-то успевает происходить.
То есть, как раз удачный случай, чтобы показать, что прерывание скрипта может произойти в любой момент, в том числе уже после анализа условия условного оператора вернуться в его тело и выполнить его уже не зная, что условие изменилось.

Надеюсь, что теперь - все стройно получилось. main() получает сигнал, что OnStop() его "прибьет" скоро и выходит из цикла ожидания, чтобы снять заявки, закрыть файлы, почистить перья и только потом, разрешает OnStop() его "прибить".
 
Добавлю свои пять копеек.
чтобы скрипт корректно завершил работу, в колбеке OnStop()  специально сделан возврат со значением времени задержки завершения.
--------------------------------------
Поэтому все другие колбеки будут вызываться после выхода из OnStop.
-------------------------------
функция main - это другой поток и она вызывается вне зависимости от колбеков.
-------------------------------------
Чтобы ничего не зависало при выходе вы должны указать время задержки завершение,
которое хватит для завершения всех ваших плюшек в скрипте.
----------------------
Это время Вы определяете сами, так как никто не знает что Вы нагородили в своем скрипте.
Вполне возможно, что и 5 секунд может не хватить чтобы корректно завершить скрипт.
 
nikolz, Чтобы скрипт корректно завершил работу, в колбеке OnStop, нужен нормальный колбек OnStop, а не этот маразм со "специально сделанным (!!!) возвратом со значением времени задержки завершения". В 99 случаях из 100 работа скрипта завершается корректно, и вся "работа" этого "коллбека" состоит в том, чтобы сбросить флаг останова - И ВСЁ!!! Этот же придурок считает, что его вызвали для АВАРИЙНОГО завершения скрипта, причём даже этого он тоже делать не умеет. Разделение по потокам - ещё один идиотизм, источник бесконечных глюков и ничего более. Когда я узнал, что интерпретируемый код способен подвесить задачу, я просто не поверил своим глазам! Кстати, какое бы Вы ни указали время задержки завершения, это ничего не значит и не гарантирует от зависания при выходе - этот придурок просто и нагло не отдаёт управление "для завершения всех ваших плюшек в скрипте".
Страницы: 1
Читают тему
Наверх