platform handle что это

[SourcePawn] Пара слов о типе переменных Handle

Kruzya

Главный уборщик говнокода

Открытие плагином нового объекта Handle
По факту, объекты открываются функциями ядра/расширений. Для примера, ф-ия OpenDirectory() возвращает Handle папки (тип Directory), из которого мы можем вытянуть список имеющихся там файлов, папок, и прочих объектов (сокеты UNIX) с помощью нативов, предназначенных для работы с папкой.

Заметка: Пытаться работать с Handle папки функциями типа ArrayList, например, не получится: ядро проверяет тип объекта, и в случае несовпадения, возвращает ошибку.

Закрытие объектов
Уничтожение указателя на объект вручную
Уничтожение указателя может повлечь за собой полное удаление оригинального объекта из памяти, если это будет последний указатель. Для примера, уничтожение указателя на объект соединения с базой (тип IDatabase), повлечёт за собой закрытие соединения, если больше не будет указателей на это соединение. Указатель можно закрыть с помощью оператора языка delete, или функции CloseHandle().

И так, что здесь? Функция ищет в папке некоторый файл, и если находит, возвращает true, не закрывая указатель на Directory объект. В итоге происходит так называемая утечка памяти каждый раз, когда мы проверяем, есть ли нужный нам файл в некоторой папке. Как итог, у плагина копятся указатели на незакрытые объекты.

Когда мы не можем уничтожать указатели?
Есть пара моментов, когда мы не можем уничтожать указатели. Самый банальный пример: тип Plugin. Мы его не можем закрыть, он принадлежит ядру. Мы можем лишь вытягивать информацию из него.
Так же, один плагин не может закрывать указатели другого плагина. Это своеобразная защита от недочётов в API и прочих ошибок. Если Вам надо дать возможность закрыть указатель другим плагином, склонируйте объект на имя нужного плагина (подробнее ниже), свой указатель закройте, и отдайте указатель клона.

О счётчике указателей
Закрытие указателей на объект не всегда означает, что сам объект сразу же будет уничтожен. Пока в памяти есть указатели на него, он будет жить. Как только кол-во указателей достигнет нуля, он будет уничтожен.

Источник

Что такое дескриптор Windows?

Что такое «дескриптор» при обсуждении ресурсов в Windows? Как они работают?

Это абстрактное значение ссылки на ресурс, часто в память, открытый файл или канал.

Например, GetModuleHandle возвращает уникальный идентификатор для загруженного в данный момент модуля. Возвращенный дескриптор может использоваться в других функциях, которые принимают дескрипторы модуля. Это не может быть дано функциям, которые требуют других типов ручек. Например, вы не можете дать дескриптор, возвращенный из GetModuleHandle в, HeapDestroy и ожидать, что он сделает что-то разумное.

Сам по HANDLE себе просто интегральный тип. Обычно, но не обязательно, это указатель на некоторый базовый тип или область памяти. Например, HANDLE возвращаемое значение на GetModuleHandle самом деле является указателем на базовый адрес виртуальной памяти модуля. Но нет правила, утверждающего, что дескрипторы должны быть указателями. Дескриптор также может быть простым целым числом (которое может быть использовано некоторыми Win32 API в качестве индекса в массиве).

HANDLE Это намеренно непрозрачные представления, которые обеспечивают инкапсуляцию и абстрагирование от внутренних ресурсов Win32. Таким образом, Win32 API могли бы потенциально изменить базовый тип за HANDLE, без какого-либо влияния на пользовательский код (по крайней мере, в этом идея).

Оба эти последствия могут быть нежелательными.

Зачем переживать эту проблему? Рассмотрим этот четвертый пример более новой версии этого же API:

Обратите внимание, что интерфейс функции идентичен третьему примеру выше. Это означает, что пользовательский код может продолжать использовать эту новую версию API без каких-либо изменений, даже несмотря на то, что реализация «за кулисами» изменилась для использования NewImprovedWidget структуры вместо этого.

