Во время реализации обёртки для некой внешней нэтивной библиотеки я столкнулся с проблемами связанными с SafeHandle.
Итак: Имеется некая нэтивная библиотека, которая реализует сетевую работу с DICOM (медицинский стандарт).
При написании обёртки сделан свой SafeHandle для сущности отвечающей за сетевое взаимодействие (далее MSM). При работе с этой MSM есть блокирующие операции, например, операция ожидания входящего соединения, так же есть просто длительные операции. Соответственно оно крутится в отдельном потоке.
Для того что бы блокирующая операция завершилась необходимо вызвать метод по уничтожению MSM, который, собственно, завёрнут в SafeHandle.ReleaseHandle.
Проблема, заключается в том, что если в данный момент для MSM активна блокирующая или длительная операция, то при вызове SafeHandle.Dispose (вызов происходит из другого потока, конечно) до вызова ReleaseHandle, в первом случае, дело не доходит никогда, а во втором, оно вызывается только по завершении длительной операции. Сейчас я борюсь с этим передавая в такие функции IntPtr из DangerousGetHandle, но как то это, IMHO, не правильно.
Вопрос, как мне избавиться от IntPtr и использовать везде SafeHandle? Или я чего то не понимаю?
Здравствуйте, DiRTy GaRRy, Вы писали:
DG>Проблема, заключается в том, что если в данный момент для MSM активна блокирующая или длительная операция, то при вызове SafeHandle.Dispose (вызов происходит из другого потока, конечно) до вызова ReleaseHandle, в первом случае, дело не доходит никогда, а во втором, оно вызывается только по завершении длительной операции.
Дело видимо в том, что рантайм автоматически увеличивает внутренний счётчик при передаче потомка SafeHadle в нативный код, как раз для предотвращения его преждевременного закрытия. Если Вы уверены, что для Вашего случая SafeHandle подходит наилучшим образом, то можно, во-первых, вызывать вручную DangerousRelease. Во-вторых, метод Dispose(bool) — виртуальный, Вы его можете переписать так, как Вам надо. И в третьих, можно добавить свой, специфичный для Вашего случая, метод, который будет выполнять закрытие хёндла. Не забывайте только при этом его инвалидировать.
Здравствуйте, Jolly Roger, Вы писали:
JR>Здравствуйте, DiRTy GaRRy, Вы писали:
DG>>Проблема, заключается в том, что если в данный момент для MSM активна блокирующая или длительная операция, то при вызове SafeHandle.Dispose (вызов происходит из другого потока, конечно) до вызова ReleaseHandle, в первом случае, дело не доходит никогда, а во втором, оно вызывается только по завершении длительной операции.
JR>Дело видимо в том, что рантайм автоматически увеличивает внутренний счётчик при передаче потомка SafeHadle в нативный код, как раз для предотвращения его преждевременного закрытия. Если Вы уверены, что для Вашего случая SafeHandle подходит наилучшим образом, то можно, во-первых, вызывать вручную DangerousRelease. Во-вторых, метод Dispose(bool) — виртуальный, Вы его можете переписать так, как Вам надо. И в третьих, можно добавить свой, специфичный для Вашего случая, метод, который будет выполнять закрытие хёндла. Не забывайте только при этом его инвалидировать.
Ну я, в принципе, про некий счётчик тоже догадывался. Если же далать через всяческие дополнительные методы, то тут получается, что SafeHandle и не нужен, так как проще реализовать IDisposable и не париться. Мне просто хотелось сделать работу с unmanaged ресурсами, как это рекомендует MS, но по ходу дела не получится.
Здравствуйте, DiRTy GaRRy, Вы писали:
DG>Ну я, в принципе, про некий счётчик тоже догадывался. Если же далать через всяческие дополнительные методы, то тут получается, что SafeHandle и не нужен, так как проще реализовать IDisposable и не париться. Мне просто хотелось сделать работу с unmanaged ресурсами, как это рекомендует MS, но по ходу дела не получится.
SafeHandle наследует от CriticalFinalizerObject, для которого рантайм даёт дополнительные гарантии по освобождению, кроме того автоматически предоставляется конвертация в IntPtr и обратно при вызове нативных методов. Разумеется, волшебства нет, и не вполне стандартное поведение придётся реализовывать самостоятельно. Ну а если вышеперечисленное не нужно, то конечно, можно и не париться, хотя я не вижу, чем свой объект, реализующий IDisposable, будет сколь-нибудь заметно проще
Здравствуйте, DiRTy GaRRy, Вы писали:
DG>Ну я, в принципе, про некий счётчик тоже догадывался. Если же далать через всяческие дополнительные методы, то тут получается, что SafeHandle и не нужен, так как проще реализовать IDisposable и не париться. Мне просто хотелось сделать работу с unmanaged ресурсами, как это рекомендует MS, но по ходу дела не получится.
SafeHandle отвечает за освобождение используемого Handle в случае если произодет исключение при маршаллинге результата. Без этого есть возможность что, вновь полученный handle будет просто потерян.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, DiRTy GaRRy, Вы писали:
DG>Итак: DG> DG>Имеется некая нэтивная библиотека, которая реализует сетевую работу с DICOM (медицинский стандарт). DG>При написании обёртки сделан свой SafeHandle для сущности отвечающей за сетевое взаимодействие (далее MSM). При работе с этой MSM есть блокирующие операции, например, операция ожидания входящего соединения, так же есть просто длительные операции. Соответственно оно крутится в отдельном потоке. DG>Для того что бы блокирующая операция завершилась необходимо вызвать метод по уничтожению MSM, который, собственно, завёрнут в SafeHandle.ReleaseHandle. DG>
Извините, что поднимаю эту древнюю тему. Насколько я понял, проблема в том что
— один поток юзает ресурс
— второй поток пытается этот ресурс убить
— .NET второму потоку не позволяет такое делать
Ну дык, полагаю, .NET тут прав на все 100%. И здесь нужно менять принцип прерывания работы с этим ресурсом. Или нет?
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
[Skip]
КД>Извините, что поднимаю эту древнюю тему. Насколько я понял, проблема в том что КД>- один поток юзает ресурс КД>- второй поток пытается этот ресурс убить КД>- .NET второму потоку не позволяет такое делать
КД>Ну дык, полагаю, .NET тут прав на все 100%. И здесь нужно менять принцип прерывания работы с этим ресурсом. Или нет?
Без SafeHandle всё отлично прерывается и работает. Библиотека, которая отдаёт хэндл, сторонняя, так поменять API не представляется возможным. Я просто хотел сделать работу с unmanaged ресурсами "правильно", то есть через SafeHandle, ан нет, не получилось.
Здравствуйте, Igor Vyatkin, Вы писали:
КД>>Извините, что поднимаю эту древнюю тему. Насколько я понял, проблема в том что КД>>- один поток юзает ресурс КД>>- второй поток пытается этот ресурс убить КД>>- .NET второму потоку не позволяет такое делать
КД>>Ну дык, полагаю, .NET тут прав на все 100%. И здесь нужно менять принцип прерывания работы с этим ресурсом. Или нет?
IV>Без SafeHandle всё отлично прерывается и работает. Библиотека, которая отдаёт хэндл, сторонняя, так поменять API не представляется возможным. Я просто хотел сделать работу с unmanaged ресурсами "правильно", то есть через SafeHandle, ан нет, не получилось.
И никаких падений/утечек ресурсов?
Получается что апишная функция закрытия ресурса прерывает работу другой апишной функции? Вообще — толковая идея
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, Igor Vyatkin, Вы писали:
КД>>>Извините, что поднимаю эту древнюю тему. Насколько я понял, проблема в том что КД>>>- один поток юзает ресурс КД>>>- второй поток пытается этот ресурс убить КД>>>- .NET второму потоку не позволяет такое делать
КД>>>Ну дык, полагаю, .NET тут прав на все 100%. И здесь нужно менять принцип прерывания работы с этим ресурсом. Или нет?
IV>>Без SafeHandle всё отлично прерывается и работает. Библиотека, которая отдаёт хэндл, сторонняя, так поменять API не представляется возможным. Я просто хотел сделать работу с unmanaged ресурсами "правильно", то есть через SafeHandle, ан нет, не получилось.
КД>И никаких падений/утечек ресурсов?
КД>Получается что апишная функция закрытия ресурса прерывает работу другой апишной функции? Вообще — толковая идея
К сожалению, асинхронного API у библиотеки нет, так что приходится извращаться. Библиотека отдаёт некий внутренний хэндл сервиса. Затем мы стартуем блокирующую функцию ожидания приёма данных. В моём случае, к сожалению, прервать эту функцию можно, только вызовом закрытия хэндла сервиса или закрытием приложения. Последнее меня не устраивает, так как мне нужно стартовать/стопать сервисы в рамках одного приложения динамически. Вот и приходится извращаться.
Здравствуйте, Igor Vyatkin, Вы писали:
IV>К сожалению, асинхронного API у библиотеки нет, так что приходится извращаться. Библиотека отдаёт некий внутренний хэндл сервиса. Затем мы стартуем блокирующую функцию ожидания приёма данных. В моём случае, к сожалению, прервать эту функцию можно, только вызовом закрытия хэндла сервиса или закрытием приложения. Последнее меня не устраивает, так как мне нужно стартовать/стопать сервисы в рамках одного приложения динамически. Вот и приходится извращаться.
Медицинское ПО, говоришь? Нафиг, нафиг
-- Пользователи не приняли программу. Всех пришлось уничтожить. --