modularMX
smart pointer implementation - please read Thread Safety note below.
More...
#include <mxmSmartHandle.h>
Inheritance diagram for mxm::smart< T >:
Public Member Functions | |
smart () | |
[Not synchronized] Constructs a default-state-handle: null -handle (=> alone), no lock. | |
smart (const smart< T > &other) | |
[Synchronized via target group's lock] Copy constructor. | |
smart (T *handlee) | |
[Not synchronized] Constructor immediately assigning a dumb handlee pointer. | |
~smart () | |
[Synchronized] | |
smart< T > & | operator= (const smart< T > &other) |
[*** Synchronized, but SAFETY BREACH] Assignment operator. | |
smart< T > & | operator= (T *handlee) |
[*** Synchronized, but SAFETY BREACH] Assigns handlee object. | |
T * | operator-> (void) const |
[Not synchronized] Dereference operator. | |
T & | operator * (void) const |
[Not synchronized] Dereference operator. | |
bool | operator== (const smart< T > &other) const |
[Not synchronized] Comparison. | |
template<class NewT> | |
operator smart (void) const | |
[Synchronized] Dynamic type cast: This is advanced magic stuff for inheritance-based type conversion between smart handles for different, but related handle types. | |
Private Member Functions | |
void | deleteHandlee (void) |
[Not synchronized] (Re)implemented. |
modularMX
smart pointer implementation - please read Thread Safety note below.
[khe]
Kai HergenroetherThe used smart pointer semantics is shared: By passing an object allocated on the heap to a smart pointer, you thereby create the first smart handle for that handlee object. This handle can then be copied by various methods, for example by assigning it to other, type-compatible smart handles or by passing it as parameter into methods. All the resulting copies, that all refer to the same one handlee object on the heap, are said to form a handle group. The moment the handlee was passed to the first of the handles, ownership over it was transfered from the current context to this handle group. At the time the last member of the group dies, the handlee object is automatically deleted.
Smart handles have the following benefits:
C++
scopes (blocks delimited by {}
brackets) by putting them as automatic (local) variables on the stack, they support the RAII
programming technique (resource allocation is initialization), and thus Note that the terms smart pointer and smart handle will in the following be used synonymously.
Null
-ness
In analogy to null
-pointers, mxm
smart pointers can also be null
-handles. null
-handles result from
0
to the copy constructor0
via the assignment operator. If in this case the smart pointer was the only/last handle for a given handlee object, the object is deleted. A potentially assigned synchronization lock in owned
mode is likewise discarded.
null
-handle Properties
null
-handle is always "alone", which means it is sole member of the handle group spawned by itself.null
-handle is generally a bad idea, because the lower bytes in the virtual address space usually are under the jurisdiction of the operating system and as a result access protected. In most cases you don't have any business of poking around there anyways.
null
-ness
To test whether or not a given smart handle is a null
handle, either use null() or the overloaded !
operator as in
if(!handle) {
// _handle_ is _null_ handle }An implicit conversion to
bool
was not included in the class to avoid the risk of comparing smart handles with different template arguments and thus incomatible types. So don't use if(handle) handle->doSomething();
As of 20070328
, mxm
smart handles provide synchronization mechanisms to allow safe operation in multithreaded scenarios. Such scenarios can for example be found
FUSE
-based unirecfs
filesystem daemon for retrieving recorded events from CF/SD flashcards. FUSE
filesystem daemons usually are massively multithreaded to deliver good performance for concurrent file accesses. Since we wish to exploit that, and since mxm::smart
handles are used bigtime to tie together the various components of a unirec
storage facility, they better be threadsafe.MxSDK
client applications as the MxControlCenter
and the MxPEG Server
. The SDK's component framework uses mxm::smart
handles to facilitate the handling of loaded components and their resources. Since SDK clients might very well be multithreaded, as is the MxPEG Server
in quite a relaxed fashion, these handles, again, better be threadsafe.
Usage Practices
below.Note that the handlee object itself will not be protected by the smart handle group. If you spread the members of a given handle group out over multiple threads that then use the respective handlee object as a shared resource, you yourself are responsible of providing adequate synchronization when accessing it.
This section is only relevant for the special case in which you intend to use a given smart handle instance as a shared resource itself, meaning: in case you wish to access it simultaneously from multiple threads (the handle, not the handlee object!). This might for example be the case if you have used smart pointers to tie together a complex object structure that is then accessed by multiple threads without having access locks preventing the threads from simultaneously following (dereferencing) the object references.
The thread safety semantics for a given smart handle object are the same as for the boost::shared_ptr
implementation, and defined as follows:
mxm::smart
objects offer the same level of thread safety as built-in types.mxm::smart
instance can be "read" (accessed using only const
operations) simultaneously by multiple threads. Specifically, a given smart handle can be simultaneously dereferenced, potentially together with invocations of other const
operations. Note that for example copying a given handle by construcing another one with the copy constructor mxm::smart<T>(const mxm::smart<T> &other)
is a const
("read") operation on the handle other
.mxm::smart
instances can be "written to" (accessed using mutable operations such as assignment operator=()
) simultaneosly by multiple threads, even when these instances are copies and refer to the same handlee object (= are in the same handle group).
mxm
library it is quite common to find so-called generator methods that dynamically create a datastructure on the heap and conveniently return a smart handle to it, as for example mxmString::tokenize(). In most cases these handles won't feature a lock, because the called method is a library functionality that might very well be used in a monothreaded setting. There might be other methods however that simply return smart handles that were passed into their objects from the outside earlier. So there is a certain uncertainty involved when accepting a smart handle as a return value regarding whether they are protected or not. This uncertainty might hurt you if you need to ensure protection before using the handle further, because mxmSmartHandleCore::setLock() will fail badly if its preconditions are not met. So do some documentation:
To deliver the above straightforward, boost::shared_ptr
-compatible thread safety semantics, the following conventions were used for the implementation:
const
, so everytning is OK because simultaneous activation is forbidden by the above thread safety semantics. const
operations operator!()
(test for null
-ness) null()
(test for null
-ness) operator->()
(dereferencing) operator*()
(dereferencing) operator==()
(comparison) template<class NewT> operator smart<NewT>()
(dynamic_cast
-style type conversion) const
, so that's ok.
To summarize, since all potentially malicious operations are non-const
, it is safe to simultaneously activate const
operations on the same given smart handle object.
Although this class is a C++
template class, the main functionality is inherited from the non-template mxmSmartHandleCore. Only the code needed to implement type-safety remains here, hopefully minimzing code expansion.
This implementation currently uses a doublelinked list to link together all handles for a given "handlee" object. Since we do not intend to instantiate vast numbers of handles per handled object, this makes sense.
|
[*** Synchronized, but SAFETY BREACH] Assigns handlee object.
Before assigning the new handlee object, the handle is reset to default state: |
|
[*** Synchronized, but SAFETY BREACH] Assignment operator.
Before joining the new handle group, the handle is reset to default state: |