Источник

Как мы две недели охотились на баг NFS в ядре Linux

Подробное описание поисков бага из задачи GitLab, которые привели к патчу для ядра Linux

Пришлось изучать внутренние механизмы Git и сетевой файловой системы NFS. В итоге мы нашли баг в клиенте Linux v4.0 NFS, Тронд Мюклебуст (Trond Myklebust) написал патч для ядра, и с 26 октября этот патч входит в основное ядро Linux.

В этом посте я расскажу, как мы изучали проблему, в каком направлении думали и какие инструменты использовали, чтобы отследить баг. Мы вдохновлялись отличной детективной работой Олега Дашевского, описанной в посте «Как я две недели охотился за утечкой памяти в Ruby».

platform handle что это. Смотреть фото platform handle что это. Смотреть картинку platform handle что это. Картинка про platform handle что это. Фото platform handle что это

А еще это отличный пример того, что отладка ПО с открытым исходным кодом — это командный спорт, в котором участвует много людей, компаний и стран. Девиз GitLab «Каждый может внести свой вклад» справедлив не только для самого GitLab, но и для других проектов с открытым исходным кодом, например для ядра Linux.

Воспроизведение бага

Мы много лет держали NFS на GitLab.com, но потом перестали использовать его для доступа к данным репозиториев на машинах с приложениями. Мы перенесли все вызовы Git в Gitaly. Мы поддерживаем NFS для клиентов, которые управляют своими инсталляциями на GitLab, но никогда не встречали такую проблему, как у вышеупомянутого клиента.

Понятно, что первые два пункта связаны. Когда вы отправляете изменения в ветку Git, Git создает слабую ссылку — длинное имя файла, которое указывает имя ветки для коммита. Например, при отправке в master будет создан файл с именем refs/heads/master в репозитории:

Системные вызовы показали, что команда git gc :

platform handle что это. Смотреть фото platform handle что это. Смотреть картинку platform handle что это. Картинка про platform handle что это. Фото platform handle что это

«В научных целях» мы попросили клиента провести один эксперимент на двух разных машинах (Элис и Боб):
1) В общем томе NFS создайте два файла: test1.txt и test2.txt с разным содержимым, чтобы их было проще различать:

2) На машине Элис файл test1.txt должен быть открыт:

3) На машине Элис непрерывно показывайте содержимое test1.txt :

4) Затем на машине Боб выполните команду:

Есть! Кажется, мы контролируемо воспроизвели проблему. Но в этом же эксперименте на Linux NFS-сервере такая проблема не возникла. Результат был ожидаемым — после переименования принималось новое содержимое:

Вместо устаревшего дескриптора файла Stale file handle сервер Linux NFS v4.0 показывал устаревшее содержимое. Оказывается, разницу в поведении можно объяснить спецификациями NFS. Из RFC 3010:

Дескриптор файла может устареть или потерять силу при переименовании, но не всегда. Специалистам по внедрению серверов рекомендуется принять меры, чтобы дескрипторы файлов не устаревали и не теряли силу таким образом.

Иными словами, NFS-серверы могут выбирать, как себя вести, когда файл переименован, и NFS-сервер вполне обоснованно возвращает ошибку Stale file error в таких случаях. Мы предположили, что причина проблемы — та же, хотя результаты и были разными. Мы подозревали, что дело в проверке кэша, ведь утилита ls в каталоге убирала ошибку. Теперь у нас был воспроизводимый тестовый сценарий, и мы обратились к экспертам — мейнтейнерам Linux NFS.

Ложный след: делегирование на NFS-сервере

Когда мы сумели пошагово воспроизвести ошибку, я написал контактам по Linux NFS о том, что мы узнали. Неделю я переписывался с Брюсом Филдсом, мейнтейнером Linux NFS-сервера, и он предположил, что баг в NFS и нужно изучить сетевой трафик. Он думал, что проблема в делегировании задач на NFS-сервере.

Что такое делегирование на NFS-сервере?

В двух словах, в версии NFS v4 появилась функция делегирования для ускорения доступа к файлам. Сервер может делегировать доступ на чтение или запись клиенту, чтобы клиенту не пришлось постоянно спрашивать у сервера, не изменен ли файл другим клиентом. Проще говоря, делегирование записи — это как одолжить кому-то свой блокнот и сказать: «Ты пока тут пиши, а я заберу его, когда буду готов». И человеку не придется просить блокнот каждый раз, когда нужно что-то записать — у него есть полная свобода действий, пока блокнот не отнимут. В NFS просьба вернуть блокнот называется отзывом делегирования.

Wireshark — это отличный инструмент с открытым исходным кодом для анализа сетевого трафика, особенно для изучения NFS в действии. Мы записали трассировку с помощью следующей команды на NFS-сервере:

Эта команда записывает весь NFS-трафик, который обычно проходит через порт TCP 2049. Раз наш эксперимент удался с NFS v4.1, но не с NFS v4.0, мы могли сравнить поведение NFS в рабочем и нерабочем случае. С Wireshark мы увидели следующее поведение:

NFS v4.0 (устаревший файл)

platform handle что это. Смотреть фото platform handle что это. Смотреть картинку platform handle что это. Картинка про platform handle что это. Фото platform handle что это

NFS v4.1 (рабочий случай)

platform handle что это. Смотреть фото platform handle что это. Смотреть картинку platform handle что это. Картинка про platform handle что это. Фото platform handle что это

Мы повторили эксперимент, но проблема никуда не делась. Мы убедились, что проблема не в NFS-сервере или делегировании, и решили посмотреть на NFS-клиент в ядре.

Копаем глубже: Linux NFS-клиент

Первый вопрос, на который мы должны были ответить мейнтейнерам NFS:

Эта проблема сохраняется в последней версии ядра?

Проблема возникала в ядрах CentOS 7.2 и Ubuntu 16.04 с версиями 3.10.0-862.11.6 и 4.4.0-130 соответственно. Но оба ядра отставали от последней версии, которой на тот момент была 4.19-rc2.

Генетики отслеживают эволюцию в реальном времени по дрозофилам, а мы с помощью первого пункта могли по-быстрому вносить правки в NFS-клиент, не перезапуская ядро. Второй пункт гарантировал, что ядро запустится после установки. К счастью, нас устроили параметры ядра по умолчанию.

Мы убедились, что проблема устаревшего файла никуда не делась в последней версии ядра. Мы спросили себя:

Чтобы ответить на эти вопросы, мы углубились в исходный код NFS. Отладчика ядра у нас не было, так что мы посылали в исходный код вызовы двух типов:

Например, первым делом мы подключились к функции nfs4_file_open() в fs/nfs/nfs4file.c :

После каждого изменения мы перекомпилировали модуль и переустанавливали его в ядро с помощью команд:

С модулем NFS мы могли повторять эксперименты и получать сообщения, чтобы разобраться в коде NFS. Например, сразу видно, что происходит, когда приложение вызывает open() :

VFS реализует open(2), stat(2), chmod(2) и другие системные вызовы. Система VFS использует аргумент имени пути, который им передается, для поиска по кэшу записей каталога (dentry-кэш, или dcache). Это обеспечивает очень быстрый механизм поиска, который преобразует имя пути (или имя файла) в конкретный dentry. Dentry находятся в RAM и никогда не сохраняются на диске — они существуют только для производительности.

И нас осенило — а что если проблема в dentry-кэше?

И в этом эксперименте проблема устаревшего файла не возникла! Наконец, мы напали на след.

Чтобы узнать, почему проблема не возникала в NFS v4.1, мы добавили вызовы pr_info() в каждый блок if в этой функции. Мы поэкспериментировали с NFS v4.0 и v4.1 и нашли особое условие в версии v4.1:

Решение

Оказывается, исправление для бага NFS v4.0 было глубже в базе кода, чем мы думали. Тронд хорошо описал это в патче:

Нужно сделать так, чтобы inode и dentry правильно перепроверялись, когда открывается уже открытый файл. Сейчас мы не перепроверяем ни то, ни другое в NFSv4.0, потому что открытый файл кэширован. Давайте это исправим и будем кэшировать открытые файлы только в особых случаях — для восстановления открытых файлов и возврата делегирования.

Мы убедились, что это исправление решило проблему устаревшего файла, и отправили отчеты о багах командам Ubuntu и RedHat.

Чему мы научились

Баг может прятаться в любом уголке стека программного обеспечения, и иногда искать его нужно за пределами приложения. Если у вас есть полезные связи в мире открытого исходного кода, это облегчит ваш труд.

Огромное спасибо Тронду Мюклебусту за то, что исправил проблему, и Брюсу Филдсу за то, что ответил на наши вопросы и помог разобраться в NFS. Именно за такую отзывчивость и профессионализм мы ценим сообщество разработчиков открытого исходного кода.

Источник

platform handling costs

Смотреть что такое «platform handling costs» в других словарях:

GM F platform — The F platform, or F body, was General Motors small rear wheel drive automobile platform from 1967 until 2002. It was based partially on the GM X platform, which was used for compact applications instead of the sporting intent of the F Body. The… … Wikipedia

Sun Certified Professional — (SCP) is a professional certification program by Sun Microsystems. It is meant to verify a particular skillset in Sun technologies, especially the Java programming language and the Solaris Operating System.Java certification programsun Certified… … Wikipedia

RingGo — is a mobile phone parking service currently offered in public car parks and on street locations in the United Kingdom. The first major implementation (from June 2006) was for the First Great Western Railway. 60 stations were involved such as… … Wikipedia

Import gamers — are a subset of the video game player community that partake in the practice of playing video games from another region, usually from Japan where the majority of games for certain systems originate. Reasons for importing There is no uniform… … Wikipedia

United States — a republic in the N Western Hemisphere comprising 48 conterminous states, the District of Columbia, and Alaska in North America, and Hawaii in the N Pacific. 267,954,767; conterminous United States, 3,022,387 sq. mi. (7,827,982 sq. km); with… … Universalium

Economic Affairs — ▪ 2006 Introduction In 2005 rising U.S. deficits, tight monetary policies, and higher oil prices triggered by hurricane damage in the Gulf of Mexico were moderating influences on the world economy and on U.S. stock markets, but some other… … Universalium

environment — environmental, adj. environmentally, adv. /en vuy reuhn meuhnt, vuy euhrn /, n. 1. the aggregate of surrounding things, conditions, or influences; surroundings; milieu. 2. Ecol. the air, water, minerals, organisms, and all other external factors… … Universalium

building construction — Techniques and industry involved in the assembly and erection of structures. Early humans built primarily for shelter, using simple methods. Building materials came from the land, and fabrication was dictated by the limits of the materials and… … Universalium

Media and Publishing — ▪ 2007 Introduction The Frankfurt Book Fair enjoyed a record number of exhibitors, and the distribution of free newspapers surged. TV broadcasters experimented with ways of engaging their audience via the Internet; mobile TV grew; magazine… … Universalium

Holden VE Commodore — For a complete overview of all Commodore models, see Holden Commodore. Holden VE Commodore Manufacturer Holden (General Motors) Also called … Wikipedia

Ford Crown Victoria — 1998 2002 Ford Crown Victoria LX Manufacturer Ford Motor Company Production 1955–1956 1991–2011 … Wikipedia

Источник

Использование handle и intrusive reference counter-ов в многопоточных средах в языке C

Доступ к одим и тем же данным в нескольких потоках считается плохой практикой, но во многих случаях это неизбежно, и это не тот вопрос, который обсуждается здесь. Вопрос который здесь обсуждается, это как организовать такой доступ наиболее безопасным способом. Также тут не обсуждаются атомарные операции, которые тут упоминаются: разные компиляторы предлагают различные средства для таких операций.

В многопоточной среде при использовании объекта или структуры данных, один из главных вопросов, помимо прочего, это гарантия того, что объект к которому производится доступ все еще жив и память, выделенная для структуры не освобождена.

Это может быть сделано несколькими способами, но мы будем говорить только о двух из них: хэндлы (handles) и встроенные счётчики ссылок (intrusive reference counters).

Хэндлы — это небольшие структуры, которые содержат указатель на объект данных и вспомогательные данные, чтобы гарантировать, что объект еще жив. Как правило для работы с хэндлами пишутся две функции: lock_handle и unlock_handle (имена выбраны произвольно, чтобы показать функциональность). Lock_handle проверяет «живость» объекта, увеличивает атомарный счетчик ссылок и возвращает указатель на объект данных, если он ещё доступен. Если нет, то функция возвращает NULL, или с помощью другого способа даёт знать, что объект больше не доступен. Соответственно своему названию, unlock_handle атомарно уменьшает счетчик ссылок и как только он достигает значения 0, удаляет объект.

Встроенные счетчики ссылок — это атомарные числовые переменные внутри объекта данных, которые считают количество ссылок в программе на указанный объект данных. Как только счетчик ссылок достигает значения 0, объект удаляется.

Теперь давайте взглянем на преимущества и распространенные ошибки обеих стратегий и определим, какую из них лучше использовать в конкретных случаях. Сперва давайте разберемся с встроенными счетчиками ссылок.

Встроенные счетчики ссылок

Встроенные счетчики ссылок, как понятно из названия, должны быть встроены в структуру данных: это атомарное целое число. В простой структуре, назовем ее data_packet_s, подобная модификация может выглядеть так:

Именно это является главным недостатком этого подхода: он нуждается в модификации структуры данных, поэтому использовать его можно только для таких структур, которые мы можем изменить. Мы не можем использовать его с произвольным типом данных или структурами, например, библиотечными.

Любопытно, но это же факт является и преимуществом. Преимущество состоит в том, что нам не нужны никакие дополнительные структуры и дополнительное выделение памяти для подобных структур.

Еще один недостаток, или, скорее, специфика этого подхода заключается в следующем: доступность объекта должна быть гарантирована, когда происходит приращение счетчика ссылок. Другими словами, мы не можем просто хранить указатель на объект и дожидаться момента, когда мы хотим получить к нему доступ, и затем просто увеличить счетчик ссылок и далее работать с объектом, поскольку между моментом когда мы сохранили указатель и моментом когда мы начинаем использовать объект он может быть уничтожен, при этом уничтожается и счётчик ссылок. Давайте продемонстрируем простой случай такого события, используя простой условный язык.

platform handle что это. Смотреть фото platform handle что это. Смотреть картинку platform handle что это. Картинка про platform handle что это. Фото platform handle что это

В указанном случае, чтобы обеспечить целостность объекта, придется увеличить количество ссылок в потоке 1 и передать полученную ссылку в собственность потоку 2 (уменьшать reference_count будет поток 2 после того как объект больше не нужен). Другими словами, эта схема может работать очень хорошо, если необходимо обработать объект в другом потоке, и забыть о нем. Это может быть продемонстрировано с помощью следующей иллюстрации:

platform handle что это. Смотреть фото platform handle что это. Смотреть картинку platform handle что это. Картинка про platform handle что это. Фото platform handle что это

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

Тут становится виден и второй недостаток: мы увеличивать и уменьшаем счетчик ссылок в разных потоках, что часто может приводить к различным ошибкам, начиная от утечек памяти когда ссылка не освобождается, заканчивая двойным освобождением ссылки и соответственно ложным освобождением объекта, что ведёт к его удалению во время того как он ещё используется в другом потоке или потоках.

Эта схема реализует так называемый «shared ownership», когда потоки совместно владеют объектом, и нить, уменьшающая счетчик ссылок до нуля, будет удалять объект.

И если вы хотите сохранить указатель на объект в одном из потоков, и использовать его при необходимости вы увидите, что память выделенная вашей программой будет расти с каждым хранящимся объектом, потому что поток никогда не освободит ссылку на объект, «живость» которого он хочет гарантировать.

Другая возможная ошибка в таком подходе это обращение к косвенно переданным объектам, то есть объектам, которые передаются по указателю, содержащемуся в одном из других, прямо или косвенно переданных объектов, которые ссылаются, но не владеют объектом. Все такие объекты должны быть известны, при этом должен быть обеспечен безопасный доступ ко всем подобным объектам, если они используются наряду с основным.

Итак, давайте просуммируем недостатки и преимущества встроенных счетчиков ссылок:

Хэндлы

Хэндлы — это легкие структуры, которые передаются по значению, они ссылаются на объект, управляют ссылками и обеспечивают целостность объекта.

Очевидно, что хэндлы, которые ссылаются на один и тот же объект, будут иметь указатель на один счетчик ссылок. Как правило, это означает, что счетчик ссылок должен быть выделен на куче динамически. Простейшая структура хэндла, которая первой приходит на ум, может выглядеть так:

Где reference_count выделяется при создании первого хэндла на данный объект.

Первый недостаток такого подхода уже очевиден. Мы должны управлять еще одной дополнительной структурой, выделять еще одну область памяти для счетчика ссылок. Но это окупается, если вы хотите использовать подсчет ссылок со структурами, к которым вы не имеете доступа.

Типичное применение такого хэндла будет следующим:

Давайте посмотрим, что происходит, когда последняя ссылка на объект удалена. Прежде всего, мы, очевидно, хотим, чтобы удалился управляемый объект. Если объект является простым куском выделенной памяти, мы просто хотим, чтобы память, используемая объектом была освобождена. Но в большинстве случаев это не так. Как и в вышеупомянутом примере data_packet_s, где мы хотели бы освободить также и память data_buffer. Если мы используем хэндл для только одного типа объектов, это не создаёт большой проблемы. Но если мы хотим, чтобы хэндл мог обрабатывать разыличные типы, это привносит ещё один вопрос: как правильно удалить управляемый объект?
Мы можем добавить в хэндл ещё одно поле: указатель на функцию, которая будет использоваться для уничтожения/освобождения управляемого объекта. Теперь хэндл выглядит следующим образом:

Теперь release_handle не обязательно знать специфику объекта, чтобы удалить его, он просто будет использовать функцию, которую мы сохранили в хэндле, так что давайте вернёмся к тому, что происходит, когда последняя ссылка будет освобождена.

После этого мы хотели бы освободить память, которая содержит reference_count. Но не тут то было: сделать это будет ужасной ошибкой. Если другие хэндлы на этот же объект до сих пор хранятся в других потоках, после освобождения памяти они будут ссылаться на reference_count, который уже удален. А на следующей попытке получить доступ к объекту, мы будем иметь попытку обращения к освобожденной памяти.

Есть ли решение, которое позволило бы не допустить утечки памяти но при этом избежать обращений к освобождённой памяти счетчиков ссылок? Такой способ есть. Это пул объектов, который будут управлять освобожденными счетчиками. И вот тут-то и появляется проблема, найти которую может быть довольно непросто, и которая известна как «проблема ABA». Представьте себе, ситуацию, когда у вас есть хэндл на объект. Один из потоков удаляет объект. Затем конструируется другой объект, и для него создается управляющих хэндл. Что при этом произойдет?

