🤖 Розділ 20 Аборт#
Примітка
Цей ШІ-переклад ще не відредаговано.
Оператор переривання призначено для реагування на помилкові ситуації, коли відновлення роботи завдання, що помилилося, є неможливим. Кажуть, що перервані завдання стають ненормальними і таким чином не можуть взаємодіяти з будь-якими іншими завданнями. В ідеалі, ненормальне завдання негайно припиняє виконання: перерване завдання стає ненормальним, і всі незавершені завдання, які залежать від перерваного завдання, також стають ненормальними. Після того, як всі іменовані завдання позначено як ненормальне, оператор переривання буде завершено, і завдання, що виконує переривання, може продовжити виконання. Він не чекає, доки іменовані завдання фактично завершаться [BW98, розділ 10.2].
Після того, як завдання позначено як ненормальне, виконання його тіла переривається. Це означає, що виконання кожної конструкції в тілі завдання переривається. Однак, певні дії повинні бути захищені, щоб забезпечити цілісність решти завдань та їхніх даних. Наступні операції визначаються як відкладені []: захищена дія, очікування завершення виклику входу, очікування завершення залежних завдань, а також виконання процедури «ініціалізації», процедури «фіналізації» або операції присвоєння об’єкту з контрольованою частиною. Крім того, для забезпечення цілісності структур даних під час виконання деяких підпрограм часу виконання також необхідно відкладати переривання під час виконання. У мові також визначено точки завершення переривання[]: 1) кінець активації завдання, 2) точка, де виконання ініціює активацію іншого завдання, 3) початок або кінець виклику введення, оператора accept, оператора затримки або стану переривання, і 4) початок виконання оператора вибору, або послідовності чи операторів обробника винятків.
Загалом, обробка переривання вимагає розмотування стеку цільового завдання, а не негайного виходу з перерваної частини (або вбивства завдання, у випадку переривання всього завдання). Можуть існувати локально керовані об’єкти, які вимагають виконання процедури фіналізації. Також можуть бути залежні завдання, які вимагають перерваного блоку обробки, поки вони не будуть перервані, фіналізовані та завершені. Фіналізація повинна виконуватися у порядку LIFO, а контексти стеку об’єктів, що потребують фіналізації, повинні зберігатися доти, доки об’єкти не буде фіналізовано [GB94, Розділ 3.4].
Реалізацію відкладеного переривання можна розділити на дві частини [GB94, Розділ 3.3]: 1) визначення того, чи відкладено переривання для даного завдання в момент, коли воно призначене для переривання, і 2) забезпечення негайної обробки відкладених переривань після зняття відкладення переривання. Загалом, визначення того, чи є завдання відкладеним, має здійснюватися самим завданням. В однопроцесорній системі завдання, яке ініціює переривання, може визначати, чи є цільове завдання відкладеним. Однак у багатопроцесорній системі або однопроцесорній системі, де час виконання Ada не має прямого контролю над плануванням завдань, це неможливо. Стан відкладеного переривання цільової задачі може змінюватися між моментом тестування і моментом переривання цільової задачі.
Існує два очевидних способи фіксування того, чи відкладено завдання. Один з них іноді називають картографуванням ПК. Компілятор та редактор компонування створюють мапу відкладених областей. Чи є завдання відкладеним, можна визначити, порівнявши поточне значення вказівника інструкції та всі збережені адреси повернення активних викликів підпрограм з мапою. Щоб забезпечити обробку переривання при виході з області з відкладеним перериванням, потрібно перезаписати збережену адресу повернення крайнього кадру виклику з відкладеним перериванням адресою підпрограми, яка обробляє переривання (зберігши стару адресу повернення в іншому місці). Перевірка відкладеного переривання може зайняти час, пропорційний глибині вкладеності викликів підпрограми, але це відбувається лише у разі спроби виконання ATC. Доки цього не сталося, відтермінування переривання не спричиняє жодних накладних витрат під час виконання. Обмеженням цього методу є те, що відкладені області повинні відповідати блокам коду, які можна викликати. Іншим обмеженням є те, що угода про виклик підпрограми має (1) гарантувати, що адреси повернення завжди знаходяться у передбачуваному та доступному місці, і (2) гарантувати, що ці дані є дійсними, навіть якщо послідовність виклику перервано. На жаль, для деяких архітектур це не так [GB94, Розділ 3.3].
У другій методиці задача збільшує та зменшує рівень вкладеності відкладених даних (наприклад, у спеціальному регістрі або ATCB) щоразу, коли вона входить та виходить з області з відкладеним перериванням. На виході з такої області, якщо лічильник дорівнює нулю, задача повинна перевірити, чи є відкладене переривання, і, якщо так, обробити переривання. Цей метод з лічильником відкладень накладає певні розподілені накладні витрати на вхід і вихід з областей з відкладеними перериваннями, але дозволяє швидко перевіряти [GB94, розділ 3.3]. У середовищі виконання GNAT реалізовано цей другий метод.
Реалізація УПР повинна вирішувати наступні питання [GB94, Розділ 3]: Переривання цільового завдання та ініціювання переривання.
Відтермінування аборту в певних регіонах.
Виконання процедур фіналізації для будь-яких локальних об’єктів у перерваній частині, кожен у своєму правильному контексті.
Пошук відповідного місця та контексту для продовження виконання після завершення АТО.
Обробка вкладених областей видимості, включаючи вкладені асинхронні інструкції вибору.
Забезпечення безпеки згенерованих компілятором послідовностей коду, включаючи виклик та повернення підпрограм при перериванні ATC.
ATC дуже схожий на поширення винятків, тому бажано, щоб один механізм слугував для обох цілей. Оскільки ATC навряд чи буде використовуватися у програмах на Ада, що не працюють у реальному часі, ключовою метою будь-якої реалізації має бути забезпечення мінімальних розподілених накладних витрат, необхідних для існування цієї функції мови, або їх повна відсутність. У принципі, певної ефективності можна досягти, уникаючи детального розгортання стеку, виконуючи процедури фіналізації з вершини стеку або з іншого стеку, а потім розкриваючи весь стек вниз до контексту, куди має бути передано керування. Однак, це передбачає, що існує спосіб відновити цей контекст без повного розгортання. Якщо компілятор використовує умову збереження регістрів за викликом, то значення актуальних регістрів можуть бути витіснені у непередбачуваних місцях стеку. У цьому випадку, схоже, потрібно створити область збереження регістрів для кожної потенційної цілі ATC (аналогічно до реалізації буфера переходу в операціях setjmp() та longjmp() у мові C). Хоча асинхронні оператори вибору можуть бути не дуже поширеними, обробники винятків є поширеними (деякі з них неявно надаються компілятором), а керовані об’єкти також очікувано будуть поширеними. Таким чином, накладні витрати на створення буфера переходу для кожної потенційної точки асинхронної передачі є об’єктивними [GB94, Розділ 3.4].
Повинні бути передбачені певні засоби для визначення місцезнаходження підпрограм завершення та точки, в якій виконання має бути відновлене після ATC. Ця проблема дуже схожа на проблему пошуку обробника винятків, і для неї застосовуються ті ж самі рішення. Основними підходами є збереження вказівника у кадрі стеку для кожної області видимості, відображення на ПК та різні гібриди цих двох підходів. Підхід з відображенням на ПК є кращим, оскільки він не накладає ніяких розподілених накладних витрат на виконання. Якщо для останньої мети використовується PC-відображення, є сильна мотивація спробувати зробити ту саму техніку подвійною, для переривання-відкладення [GB94, розділ 3.4].
Підпрограми часу виконання#
Реалізація GNAT щодо абортів складається з..:
Один прапорець в ATCB (переривання). Цей прапорець запобігає змаганню між кількома переривниками та перерваним завданням. Це дуже важливо, оскільки переривник може бути витіснений і надішле сигнал переривання при відновленні виконання.
Один внутрішній виняток (Сигнал переривання). Цей виняток невидимий для користувацького коду (див. розділ 18.1.1); він може бути перехоплений лише кодом під час виконання. При його поширенні виконується фіналізація всіх областей видимості на цьому шляху.
Один POSIX-сигнал (SIGABRT), який неможливо замаскувати.
Рисунок 20.1: Підпрограми GNARL для оператора переривання.
На рисунку 20.1 показано послідовність підпрограм часу виконання, що беруть участь у перериванні завдання, які описано у наступних розділах.
Виклик введення завдання GNARL.Task#
Підпрограма часу виконання GNAT Task Entry Call (див. розділ 15.5.4) підтримує не лише звичайні виклики входу, але й виклики ATC. У цьому останньому випадку, оскільки ATC можуть бути вкладеними, програма виконання повинна зберігати усі ці виклики, що очікують на вхід. З цією метою GNAT пов’язує стек викликів входу з кожним завданням Ада (див. Рисунок 20.2). Поле Pending ATC Level ATCB використовується для сигналізації про переривання ATC. Для того, щоб відрізнити інструкцію Abort від переривання ATC, час виконання визначає наступні правила:
У випадку оператора переривання, Pending ATC Level дорівнює 0.
У разі переривання ATC, рівень очікування ATC встановлюється на рівень, на якому абонент перебував безпосередньо перед вхідним викликом (рівень вкладеності ATC мінус один).
GNARL.Locked Перервати до рівня#
Підпрограма GNARL.Undefer Abort є універсальною точкою опитування для відкладеної обробки. Вона підтримує зміну базового пріоритету, обробку виключень та асинхронну передачу керування (ATC). У випадку зміни базового пріоритету, після встановлення нового пріоритету, він звільняє процесор, щоб планувальник вибрав наступні завдання для виконання. В інших випадках він перевіряє, чи не потрібно згенерувати виняток (переривання ATC викликає внутрішній сигнал переривання винятку Abort Signal).
GNARL.Locked Перервати до рівня#
Locked Abort To Level встановлює прапорець ATCB Pending Action у true і, залежно від поточного стану цільового завдання (заблоковане чи запущене), викликає GNARL.Wakeup або GNARL.Abort Task:
Якщо завдання, яке потрібно перервати, перебуває в стані сну (див. розділ 14.2), воно знаходиться в розділі відкладеного переривання. Тому, коли в майбутньому перерване завдання прокидається і продовжує своє виконання, воно виконує підпрограму Відкладеного переривання. У цей момент буде перевірено прапорець Pending Action ATCB і, якщо він буде істинним, це встановить прапорець ATCB Aborting і підніме внутрішній сигнал винятку Abort Signal.
Рисунок 20.2: Стек вхідних викликів.
Якщо задача знаходиться у стані виконання, то переривник надсилає їй сигнал SIG ABRT, після чого відповідний обробник переривання під час виконання асинхронно генерує внутрішній сигнал виключення Abort Signal у перерваній задачі.
В обох випадках внутрішній виняток Сигнал переривання розмотує стек перерваного завдання.
Підсумок#
GNARL-реалізація оператора переривання Ada складається з одного прапора в ATCB: Aborting, одного сигналу виключення Abort Signal і одного сигналу POSIX (SIGA- BRT). Прапор запобігає перегонам між кількома переривачами та перерваним завданням.
Винятком є те, що він може бути оброблений лише системним кодом під час виконання. Знак POSIX не може бути замасковано.