OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Суббота, 08 Август, 2020 16:13

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 17 ] 
Автор Сообщение
СообщениеДобавлено: Вторник, 07 Январь, 2020 01:47 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
Коллеги, в ядре имеются средства для "защищенного" вызова процедур:

Код:
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, бирюсовым - то, что добавлено.

(Вы добросовестно дочитали до конца мой лонгрид? Весьма вам признателен :P)


Вложения:
KernelAmndmnt1.txt [3.05 КБ]
Скачиваний: 134
Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 07 Январь, 2020 12:56 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8351
Откуда: Троицк, Москва
Серьёзно. Впечатляет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 11:24 
Администратор

Зарегистрирован: Вторник, 15 Ноябрь, 2005 01:14
Сообщения: 4442
Откуда: Россия, Орёл
Только желательно это не на базе Kernel реализовать. По той же самой причине: минимизация импорта Kernel в прикладном коде.
Можно хоть в тот же Services поставить.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 11:27 
Администратор

Зарегистрирован: Вторник, 15 Ноябрь, 2005 01:14
Сообщения: 4442
Откуда: Россия, Орёл
Ещё раз на всякий случай.

Services и Meta -- это "человеческое лицо" Kernel. Kernel не гарантирует и не должен гарантировать вам НИКАКИХ интерфейсов.

Цитата:
This module has a private interface, it is only used internally.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 11:40 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
Да, можно SafeAction в Services отправить, но тогда Try нужно оставить в Kernel - тогда реализация SafeAction Будет опираться на Try. Собсно в Win32 версии я так и сделал внутри Kernel (экспериментально).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 11:47 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
Борис Рюмшин писал(а):
Services и Meta -- это "человеческое лицо" Kernel.


Борис, нужно тогда кое-что подправлять, чтобы действительно можно было не импортировать Kernel. В частности, нужно

MODULE Serivces;
TYPE Name* = Kernel.Name;
END Services.

А то, как только я хочу с идентификаторами что-то сделать, приходится Kernel импортировать.

И еще, Kernel - не для приложений, но если я системную вещь делаю, его импорт оправдан, кмк.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 11:54 
Аватара пользователя

Зарегистрирован: Четверг, 08 Октябрь, 2009 15:00
Сообщения: 2809
adimetrius писал(а):
Да, можно SafeAction в Services отправить, но тогда Try нужно оставить в Kernel - тогда реализация SafeAction Будет опираться на Try. Собсно в Win32 версии я так и сделал внутри Kernel (экспериментально).

Поддерживаю. Try всё равно надо оставить для совместимости.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 11:57 
Администратор

Зарегистрирован: Вторник, 15 Ноябрь, 2005 01:14
Сообщения: 4442
Откуда: Россия, Орёл
Для системных вещей оправдан, да. Но, во-первых, его надо стараться минимизировать. Во-вторых, таких случаев реально мало.
Во-третьих, мне непонятно
adimetrius писал(а):
MODULE Serivces;
TYPE Name* = Kernel.Name;
END Services.

Зачем?
При использовании Meta и Services ничего импортировать не надо.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 12:00 
Администратор

Зарегистрирован: Вторник, 15 Ноябрь, 2005 01:14
Сообщения: 4442
Откуда: Россия, Орёл
Иван Денисов писал(а):
Поддерживаю. Try всё равно надо оставить для совместимости.

А где я сказал, что Try надо убрать? (по всяком случае не сейчас :D )
Я говорю о том, что не надо рантайм языка втаскивать куда не надо. Это закрытый модуль. Для узкосистемных у вас есть на него документация, для всех остальных -- нет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 08 Январь, 2020 12:14 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
Борис Рюмшин писал(а):
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 
Администратор

Зарегистрирован: Вторник, 15 Ноябрь, 2005 01:14
Сообщения: 4442
Откуда: Россия, Орёл
А Name то зачем нужен?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 17 Январь, 2020 13:56 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
Коллеги, после обсуждений в Рокетчате и спасибо 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:06, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 17 Январь, 2020 14:05 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
Вот второй вариант полностью, для ясности:

Код:
   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;


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 17 Январь, 2020 17:53 
Аватара пользователя

Зарегистрирован: Вторник, 28 Август, 2007 00:55
Сообщения: 406
Откуда: Украина, Днепропетровская обл.
Ну чего сказать... нормальный код. Но в Ofront'е+ нет SYSTEM.TYP и SYSTEM.THISRECORD.

Но можно сделать, если надо.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Версия 1.8
СообщениеДобавлено: Суббота, 08 Февраль, 2020 22:22 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
Коллеги, после обсуждения на этом форуме и в чате 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 
Аватара пользователя

Зарегистрирован: Четверг, 08 Октябрь, 2009 15:00
Сообщения: 2809
Антон, документация добавилась.

Товарищи, ждем ваши отзывы.
https://blackbox.oberon.org/unstable/de ... a1.010.zip
Там есть модуль
VarSafeActionTest

Также высказано мнение, что с их применением и использованием HALT(128) возможно строить системы обработки исключений не многим хуже популярных языков.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 12 Февраль, 2020 00:19 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 273
О, нет, НИЧУТЬ не хуже, в смысле не менее мощные. Ток, похоже, в нашем КомПасном мире не нужны они никому - эти структурные исключения.
И, кстати, там же приложена была статья из далеких 90х про очень, кмк, оригинальный и минималистичный подход к исключениям на основе метапрограммирования, который использовали в одной из Оберонов.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 17 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2020, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB