OberonCore https://forum.oberoncore.ru/ |
|
#019 SafeAction — защищенные действия https://forum.oberoncore.ru/viewtopic.php?f=134&t=6536 |
Страница 1 из 2 |
Автор: | adimetrius [ Вторник, 07 Январь, 2020 01:47 ] | ||
Заголовок сообщения: | #019 SafeAction — защищенные действия | ||
Коллеги, в ядре имеются средства для "защищенного" вызова процедур: Код: TYPE TryHandler = PROCEDURE (a, b, c: INTEGER) The type of procedure that can be called with a local trap handler by means of Try. PROCEDURE Try (h: TryHandler; a, b, c: INTEGER) Calls the procedure h with parameters a, b, and c with a local trap handler. If a trap occurs inside h a trap window is displayed and h is terminated but the execution is continued as if Try proceeded normally. Модуль HostWindows много использовал эти средства, поскольку оконная система должна быть защищена от авостов в клиентских процедурах: иначе "посыпется" оконная система, когда какой-нибудь вид даст сбой, например, во время отработки нажатий. Очевидны недостатки: 1. чтобы пользоваться Try, приходится забыть о контроле типов и пользоваться SYSTEM.VAL для приведения типов: Код: Kernel.Try(P, SYSTEM.VAL(INTEGER, SomePtr), 0, 0) PROCEDURE P (a, b, c: INTEGER); VAR ptr: SomePtr; BEGIN ... ptr := SYSTEM.VAL(SomePtr, a)... END P; 2. Параметров всегда три: если не нужны - передавать абы-чего, если нужно больше - изворачиваться. Я предлагаю решение, которое устранит эти недостатки; оно опробовано в экспериментальном Kernel в составе Tyler: Код: MODULE Kernel; TYPE SafeAction* = ABSTRACT RECORD trapped-: BOOLEAN END; PROCEDURE (VAR a: SafeAction) Do*, NEW, ABSTRACT; PROCEDURE Do* (VAR a: SafeAction); END Kernel. MODULE Client; TYPE SafeHandler = RECORD (Kernel.SafeAction) str: ARRAY OF CHAR; ptr: SomePtr END; PROCEDURE (VAR h: SafeHandler) Do; BEGIN PerformSomeRiskyActivity(h.str); IF h.ptr # NIL THEN AnotherRiskFactor(ptr) END END Do; PROCEDURE P; VAR h: SafeHandler; BEGIN h.str := "This is a safe handler"; h.ptr := something; Kernel.Do(h); IF h.trapped THEN Beda() ELSE Ura() END END P; END Client. Таким образом, решаются обе проблемы Kernel.Try. Перечислю достоинства: 1. высокоуровневая проверка типов, повышается безопасность и надежность программирования; 2. бОльшая гибкость в передаче параметров; 3. возможность анализировать результат защищенного вызова: поле .trapped; 4. SafeActions объявлены и реализованы как записи (а не указатели), поэтому они могут располагаться в автоматической памяти (стеке), не требуют дорогостоящих обращений к менеджеру памяти, не нуждаются в сборке мусора; при этом допускают размещение и в динамической, и в статической памяти. Use case: В проекте Тайлер в модуле HostWindows я повсеместно заменил Kernel.Try на Kernel.Do/Kernel.SafeAction, чем существенно ускорил отладку своих правок в HostWindows. А затем оказалось, что SafeAction гораздо шире может применяться, чем как в HostWindows - на стыке хостовой небезопасной системы и ББ. Поскольку я изменил реализацию окон, процедура открытия окна Windows.Open стала зависеть от вызовов расширений. Т.о. если в расширении происходит авост, "валится" и оконная система - нарушаются инварианты внутренних структур данных. Оказалось удобным небезопасную (зависящую от расширений) часть процедуры Open выделить в TYPE SafeOpen = RECORD (Kernel.SafeAction), и тем самым удалось гарантировать сохранность внутренних инвариантов модуля Windows. Без Kernel.SafeAction это потребовало бы использовать Kernel.Try и SYSTEM.VAL, что нежелательно для высокоуровневого модуля Windows. При этом у SafeOpen множество полей - по сути, параметров процедуры Do. Реализация Kernel.Do практически не отличается от текущей реализации Kernel.Try. ПРЕДЛАГАЮ: (1) включить приведенные Kernel.SafeAction и Kernel.Do в состав ядра; (2) объявить Kernel.Try и Kernel.TryHandler устаревшими и неспешно избавиться от них через пару-тройку лет. P.S. Мои эксперименты проводил под Linux P.P.S. Для одного системного инструмента - трассировщика сообщений - хотелось вызывать защищенную процедуру собственно с сообщением, т.е. передавать ей VAR-параметром запись, размещенную в стеке. Сделал так: Код: MODULE Kernel; TYPE SafeRecAction* = ABSTRACT RECORD (SafeAction) prep: BOOLEAN; (* DoRec => Do; this flags to Do that it was called *) adr, typedesc: INTEGER; (* address of VAR rec: ANYREC on stack; *) END; PROCEDURE (VAR a: SafeRecAction) DoRec* (VAR rec: ANYREC), NEW, ABSTRACT; PROCEDURE DoRec* (VAR a: SafeRecAction; VAR rec: ANYREC); END Kernel. Реализация DoRec Полагается на Do: Код: PROCEDURE DoRec* (VAR a: SafeRecAction; VAR rec: ANYREC); BEGIN a.adr := S.ADR(rec); a.typedesc := S.TYP(rec); a.prep := TRUE; Do(a) END DoRec; Т.е. DoRec просто "шаманит" с адресами, гарантируя клиентам безопасность. Прилагаю реализацию Do. Зеленым - то, что взято из Try, бирюсовым - то, что добавлено. (Вы добросовестно дочитали до конца мой лонгрид? Весьма вам признателен ![]()
|
Автор: | Info21 [ Вторник, 07 Январь, 2020 12:56 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Серьёзно. Впечатляет. |
Автор: | Борис Рюмшин [ Среда, 08 Январь, 2020 11:24 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Только желательно это не на базе Kernel реализовать. По той же самой причине: минимизация импорта Kernel в прикладном коде. Можно хоть в тот же Services поставить. |
Автор: | Борис Рюмшин [ Среда, 08 Январь, 2020 11:27 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Ещё раз на всякий случай. Services и Meta -- это "человеческое лицо" Kernel. Kernel не гарантирует и не должен гарантировать вам НИКАКИХ интерфейсов. Цитата: This module has a private interface, it is only used internally.
|
Автор: | adimetrius [ Среда, 08 Январь, 2020 11:40 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Да, можно SafeAction в Services отправить, но тогда Try нужно оставить в Kernel - тогда реализация SafeAction Будет опираться на Try. Собсно в Win32 версии я так и сделал внутри Kernel (экспериментально). |
Автор: | adimetrius [ Среда, 08 Январь, 2020 11:47 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Борис Рюмшин писал(а): Services и Meta -- это "человеческое лицо" Kernel. Борис, нужно тогда кое-что подправлять, чтобы действительно можно было не импортировать Kernel. В частности, нужно MODULE Serivces; TYPE Name* = Kernel.Name; END Services. А то, как только я хочу с идентификаторами что-то сделать, приходится Kernel импортировать. И еще, Kernel - не для приложений, но если я системную вещь делаю, его импорт оправдан, кмк. |
Автор: | Иван Денисов [ Среда, 08 Январь, 2020 11:54 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
adimetrius писал(а): Да, можно SafeAction в Services отправить, но тогда Try нужно оставить в Kernel - тогда реализация SafeAction Будет опираться на Try. Собсно в Win32 версии я так и сделал внутри Kernel (экспериментально). Поддерживаю. Try всё равно надо оставить для совместимости. |
Автор: | Борис Рюмшин [ Среда, 08 Январь, 2020 11:57 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Для системных вещей оправдан, да. Но, во-первых, его надо стараться минимизировать. Во-вторых, таких случаев реально мало. Во-третьих, мне непонятно adimetrius писал(а): MODULE Serivces; TYPE Name* = Kernel.Name; END Services. Зачем? При использовании Meta и Services ничего импортировать не надо. |
Автор: | Борис Рюмшин [ Среда, 08 Январь, 2020 12:00 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Иван Денисов писал(а): Поддерживаю. Try всё равно надо оставить для совместимости. А где я сказал, что Try надо убрать? (по всяком случае не сейчас ![]() Я говорю о том, что не надо рантайм языка втаскивать куда не надо. Это закрытый модуль. Для узкосистемных у вас есть на него документация, для всех остальных -- нет. |
Автор: | adimetrius [ Среда, 08 Январь, 2020 12:14 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Борис Рюмшин писал(а): adimetrius писал(а): MODULE Serivces; TYPE Name* = Kernel.Name; END Services. Зачем? MODULE M; IMPORT Services (* вместо Kernel *); PROCEDURE P; VAR name: Services.Name; ... END P; END M. |
Автор: | Борис Рюмшин [ Среда, 08 Январь, 2020 12:20 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
А Name то зачем нужен? |
Автор: | adimetrius [ Пятница, 17 Январь, 2020 13:56 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Коллеги, после обсуждений в Рокетчате и спасибо SovietPony за contribution предлагается такая платформо-независимая формулировка для Services: Код: PROCEDURE (VAR a: SafeAction) Do*, NEW, ABSTRACT; PROCEDURE (VAR a: SafeRecAction) DoRec* (VAR rec: ANYREC), NEW, ABSTRACT; PROCEDURE (VAR a: SafeRecAction) Do*; BEGIN a.DoRec(SYSTEM.THISRECORD(a.adr, a.typ)) END Do; (**) PROCEDURE TryHandler (VAR a: SafeAction; dummy: INTEGER); BEGIN a.Do END TryHandler; PROCEDURE Try* (VAR a: SafeAction); VAR count: INTEGER; BEGIN count := Kernel.trapCount; a.trapped := FALSE; Kernel.Try(SYSTEM.VAL(Kernel.TryHandler, SYSTEM.ADR(TryHandler)), SYSTEM.ADR(a), SYSTEM.TYP(a), -1); a.trapped := Kernel.trapCount # count END Try; PROCEDURE TryRec* (VAR a: SafeRecAction; VAR rec: ANYREC); VAR count: INTEGER; BEGIN count := Kernel.trapCount; a.trapped := FALSE; a.adr := SYSTEM.ADR(rec); a.typ := SYSTEM.TYP(rec); (**) Kernel.Try(SYSTEM.VAL(Kernel.TryHandler, SYSTEM.ADR(TryHandler)), SYSTEM.ADR(a), SYSTEM.TYP(a), -1); a.trapped := Kernel.trapCount # count END TryRec; То, что отмечено (**) - это "трюк", основанный на том, что в реализации VAR a: SafeRec - передается как два целых: адрес записи и адрес дескриптора типа. SovietPony предлагал иную реализацию этой части: Код: PROCEDURE SafeCall (VAR a: SafeAction); BEGIN a.Do END SafeCall; PROCEDURE TryHandler (actionAdr, actionTyp: ADDRESS; dummy: INTEGER); BEGIN SafeCall(SYSTEM.THISRECORD(actionAdr, actionTyp)) END TryHandler; ... Kernel.Try(TryHandler, SYSTEM.ADR(a), SYSTEM.TYP(a), -1); Вопрос к компиляторщикам (Дмитрий В. Дагаев, Олег Н. Чер): что скажете на это? Как ваши компиляторы отнесутся к такому коду? |
Автор: | adimetrius [ Пятница, 17 Январь, 2020 14:05 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Вот второй вариант полностью, для ясности: Код: PROCEDURE (VAR a: SafeAction) Do*, NEW, ABSTRACT;
PROCEDURE (VAR a: SafeRecAction) DoRec* (VAR rec: ANYREC), NEW, ABSTRACT; PROCEDURE (VAR a: SafeRecAction) Do*; BEGIN a.DoRec(SYSTEM.THISRECORD(a.adr, a.typ)) END Do; PROCEDURE SafeCall (VAR a: SafeAction); BEGIN a.Do END SafeCall; PROCEDURE TryHandler (actionAdr, actionTyp: ADDRESS; dummy: INTEGER); BEGIN SafeCall(SYSTEM.THISRECORD(actionAdr, actionTyp)) END TryHandler; PROCEDURE Try* (VAR a: SafeAction); VAR count: INTEGER; BEGIN count := Kernel.trapCount; a.trapped := FALSE; Kernel.Try(SYSTEM.VAL(Kernel.TryHandler, SYSTEM.ADR(TryHandler)), SYSTEM.ADR(a), SYSTEM.TYP(a), -1); a.trapped := Kernel.trapCount # count END Try; PROCEDURE TryRec* (VAR a: SafeRecAction; VAR rec: ANYREC); VAR count: INTEGER; BEGIN count := Kernel.trapCount; a.trapped := FALSE; a.adr := SYSTEM.ADR(rec); a.typ := SYSTEM.TYP(rec); Kernel.Try(TryHandler, SYSTEM.ADR(a), SYSTEM.TYP(a), -1); a.trapped := Kernel.trapCount # count END TryRec; |
Автор: | Oleg N. Cher [ Пятница, 17 Январь, 2020 17:53 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Ну чего сказать... нормальный код. Но в Ofront'е+ нет SYSTEM.TYP и SYSTEM.THISRECORD. Но можно сделать, если надо. |
Автор: | adimetrius [ Суббота, 08 Февраль, 2020 22:22 ] |
Заголовок сообщения: | Re: Версия 1.8 |
Коллеги, после обсуждения на этом форуме и в чате Chat.oberone.org был реализован интерфейс Services.SafeActions в ветке работы над версией ББ 1.8. Посмотреть и попробовать можно здесь же на https://blackbox.oberon.org/download. Документация, вероятно, еще не попала в загружаемый файл, поэтому привожу ее здесь. TYPE SafeAction ABSTRACT SafeActions are objects whose Do procedures can be called in a 'protected' fashion: if a trap occurs during the execution of Do, control returns to the caller (as opposed to terminating the whole running command). trapped-: BOOLEAN TRUE if the last execution of Do ended in a trap; FALSE if Do returned successfully. Valid only after Try. PROCEDURE (a: SafeAction) Do NEW, ABSTRACT Do is called in a protected fashion by Try. TYPE SafeRecAction ABSTRACT SafeRecActions are like SafeActions, but their DoRec procedure has a reference record variable parameter. trapped-: BOOLEAN TRUE if the last execution of DoRec ended in a trap; FALSE if DoRec returned successfully. Valid only after TryRec. PROCEDURE (a: SafeRecAction) DoRec (VAR rec: ANYREC) NEW, ABSTRACT DoRec is called in a protected fashion by TryRec. PROCEDURE Try (VAR a: SafeAction) Calls a.Do. Try returns even if a trap occurs during the execution of Do. a.trapped can be analyzed to check if Do trapped or returned successfully. PROCEDURE TryRec (VAR a: SafeAction; VAR rec: ANYREC) Calls a.DoRec(rec). TryRec returns even if a trap occurs during the execution of DoRec. a.trapped can be analyzed to check if Do trapped or returned successfully. |
Автор: | Иван Денисов [ Вторник, 11 Февраль, 2020 23:15 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
Антон, документация добавилась. Товарищи, ждем ваши отзывы. https://blackbox.oberon.org/unstable/de ... a1.010.zip Там есть модуль VarSafeActionTest Также высказано мнение, что с их применением и использованием HALT(128) возможно строить системы обработки исключений не многим хуже популярных языков. |
Автор: | adimetrius [ Среда, 12 Февраль, 2020 00:19 ] |
Заголовок сообщения: | Re: Предлагаю поправку: Kernel.SafeAction - защищенные дейст |
О, нет, НИЧУТЬ не хуже, в смысле не менее мощные. Ток, похоже, в нашем КомПасном мире не нужны они никому - эти структурные исключения. И, кстати, там же приложена была статья из далеких 90х про очень, кмк, оригинальный и минималистичный подход к исключениям на основе метапрограммирования, который использовали в одной из Оберонов. |
Автор: | Иван Денисов [ Вторник, 01 Декабрь, 2020 18:06 ] |
Заголовок сообщения: | Re: #019 Kernel.SafeAction — защищенные действия |
Вынесу сюда код SovietPony из чата, как пример организации обработки исключений. SovietPony писал(а): Вот примерно так можно сделать исключения на базе SafeAction. Для простоты сделал прямо в самом SafeAction, что бы не нужно было плодить больше типов. Дорабатывать есть куда. Можно добавить в Try генерацию исключений для перехвата системных исключений (деление на ноль, etc). Код: TYPE
ADDRESS* = INTEGER; SafeAction* = ABSTRACT RECORD trapped-: BOOLEAN END; Exception* = ABSTRACT RECORD END; VAR aAdr, aTyp: ADDRESS; guard: BOOLEAN; PROCEDURE (VAR a: SafeAction) Do*, NEW, ABSTRACT; PROCEDURE (VAR a: SafeAction) Handle- (VAR e: Exception), NEW, EMPTY; PROCEDURE TryHandler (actionAdr, actionTyp, dummy: ADDRESS); PROCEDURE SafeCall (VAR a: SafeAction); BEGIN a.Do END SafeCall; BEGIN SafeCall(SYSTEM.THISRECORD(actionAdr, actionTyp)) END TryHandler; PROCEDURE Try* (VAR a: SafeAction); VAR count: INTEGER; oAdr, oTyp: ADDRESS; oGuard: BOOLEAN; BEGIN oAdr := aAdr; oTyp := aTyp; oGuard := guard; aAdr := SYSTEM.ADR(a); aTyp := SYSTEM.TYP(a); guard := FALSE; count := Kernel.trapCount; a.trapped := FALSE; Kernel.Try(TryHandler, SYSTEM.ADR(a), SYSTEM.TYP(a), -1); aAdr := oAdr; aTyp := oTyp; guard := oGuard; a.trapped := Kernel.trapCount # count END Try; PROCEDURE Raise* (VAR e: Exception); PROCEDURE Handle (VAR a: SafeAction); BEGIN guard := TRUE; a.Handle(e); HALT(128) END Handle; BEGIN ASSERT(guard = FALSE, 100); (* recursion not allowed *) Handle(SYSTEM.THISRECORD(aAdr, aTyp)) END Raise; |
Автор: | Иван Денисов [ Вторник, 01 Декабрь, 2020 21:22 ] |
Заголовок сообщения: | Re: #019 Kernel.SafeAction — защищенные действия |
Пример, что можно отключить просмотр дампов стека при авостах, и заниматься вручную обработкой исключений Код: MODULE DemoTry;
IMPORT Log, Services, Kernel; TYPE SafeAction = RECORD (Services.SafeAction) END; TrapCleaner = POINTER TO RECORD (Kernel.TrapCleaner) END; VAR array: ARRAY 2 OF INTEGER; pos, res: INTEGER; cleaner: TrapCleaner; PROCEDURE Trap; BEGIN END Trap; PROCEDURE (c: TrapCleaner) Cleanup; BEGIN Log.String("cleaner"); Log.Ln; END Cleanup; PROCEDURE (VAR s: SafeAction) Do; BEGIN res := array[pos]; END Do; PROCEDURE Start*; VAR s: SafeAction; BEGIN Kernel.PushTrapCleaner(cleaner); array[0] := 0; array[1] := 1; pos := 2; Services.Try(s); IF s.trapped THEN Log.String("error"); Log.Ln; ELSE Log.Int(res); Log.Ln; END; Kernel.PopTrapCleaner(cleaner); END Start; BEGIN Kernel.InstallTrapViewer(Trap); NEW(cleaner); END DemoTry. DemoTry.Start |
Автор: | Иван Денисов [ Среда, 06 Январь, 2021 18:07 ] |
Заголовок сообщения: | Re: #019 Kernel.SafeAction — защищенные действия |
Оказывается в Блэкбоксе предусмотрена возможность на время отключать вывод дампов после аварийных остановок с помощью: Код: Kernel.SetTrapGuard(TRUE); Так что используя SafeAction возможно делать так: Код: MODULE DemoTry;
IMPORT Log, Services, Kernel; TYPE SafeAction = RECORD (Services.SafeAction) END; VAR array: ARRAY 2 OF INTEGER; pos, res: INTEGER; PROCEDURE (VAR s: SafeAction) Do; BEGIN res := array[pos]; END Do; PROCEDURE Start*; VAR s: SafeAction; BEGIN Kernel.SetTrapGuard(TRUE); array[0] := 0; array[1] := 1; pos := 2; Services.Try(s); IF s.trapped THEN Log.String("error"); Log.Ln; ELSE Log.Int(res); Log.Ln; END; Kernel.SetTrapGuard(FALSE); END Start; END DemoTry. DemoTry.Start |
Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |