00001 /* 00002 * CntmLib - Подсчет ссылок, потоки, синхронизация, асинхронные процедуры, события 00003 * Copyright (c) 2005, Овсеевич Роман, CntmLib@mail.ru 00004 * _______________________________________________________________________________ 00005 * Разрешено свободное использование, копирование, распространение, изменение 00006 * (изменение сведений об авторских правах запрещено). Запрещена продажа и 00007 * включение всей библиотеки или ее частей в другие библиотеки. В сведениях об 00008 * авторских правах на программу (или сведениях о программе, об авторах, 00009 * использованных средствах разработки и т.д.) должна быть указана информация 00010 * о библиотеке CntmLib, ее авторе и, возможно, сайте или email'е. 00011 * Библиотека поставляется "как есть", без каких-либо гарантий со стороны автора. 00012 */ 00013 00014 #ifndef CNTM_REFBASEEX_H 00015 #define CNTM_REFBASEEX_H 00016 #include <boost/limits.hpp> 00017 #include <Cntm/SystemUtils/AtomicUtils.h> 00018 #include <Cntm/Exceptions/IllegalStateException.h> 00019 #include <Cntm/Exceptions/NullArgException.h> 00020 #include <Cntm/RefCount/RefObjectUsedException.h> 00021 #include <Cntm/RefCount/ForceDeleteRefObjectException.h> 00022 #include <Cntm/RefCount/RefBase.h> 00023 00024 namespace Cntm 00025 { 00026 00027 /** 00028 * Более сложная реализация подсчета ссылок. Применяется в особых случаях, когда возможностей RefBase недостаточно. Кроме функциональности RefBase он обеспечивает расширенные средства по деинициализации объекта при уничтожении ссылок на него. Применяется в качестве базового класса или примеси для пользовательских классов, которые управляются подсчетом ссылок. 00029 * 00030 * Особенности использования класса RefBaseEx в том, что он позволяет использовать ссылочные указатели в деструкторе и производить отложенное уничтожение. 00031 * 00032 * Данный класс содержит виртуальный метод OnReleaseInstance(), который вызывается перед деструктором когда ссылок на объект не осталось и он переходит в удаляемое состояние. Этот метод может использоваться для запуска длительной асинхронной операции деинициализации объекта, например, в другом потоке. Чтобы обрабатывать это событие пользовательские классы должны переопределить этот метод. Реализация метода в RefBaseEx не выполняет ни каких действий. 00033 * 00034 * Выше упоминалось, что при исчезновении ссылок на объект он переводится в удаляемое состояние, в котором подсчет ссылок не работает (см. IRefObject). В отличие от RefBase RefBaseEx позволяет вернуть объект из удаляемого состояние в рабочее, по сути дела начать новый цикл использования объекта. Для этого служит метод ReanimateInstance(). Данному методу передается ссылочный указатель, который инициализируется указателем на данный объект, после завершения метода кол-во ссылок устанавливается в 1 и появляется возможность использовать ссылочные указатели на данный объект. Когда все вновь созданные ссылки на данный будут утеряны, объект вновь перейдет в удаляемое состояние. Второй аргумент данного метода влияет на то, будет ли вызван метод OnReleaseInstance() при повторном переходе в удаляемое состояние (по умолчанию не будет). 00035 * 00036 * Рассмотрим несколько сценариев использования: 00037 * 00038 * 1. Использование ссылочных указателей в деструкторе. Для этого в деструкторе следует вызвать ReanimateInstance(), передав ему принимающий указатель, который после завершения метода будет указывать на данный объект. Второй аргумент функции не используется. После этого можно использовать ссылочные указатели на данный объект. Особенности данного сценария: 00039 * - когда счетчик ссылок достигнет 0, OnReleaseInstance вызываться не будет; 00040 * - после выхода из деструктора пользовательского класса ссылок на объект оставаться не должно. В противном случае для сигнализации о такой ошибке в деструкторе RefBaseEx будет сгенерировано исключение RefObjectUsedException для сигнализации о серьезной ошибке, которое будет автоматически перехвачено библиотекой. 00041 * 00042 * 2. Отложенное уничтожение. Для этого следует переопределить метод OnReleaseInstance() и в нем вызвать ReanimateInstance(), передав первым параметром указатель, который будет указывать на данный объект, а вторым - флаг нужно ли вызывать OnReleaseInstance в следующий раз (если ничего не указывать, то вызова не будет). После этого можно передать указатель другим объектам для операций деинициализации или запустить поток, выполняющий эти действия. В последнем случае следует сохранять ссылочный указатель. Это можно обеспечить либо передачей указателя потоку как параметра или использовать поле класса. Перед завершением работы поток должен уничтожить или сбросить указатель в NULL. Особенности данного сценария: 00043 * - запрещается вручную удалять объект из метода OnReleaseInstance; 00044 * - когда счетчик ссылок повторно достигнет 0, будет опять вызван метод OnReleaseInstance() (если это явно указано при предыдущем вызове). Этот вызов может придти, когда первый OnReleaseInstance еще не завершил работу (например, если полученный указатель будет уничтожен в самом OnReleaseInstance() или фоновый поток закончит работу раньше, чем завершится первый OnReleaseInstance()). 00045 * - в случае повторных вызовов OnReleaseInstance() библиотека гарантирует, что деструктор объекта будет вызван после завершения работы всех вызывов OnReleaseInstance, даже если они вложены или выполняются параллельно. 00046 * 00047 * Объекты классов, использующих RefBaseEx, должны размещаться только в динамической памяти, т.к. их поведение сильно связано с подсчетом ссылок, кроме того, принудительное удаление таких объектов запрещено (в деструкторе RefBaseEx будет сгенерировано исключение ForceDeleteRefObjectException, которое будет автоматически перехвачено библиотекой, сигнализирующее о неправильном использовании объекта). 00048 * 00049 * Еще одно назначение класса RefBaseEx - блокировка завершения программы при наличие объектов, унаследованных от RefBaseEx. Более подробное описание этой возможности приведено в документации по методу WaitDestroyAll. 00050 * 00051 * Данный класс обеспечивает многопоточность. 00052 * @author Овсеевич Р. 00053 * \ingroup RefCount 00054 */ 00055 class RefBaseEx: public RefBase 00056 { 00057 public: 00058 00059 /** 00060 * Ожидание разрушения всех объектов, унаследованных от RefBaseEx, в течении MSecTimeout мсек. Если задержка не указана, то ожидание будет длиться бесконечно. 00061 * 00062 * Результат: true - все объекты уничтожились, false - некоторые объекты остались неуничтоженными. 00063 * 00064 * Функцией можно пользоваться одновременно из нескольких потоков. 00065 */ 00066 static inline bool WaitDestroyAll(uint MSecTimeout = std::numeric_limits<uint>::max()) 00067 { 00068 return SpecUtils::RefObjectImplUtils::WaitDestroyAll(MSecTimeout); 00069 } 00070 00071 protected: 00072 00073 /** 00074 * Конструктор. Производит инициализацию объекта, устанавливает кол-во ссылок в 0. 00075 */ 00076 RefBaseEx(): RefBase(), reanimateCounter(1), enableOnReleaseNotify(true) 00077 { 00078 // Сообщаем о создании нового объекта с подсчетом ссылок 00079 // для обеспечения ожидания уничтожения всех таких объектов. 00080 SpecUtils::RefObjectImplUtils::IncRefObjectCount(this); 00081 } 00082 00083 /** 00084 * Виртуальный деструктор. Выполняет ряд проверочных действий. 00085 * 00086 * Исключения: 00087 * RefObjectUsedException - когда на объект еще имеются ссылки. 00088 * ForceDeleteRefObjectException - когда произошла попытка принудительного удаления объекта. 00089 * Эти исключения обрабатываются самой библиотекой и служат отладочным целям. 00090 */ 00091 ~RefBaseEx() 00092 { 00093 try 00094 { 00095 // Сообщаем об уничтожении объекта с подсчетом ссылок. 00096 SpecUtils::RefObjectImplUtils::DecRefObjectCount(this); 00097 00098 // Попытка удалить объект вручную. В нормальном состоянии 00099 // reanimateCounter должен равняться 1 (перед деструктором 0 + 1). 00100 if (!reanimateCounter.DecAndTest()) 00101 throw ForceDeleteRefObjectException("Cntm::RefBaseEx::~RefBaseEx"); 00102 } 00103 catch (...) 00104 { 00105 } 00106 } 00107 00108 /** 00109 * Перевести объект из удаляемого состояния, в которое он переходит при уничтожении всех ссылок на него, в рабочее. После вызова этого метода счетчик ссылок устанавливается в 1, становятся вновь доступны ссылочные указатели на данный объект. Если объект не в удаляемом состоянии, то производится установка RepeatOnRelease и возвращается ссылочный указатель. 00110 * 00111 * Данный метод должен вызываться только из переопределенного виртуального метода OnReleaseInstance() или деструктора. 00112 * 00113 * Параметры: 00114 * Первый параметр этой функции - Dest - адрес ссылочного указателя, который принимает новый указатель на данный объект. Он не должен равняться NULL. Тип Dest является аргументом шаблона (ReanimateInstance - шаблонная функция), поэтому к нему выдвигается такое требование: он должен быть адресом ссылочного указателя на класс данного объекта или класс предка. 00115 * Второй аргумент - RepeatOnRelease - разрешает или запрещает вызов метода OnReleaseInstance() при следующем переходе объекта в удаляемое состояние. Если он равен false, то будет сразу вызван деструктор, если true, сначала вызовется OnReleaseInstance(). По умолчанию параметр равен false. 00116 * 00117 * Исключения: 00118 * NullArgException - если первый параметр Dest равен NULL. 00119 * BadArgException - если тип Dest'а не является типом ссылочного указателя на класс данного объекта или на класс его предка. 00120 */ 00121 template < typename T > 00122 void ReanimateInstance(RefPtr < T > * Dest, bool RepeatOnRelease = false) 00123 { 00124 // Получающий указатель должен быть задан обязательно. 00125 if (Dest == NULL) 00126 throw NullArgException("Cntm::RefBaseEx::ReanimateInstance", 00127 "Указатель обязательно должен быть задан"); 00128 00129 // Если объект в удаляемом состоянии, то реинициализируем кол-во 00130 // ссылок. 00131 ReinitReferenceCounter(); 00132 *Dest = dynamic_cast<T*>(this); 00133 00134 // Устанавливаем флаг повторных вызовов OnDeleteInstance(), увеличиваем 00135 // счетчик кол-ва текущих жизненных циклов. 00136 enableOnReleaseNotify = enableOnReleaseNotify && RepeatOnRelease; 00137 reanimateCounter.Inc(); 00138 } 00139 00140 /** 00141 * Данный виртуальный метод вызывается когда объект переходит в удаляемое состояние, если его вызов не был запрещен в предыдущем вызове данного метода с помощью ReanimateInstance. Для обработки этого события пользовательский класс может переопределить метод. 00142 * 00143 * Реализация этого метода для RefBaseEx не выполняет ни каких действий. 00144 * 00145 * Все исключения, сгенерированные в переопределнном методе, будут проигнорированы. 00146 */ 00147 virtual void OnReleaseInstance() {} 00148 00149 /** 00150 * Метод обработки события уничтожения последней ссылки на данный объект. Он позволяет либо запустить фоновый процесс уничтожения с длительными действиями, либо уничтожить объект. 00151 * 00152 * Без крайней необходимости этот метод переопределять не рекомендуется. 00153 */ 00154 void DeleteInstance(); 00155 00156 private: 00157 00158 /** 00159 * Счетчик кол-ва текущих жизненных циклов. 00160 */ 00161 SpecUtils::AtomicVariable reanimateCounter; 00162 00163 /** 00164 * Разрешен ли вызов метода оповещения о возможности уничтожить объект. 00165 */ 00166 bool enableOnReleaseNotify; 00167 }; 00168 00169 } 00170 00171 #endif //CNTM_REFBASEEX_H
© Овсеевич Р.В. Документация по CntmLib 1.1.4 от 28 May 2008. Создано системой 1.5.3 |