Це стане більшою мірою концептуальною відповіддю на те, чому ці застереження можуть існувати навіть при спробах використання ресурсів. На жаль, це не просте рішення, яке ви можете сподіватися.
Відновлення помилок не вдається
finally
моделює потік керування після транзакцій, який виконується незалежно від того, успішна чи невдала транзакція.
У випадку відмови finally
фіксує логіку, яка виконується в середині відновлення після помилки, до того, як вона буде повністю відновлена (до того, як ми дістанемося до catch
місця призначення).
Уявіть концептуальну проблему, яку вона представляє, щоб зіткнутися з помилкою в середині відновлення після помилки.
Уявіть собі сервер бази даних, де ми намагаємось здійснити транзакцію, і вона провалюється на півдорозі (скажімо, у сервера не вистачало пам'яті посередині). Тепер сервер хоче повернути транзакцію до точки, ніби нічого не сталося. Однак уявіть, що це стикається з черговою помилкою в процесі відкочування. Тепер ми закінчуємо надіслану половину транзакції з базою даних - атомність та неподільний характер транзакції тепер порушені, і цілісність бази даних тепер буде порушена.
Ця концептуальна проблема існує будь-якою мовою, яка стосується помилок, будь то C з ручним поширенням коду помилок, або C ++ з винятками та деструкторами, або Java з винятками та finally
.
finally
не може вийти з ладу в мовах, які надають її так само, деструктори не можуть вийти з ладу в C ++ у процесі зустрічі з винятками.
Єдиний спосіб уникнути цієї концептуальної та складної проблеми - переконатися, що процес відхилення транзакцій та вивільнення ресурсів у середині не може зіткнутися з рекурсивним винятком / помилкою.
Отже, єдиним безпечним дизайном тут є дизайн, де writer.close()
неможливо зірватися. Зазвичай в дизайні є способи уникнути сценаріїв, коли такі речі можуть вийти з ладу в середині відновлення, що робить його неможливим.
На жаль, це єдиний спосіб - відновлення помилок не може пройти. Найпростіший спосіб забезпечити це - зробити такі види функцій "вивільнення ресурсів" та "зворотних побічних ефектів" нездатними. Це непросто - правильне відновлення помилок важко, а також, на жаль, важко перевірити. Але спосіб досягти цього - переконатися, що будь-які функції, які "знищують", "закривають", "вимикають", "відкочують" тощо, не можуть зіткнутися із зовнішньою помилкою в процесі, оскільки такі функції часто потребують викликати посеред відновлення після наявної помилки.
Приклад: Ведення журналів
Скажімо, ви хочете записати речі всередині finally
блоку. Це часто буде великою проблемою, якщо реєстрація не може провалитися . Ведення журналу майже напевно може зазнати невдачі, оскільки воно може захотіти додати до файлу більше даних, і це може легко знайти багато причин збою.
Отже, рішення полягає в тому, щоб зробити його таким чином, щоб будь-яка функція ведення журналу, що використовується в finally
блоках, не могла перекинути виклику (він може бути не в змозі, але він не кине). Як ми могли це зробити? Якщо ваша мова дозволяє викидати в контексті, нарешті, за умови наявності вкладеного блоку спробу / лову, це був би один із способів уникнути перекидання абоненту, ковтаючи винятки та перетворюючи їх у коди помилок, наприклад, можливо, реєстрацію можна виконати окремо процес або потік, які можуть вийти з ладу окремо і поза існуючим стеком відновлення помилок. Поки ви можете спілкуватися з цим процесом без можливості зіткнутися з помилкою, це також було б винятком винятків, оскільки проблема безпеки існує лише в цьому сценарії, якщо ми рекурсивно викидаємо з одного і того ж потоку.
У цьому випадку ми можемо позбутися відмови від реєстрації за умови, що вона не викидається, оскільки не входить в систему і нічого не робить, це не кінець світу (це не протікає жодних ресурсів або не може відмовити побічні ефекти, наприклад).
У всякому разі, я впевнений, що ви вже можете уявити собі, як неймовірно складно зробити справді безпечне для виключення програмне забезпечення. Можливо, не потрібно домагатися цього в повній мірі, окрім найважливішого програмного забезпечення. Але варто взяти до уваги, як по-справжньому досягти безпеки винятків, оскільки навіть дуже автори бібліотеки загального призначення тут часто натрапляють і руйнують всю виняткову безпеку вашої програми за допомогою бібліотеки.
SomeFileWriter
Якщо ви SomeFileWriter
можете кинути всередину close
, то я б сказав, що це, як правило, несумісне з обробкою винятків, якщо ви ніколи не намагаєтеся закрити його в контексті, що включає відновлення з існуючого винятку. Якщо код для нього поза вашим контролем, ми можемо бути SOL, але варто сповістити авторів цього кричущого питання щодо виключення та безпеки. Якщо це під вашим контролем, головна моя рекомендація - переконатися, що закриття його неможливо провалити будь-якими способами.
Уявіть, чи дійсно операційна система не змогла б закрити файл. Тепер будь-яка програма, яка намагається закрити файл при закритті, не зможе завершити роботу . Що ми маємо робити зараз, просто тримайте програму відкритою і в кінцівці (напевно, ні), просто просочіть файловий ресурс і проігноруйте проблему (можливо, добре, якщо це не так критично)? Найбезпечніший дизайн: зробіть так, що неможливо не закрити файл.