Когда объект уничтожен, reference_count связанный с данным объектом (назовем его object1) освобождается обратно в пул объектов со значением 0. Пока что всё идёт по плану. Но когда выделяется другой хэндл для нового объекта (назовем его object2), то reference_count, который будет связан с этим объектом берется из пула объектов, при этом данный reference_count устанавливается в 1. Теперь представьте, что поток хранящий хэндл на object1 пытается получить указатель. Это удастся, потому что reference_count на который указывает данный хэндл уже не 0, хотя он и принадлежит теперь object2. Функция блокировки вернет неверный указатель, программа (если повезёт) потерпит крушение, или (если не повезёт) повредит содержимое памяти обращением к освобожденному участку.
Решение, разумеется, существует, иначе я бы не писал всё это.

Мы хотим сделать структуру handle_s настолько легкой, насколько это возможно, чтобы иметь возможность передать ее по значению, а не по указателю, так что сделаем следующее: создадим две структуры, одна из которых будет «слабым» хэндлом, то есть не ограничивать и не проверять объект, которым она управляет, а другой будет «сильным» хэндлом, т.е. таким, который будет иметь жесткую связь с конкретным управляемым объектом и вернет NULL, если «слабый» хэндл связанный с ним больше не ссылается на тот же объект.

Давайте определим их так:

Итак, как вы видите, теперь обе ручки имеют поле «version», и strong_handle_s уже не имеет указателя на объект, так как он теперь хранится в общем для объекта weak_handle_s.

Давайте посмотрим, как он защищает нас от проблемы ABA, показанной выше.

В strong_handle_s и weak_handle_s, которые ссылаются на один и тот же объект имеются поля «version», которые равны друг другу.
Всякий раз, когда ручка освобождается и weak_handle_s помещается обратно в пул объектов, мы будем увеличивать номер версии в weak_handle_s.

В следующий раз, если освобожденный в пул weak_handle_s переиспользуется для обработки другого объекта, он будет иметь номер версии, отличный от номера версии который был у объекта, который был освобожден. Теперь в функции lock_handle, сравнивая поля версии в обоих, «слабом» и «сильном» хэндле, мы можем сказать, ссылается ли weak_handle_s по-прежнему на тот объект, указатель которого мы пытаемся получить из strong_handle_s, и вернуть NULL, если это не так.

Так что, как мы видим, хэндлы приносят некоторые довольно сложные проблемы, но он также имеет и свои плюсы: хэндл может быть сохранен и забыт, пока нам не понадобится управляемый им объект; хэндл не является встроенным, что означает, что мы можем использовать его практически с любыми типами данных.

Кроме того, передача хэндла не обязательно означает передачу права собственности на объект, так что он может быть использован вместо указателя в качестве безопасной ссылки на объекты везде, где они не являются собственностью ссылающегося объекта (в структурах данных и т.д.).

Таким образом хэндлами сложнее управлять. Но и возможности их гораздо мощнее.

Выводы

Встроенный счетчик ссылок реализует общие сильные ссылки (strong reference), которые должны удерживать счетчик ссылок, пока объект не будет гарантированно невостребован. Пока все ссылки не будут разлочены, объект не будет удален. Если ссылка разблокирована в потоке, который имел в собственности лишь одну ссылку, этот поток уже больше не сможет гарантированно безопасно получить еще одну ссылку.

Хэндл реализует общую слабую ссылку (weak reference). Если объект жив, функция lock_handle вернет указатель на запрашиваемый объект, и объект гарантированно не будет удален, пока хэндл не будет разлочен. Это безопасно для блокировки и разблокировки сколько угодно раз, так как хэндл является отдельным объектом, и гарантированно является валидным участком памяти. Соответствующие меры должны быть приняты, чтобы гарантировать, что разделяемая память счетчика ссылок не освобождается, когда объект достигает счетчика ссылок 0, и что повторно используемый счетчик ссылок не используется, чтобы проверить количество ссылок на уже освобожденные объекты.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *