Есть подсистема для локального тестирования олимпиадных (и прочих учебных) задач для школ. Пишешь решение, тыкаешь коммандер, решение прогоняется на наборе тестовых входных-выходных файлов. Это работает.
Задача такая. Если программа думает более 5 секунд, надо вежливо попросить её на выход и сообщить, что вышло время ожидания на тесте таком-то.
Как сделать таймер и зарядить его на 5 секунд я подсмотрел в DevProfiler. Это работает.
Как снять задачу? HALT или ASSERT в обработчике таймера не прокатят, поскольку обработчик выполняется (насколько я понял) в контексте другого потока.
Я подумал сделать по аналогии с Ctrl+Break. Там основной поток ставится на паузу, после чего в его контексте устанавливается флаг трэпа:
Код:
res := KERNEL32.SuspendThread(main);
context.flags := {0, 16};
res := KERNEL32.GetThreadContext(main, context);
INCL(SYSTEM.VAL(SET, context.pf), 8); (* set trap flag *)
res := KERNEL32.SetThreadContext(main, context);
res := KERNEL32.ResumeThread(main)
Если бы это удалось, то достаточно было бы проигнорировать последующий трэп (обработка трэпов при работе тестируемой задачи тоже уже реализована). Проблема в том, что Kernel.TryHandler игнорирует создаваемый таким образом трэп EXCEPTION_SINGLE_STEP, если переменная Kernel.interrupted = FALSE (устанавливается в TRUE только в обработчике Ctrl+Break). Эта переменная не экспортирована.
Как бы нам в такой ситуации поступить? Экспортировать Kernel.interrupted не хочется. Ещё более не хочется видеть сообщение "keyboard interrupt". Возможно ли возбудить исключение с кодом 128, которое отработает без шума и пыли? Может быть кто-то может порекомендовать какое-нибудь шаманство с Context.ip (instruction pointer)? Например, прыжок в процедуру/на инструкцию HALT(128), но это я корректно сделать не умею.
Какие мысли есть по этому поводу?