Difference between revisions of "Uno/Article/Multi-Thread Programming"

From Apache OpenOffice Wiki
< Uno
Jump to: navigation, search
m (Multi Threading Programming)
m (Synchronous Thread)
 
(84 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Preface ==
+
{{Uno/Note}} The technology described in this article depends on the presence of the [[Uno/Effort/Binary/Extend Threading-Model|extended Binary Uno threading-model]], which has been integrated into URE 1.3 (SRC680_m212).
The technologie described in this article depends on the presence of the [[Uno/Effort/Creating the Uno Threading Framework]].
+
  
== Multi Threading Programming ==
+
[[Uno]] is inherently multi-threaded, every [[Uno]] object may be accessed by multiple threads concurrently. The [[Uno/Spec/Threading-Model|Uno Threading-Model]] provides support for simplifying multi-thread programming.
UNO is inherently multi threaded. Every instance of a UNO component (which is an object) may be accessed by multiple threads concurrently. The UNO threading framework provides support for simplifying multi thread programming.
+
  
Every UNO reference points to an object with particular characteristics. Among implementing a concrete interface, the object may belong to one or multiple [[Uno/Spec/Purpose Environment|Purpose Environment]]s. The [[Uno/Spec/Threading Model|UNO Threading Model]] provides [[Uno/Spec/Thread Affinity Bridge|Thread Affine Purpose Environments]] and [[Uno/Spec/Thread Unsafety Bridge|Thread Unsafe Purpose Environments]]. Objects not belonging to one of these two [[Uno/Spec/Purpose Environment | Purpose Environments]] are assumed to be [[Uno/Term/Thread Safe|Thread Safe]].  
+
There are actually three things important to know about, when doing multi-threading with [[Uno]]. These are
 +
* the dedicated, thread related [[Uno/Spec/Environment|environments]],
 +
* how to use these [[Uno/Spec/Environment|environments]] when creating particular implementations,
 +
* and certainly, how to use threads wrt. Uno objects.
  
=== Implementing Objects ===
+
[[Uno/Spec/Environment|Environments]], [[Uno/Spec/Mapping|mappings]] and [[Uno/Spec/Object|objects]] are at the heart of [[Uno]], please read [[../Working with Environments, Mappings & Objects|Working with Environments, Mappings & Objects]] for an introduction.
Going to implement an UNO object, you need to decide on the threading architecture. You basically have the following choices, the object can either be
+
* [[Uno/Term/Thread Unsafe | Thread Unsafe]], or
+
* [[Uno/Term/Thread Safe | Thread Safe]], or
+
* [[Uno/Term/Thread Affine | Thread Affine]], or
+
* [[Uno/Term/Thread Free | Thread Free]].
+
  
==== Thread Unsafe ====
+
==Objects==
[[Uno/Term/Thread Unsafe | Thread Unsafe]] is the choice for most cases. Actually leaving proper synchronization of method calls to the [[Uno/Spec/Runtime | Runtime]].  
+
Going to implement an [[Uno/Spec/Object|Uno object]], you need to decide on the [[Uno/Term/Threading-Architecture|threading-architecture]]. You basically have the following choices, the object can either be
 +
* [[Uno/Term/Thread Unsafe|thread-unsafe]], or
 +
* [[Uno/Term/Thread Safe|thread-safe]], or
 +
* [[Uno/Term/Thread Affine|thread-affine]].
  
==== Thread Safe ====
+
===Purpose===
There are only rare cases where you want to implement your object [[Uno/Term/Thread Safe | Thread Safe]]. Either
+
====[[Uno/Term/Thread Unsafe|Thread-Unsafe]]====
* your object should or must allow the concurrent execution of some of its methods, or
+
[[Uno/Term/Thread Unsafe|Thread unsafe]] is the choice for most cases. Actually leaving proper synchronization of method calls to the [[Uno/Spec/Runtime|Uno runtime]].  
* your object wants to avoid the overhead associated with leaving synchronization to the [[Uno/Spec/Runtime | Runtime]].
+
  
One case, where your component must allow the concurrent execution of methods is, when you want to be able to abort a running invocation. UNO currently does not offer a mechanism to do this generically, so that particular objects must provide dedicated methods for abortion. An example for this is the [http://util.openoffice.org/source/browse/util/io/source/acceptor/ util/io/Acceptor] implementation.
+
====[[Uno/Term/Thread Safe|Thread-Safe]]====
 +
There are only rare cases where you actually want to implement your object [[Uno/Term/Thread Safe|thread-safe]]. Either
 +
* your object should or must allow the parallel execution of some of its methods, or
 +
* you want to avoid any overhead associated with leaving synchronization to the [[Uno/Spec/Runtime|Uno runtime]].
  
The overhead for automatic synchronization only affects inter-environment calls. The threading architecture of a particular application should be designed in a way, that closely connected objects happen to exist in the same [[Uno/Spec/Environment | Environment]]. Basically ensuring a low inter-environment call frequency, converting the potential advantage of self synchronized methods to the reverse.
+
One case, where your object must allow the parallel execution of methods is, when you want to be able to abort a running invocation of one of its methods. [[Uno]] currently does not offer a mechanism to do this generically, so that particular objects must provide dedicated methods for abortion. An example for this is the [http://util.openoffice.org/source/browse/util/io/source/acceptor/ util/io/Acceptor] implementation.
  
'''Note:''' Scalability may be achieved by the introduction of [[Uno/Spec/Named Environment | Named Environments]], actually allowing any number of Thread Unsafe Purpose Environments to exist simultanesously and to be entered by multiple threads concurrently.
+
The overhead for automatic synchronization only affects inter-[[Uno/Spec/Environment|environment]] calls. The [[Uno/Term/Threading-Architecture|threading-architecture]] of a particular application should be designed in a way, that closely connected objects happen to exist in the same [[Uno/Spec/Environment|Uno environment]], basically ensuring a low inter-environment call frequency, converting any potential advantage of self synchronized methods to the reverse.
  
==== Thread Affine ====
+
'''Note:''' Scalability may be achieved by the introduction of [[../Working with Environments, Mappings & Objects#Naming|named environments]], actually allowing any number of [[Uno/Term/Thread Unsafe|thread-unsafe]] [[Uno/Spec/Purpose Environment|purpose environments]] to exist simultaneously and to be activated by multiple threads independently.
[[Uno/Term/Thread Affine | Thread Affine]] objects are rare. In OOo they are needed to encapsulate the Win32 respectively the OLE/COM thread affinity.
+
  
==== Thread Free ====
+
====[[Uno/Term/Thread Affine|Thread-Affine]]====
[[Uno/Term/Thread Free | Thread Free]] objects may be used in any threading environment. They certainly need to respect particular environment related constraints, so.
+
[[Uno/Term/Thread Affine|Thread-affine]] objects are rare. In OOo they are needed to encapsulate the Win32 respectively the OLE/COM [[Uno/Term/Thread Affine|thread-affinity]]. See [[Effort/Make_VCL_Thread-Transparent|Make Vcl Thread-Transparent]] for details.
An implementation being able to deal with one of [[Uno/Term/Thread Unsafe|Thread Unsafe]], [[Uno/Term/Thread Safe|Thread Safe]] or [[Uno/Term/Thread Affine|Thread Affine]] objects only, is ''specialized'' on this particular threading model, while a [[Uno/Term/Thread Free|Thread Free]] implementation is able to deal with any threading model.
+
  
'''Note: '''Unfortunately, no type safe specialized purpose references are yet available for any UNO language binding. So, this is planned. Currently, the only possible way to detect the purpose of a reference is at runtime, via the <code>getCurrentEnvironment</code> [[Uno/Spec/Runtime | Runtime]] function.
+
===Implementation===
 +
Every object needs to be implemented somewhere. Dependent on the location, different actions need to be taken, to ensure correct usage of the object with respect to its [[Uno/Term/Threading-Architecture|threading-architecture]].
  
'''Note:''' The UNO [[Uno/Spec/Runtime | Runtime]] provides APIs for implementing any whished scenario. Including objects shared between different [[Uno/Spec/Purpose Environment | Purpose Environments]] or the instantiation of a [[Uno/Term/Thread Safe | Thread Safe]] component in a [[Uno/Term/Thread Unsafe | Thread Unsafe]] [[Uno/Spec/Environment | Environment]]. These mechanisms are in place to implement the Threading Framework itself, and to allow the programmer to gain full control, if needed.
+
====Components====
 +
The easiest way to implement an object is a [[Uno/Spec/Component|component]], as a component actively provides the managing environments of its objects. This means, that components do not need to ensure proper mapping etc., this is all taken care of by the component loader already.
  
====Capabilities====
+
=====C++ Example - A [[Uno/Term/Thread Unsafe|thread-unsafe]] Component=====
Code may actually vary in its capabilities regarding 'Transparent Concurrency'. Code may be differentiated into Code using
+
The <code>component_getImplementationEnvironment</code> function of a component does return the single managing environment for all objects provided by this component. The implementation of this function for [[Uno/Term/Thread Unsafe|thread-unsafe]] objects may look like this:
* global variables, or
+
<source lang="cpp">
* thread local variables, or
+
extern "C" void SAL_CALL component_getImplementationEnvironment(
* parameters only, or
+
  sal_Char        const ** ppEnvTypeName,  
* value parameters only, or
+
  uno_Environment      ** ppEnv
* doing internal protection of critical sections, or
+
)
* not doing internal protection.
+
{
Such categorizations help to decide, what one can do with a particular implemention. Also the codes threading type is a compile time information and may therefor be used during compilation, so that the compiler may give errors when mixing thread safe and thread unsafe code in a multi thread scenario.
+
  *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME ":unsafe";
 +
}
 +
</source>
  
=== Using Threads ===
+
=====C++ Example - A thread variable Component=====
You may create threads in your implementation. Depending on the selected threading architecture, these threads may are allowed to be visible from the outside or not. If your implementation is to be instantiated in a
+
A component implementing [[Uno/Term/Thread Safe|thread-safe]] and [[Uno/Term/Thread Transparent|thread-transparent]] objects may want to utilize these capabilities by avoiding any mapping, this can be done by implementing the <code>component_getImplementationEnvironmentExt</code> function, instead of the <code>component_getImplementationEnvironment</code> function. The implementation of this function for a thread variable component may look like this:
* [[Uno/Spec/Thread Unsafety Bridge|thread unsafe environment]], than it must be [[Uno/Term/Thread Transparent|thread transparent]], if it is to be used in a
+
<source lang="cpp">
* [[Uno/Spec/Thread Affinity Bridge|thread affine environment]], than is must be [[Uno/Term/Thread Transparent|thread transparent]] as well, if it is to be used in a
+
#include "cppu/EnvDcp.hxx"
* [[Uno/Term/Thread Safe|thread safe]] environment, it may create visible threads and be [[Uno/Term/Thread Aware|thread aware]] as needed, if it is to be used in
+
* any environment (being [[Uno/Term/Thread Free|thread free]]), it may behave differently, depending on the managing environment.
+
Obviously, invisible threads can be used and created anytime anyway. Invisible threads do not harm [[Uno/Term/Thread Transparent | Thread Transparency]].
+
  
=== Implementation Types ===
+
extern "C" void SAL_CALL component_getImplementationEnvironmentExt(
Depending on the type of the planned implementation, which either is a
+
  sal_Char        const ** ppEnvTypeName,  
* component, or a
+
  uno_Environment      ** ppEnv,
* library, or an
+
  sal_Char        const  * pImplName,
* application,
+
  uno_Environment        * pSrcEnv
care of the "purpose" characteristic of the involved objects needs to be taken.
+
)
 +
{
 +
  rtl::OUString envName(RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING));
 +
  envName += cppu::EnvDcp::getPurpose(Environment(pSrcEnv).getTypeName());
  
'''Note:''' It is planned, to support the selection of an [[Uno/Spec/Implementation Environment | Implementation Environment]] (determining the implementations [[Uno/Spec/Purpose Environment | Purpose Environment]]) by a macro or an include. Some experiments have been done, so no final decisions have been made yet.
+
  uno_getEnvironment(ppEnv, envName.pData, NULL);
 +
}
 +
</source>
  
==== Components ====
+
====Libraries&Applications====
 +
[[Uno/Spec/Uno Object|Uno objects]] may as well be implemented in libraries or applications. Caller and callee must agree one the managing [[Uno/Spec/Environment|environment]] for passed or returned objects, to not break [[Uno/Term/Environment Integrity]].
  
===== C++ =====
+
All public Uno libraries always do return appropriate objects, the implementations of the API are only [[Uno/Term/Object Binary Interface|OBI]] specialized and dynamically map the return or parameter objects according to the callers (purpose) [[Uno/Spec/Environment|environment]].
Thread Specialized components directly return their ABI plus their thread related specialisation. The implementation of the <code>component_getImplementationEnvironment</code> function for a Thread Unsafe component needs to reflect this unsafeness by providing it together with the ABI, e.g.:
+
 
<pre>
+
{{Uno/Note}} No convention, except documentation, has yet been introduced to identify any environment specialization of a function.
extern "C" void SAL_CALL component_getImplementationEnvironment(sal_Char        const ** ppEnvTypeName,
+
 
uno_Environment      ** ppEnv)
+
=====C++ Example - Function always returning a [[Uno/Term/Thread Safe|thread-safe]] Object=====
 +
The following example shows a function always returning a [[Uno/Term/Thread Safe|thread-safe]] object, while the objects implementation itself is [[Uno/Term/Thread Unsafe|thread-unsafe]]. For this function to work properly, the client must have left any [[Uno/Term/Thread Unsafe|thread-unsafe]] environment.
 +
 
 +
Callee:
 +
<source lang="cpp">
 +
// This function is environment specialized on "c++".
 +
uno::Reference<uno::XInterface> create_threadSafeObject(void) {
 +
  uno::Reference<uno::XInterface>  result_Obj;
 +
 
 +
  // We may want to ensure that we are in the "c++" only environment.
 +
  assert(uno::Environment::getCurrent().getTypeName() == rtl::OUString(RTL_CONSTASCII_PARAM("c++")));
 +
 
 +
  // We may want to open a new scope, to ensure that "result_Obj" does
 +
  // not get destructed while "c++:unsafe" is active.
 +
  {
 +
    // We activate (enter) the "c++:unsafe" environment.
 +
    // Note: Any other environment suiteable for "MyUnsafeObject" would work as well.
 +
    cppu::EnvGuard unsafeGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_PARAM("c++:unsafe"))));
 +
 
 +
    // This reference points to a "thread-unsafe" object.
 +
    uno::Reference<uno::XInterface> unsafeEnv_Obj(new MyUnsafeObject());
 +
 
 +
    // We may do some invocations on "unsafeEnv_Obj" while being in the "unsafe" environment.
 +
    unsafeEnv_Obj->doThis();
 +
    unsafeEnv_Obj->doThat();
 +
 
 +
    // We "shield" the object and assign it to "result_Obj"
 +
    result_Obj.set(cppu::shield(unsafeEnv_Obj), SAL_NO_ACQUIRE);
 +
 
 +
    // We may _not_ activate result_obj, as we are still in the "c++:unsafe" environment.
 +
  }
 +
 
 +
  // Using "result_obj" is fine here.
 +
  return result_Obj;
 +
}
 +
</source>
 +
 
 +
Caller:
 +
<source lang="cpp">
 +
...
 
{
 
{
*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME ":unsafe";
+
  // We just leave all "purpose" environments here, as "create_threadSafeObject" returns
 +
  // "c++" (thread-safe) objects only.
 +
  cppu::AntiEnvGuard antiGuard;
 +
 
 +
  uno::Reference safe_obj(create_threadSafeObject());
 
}
 
}
</pre>
+
...
 +
</source>
  
A Thread Free component providing objects for all thread related environments needs to implement the <code>component_getImplementationEnvironmentExt</code> function, e.g.:
+
=====C++ Example - Function only accepting [[Uno/Term/Thread Safe|thread-safe]] Parameters=====
<pre>
+
In the following example, the called function gets a [[Uno/Term/Thread Safe|thread-safe]] parameter, which needs to be mapped appropriately to the <code>"c++:unsafe"</code> environment, to be able to pass a [[Uno/Term/Thread Unsafe|thread-unsafe]] object to the <code>set</code> method of the parameter. For the function to work properly, the client must be in the [[Uno/Term/Thread Safe|thread-safe]] environment.
extern "C" void SAL_CALL component_getImplementationEnvironmentExt(sal_Char        const ** ppEnvTypeName,
+
 
                                                          uno_Environment      ** ppEnv,
+
Callee:
                                                          sal_Char        const  * pImplName,
+
<source lang="cpp">
                                                                  uno_Environment        * pSrcEnv
+
// This function is environment specialized on "c++".
)
+
void setUnsafeObject(uno::Reference<...> const & rObj) {
 +
  // We may want to ensure that we are in the "c++" only environment.
 +
  assert(uno::Environment::getCurrent().getTypeName() == rtl::OUString(RTL_CONSTASCII_PARAM("c++")));
 +
 
 +
  // We now activate (enter) the "c++:unsafe" environment.
 +
  // Note: Any other environment suiteable for "MyUnsafeObject" would work as well.
 +
  cppu::EnvGuard unsafeGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_PARAM("c++:unsafe"))));
 +
 
 +
  // We "unshield" the parameter.
 +
  uno::Reference<...> unsafeEnv_Obj.set(cppu::unshield(rObj), SAL_NO_ACQUIRE);
 +
 
 +
  // MyUnsafeObj has a C++ OBI and is thread-unsafe
 +
  unsafeEnv_Obj->set(new MyUnsafeObject());
 +
}
 +
</source>
 +
 
 +
Caller:
 +
<source lang="cpp">
 +
...
 
{
 
{
   rtl::OUString envName(RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING));
+
   // We leave all "purpose" environments here, as "setUnsafeObject" accepts
   envName += cppu::EnvDcp_getPurpose(Environment(pSrcEnv).getTypeName();
+
  // "c++" (thread-safe) objects only.
 +
   cppu::AntiEnvGuard antiGuard;
  
   uno_getEnvironment(ppEnv, envName.pData, NULL);
+
   uno::Reference<...> obj(...);
 +
  setUnsafeObj(obj);
 
}
 
}
</pre>
+
...
Implementing this function, the Component may very well select the threading environments depending on the provided implementation names.
+
</source>
  
==== Libraries ====
+
==[[Uno/Spec/Environment|Environments]]==
Libraries can not rely on the [[Uno/Spec/Runtime|Runtime]] to handle thread (respectively purpose) related mapping. Libraries need to care of thread related environments by their own. As well as components or applications, libraries may be implemented [[Uno/Term/Thread Specialized|Thread Specialized]] or [[Uno/Term/Thread Free|Thread Free]]. Depending on the capabilities of the particular programming language and language binding, correct specialization may be ensured during compilation time.
+
Every [[Uno]] reference points to an object with particular characteristics. Among implementing a concrete interface and [[Uno/Term/Object Binary Interface|Object Binary Interface (OBI)]], the object may have one or multiple "purposes" associated with it. The [[Uno/Term/Object Binary Interface|OBI]] and the "purposes" are expressed in the descriptor of the objects managing [[Uno/Spec/Environment|environment]], e.g. the [[Uno/Spec/Environment|environment]] described by <code>"gcc3:unsafe"</code> manages objects with a GCC3 C++ [[Uno/Term/Object Binary Interface|OBI]] (if named properly, it would have been called "g++3" or "gpp3"), which are [[Uno/Term/Thread Unsafe|thread-unsafe]].
  
===== C++ =====
+
The [[Uno/Spec/Threading-Model|Uno threading-model]] introduces [[Uno/Spec/Thread Affinity Bridge|thread-affine purpose environments]] and [[Uno/Spec/Thread Unsafety Bridge|thread-unsafe purpose environments]]. Objects not belonging to one of these two [[Uno/Spec/Purpose Environment|purpose environments]] are assumed to be [[Uno/Term/Thread Safe|thread-safe]].
The current C++ UNO language binding only supports Free (<code>cppu::EnvAwareReference</code>) and standard (<code>com::sun::star::uno::Reference</code>) references. The API of [[Uno/Term/Thread Free]] libraries should reflect the "freeness" by using the <code>cppu::EnvAwareReference</code> in its definition, e.g.:
+
<pre>
+
// Declaration
+
cppu::EnvAwareReference<XSingleServiceFactory> get_a_factory(void);
+
</pre>
+
  
For compatibility reasons, standard UNO references (<code>com::sun::star::uno::Reference</code>) may be used, doing a runtime check for the thread type of the passed or requested objects, e.g.:
+
Examples:
<pre>
+
* <code>"gcc3:unsafe"</code> - [[Uno/Spec/Environment|Environment]] for managing objects with a GCC3 C++ [[Uno/Term/Object Binary Interface|OBI]], which are [[Uno/Term/Thread Unsafe|thread-unsafe]].
static void creator(va_list param) {
+
* <code>"java"</code> - [[Uno/Spec/Environment|Environment]] for managing Java JNI objects, no further characteristics (AKA purposes) have been specified, therefore the managed objects have to be [[Uno/Term/Thread Safe|thread-safe]].
cppu::EnvAwareReference * prFactory = va_arg(param, cppu::EnvAwareReference<XSingleServiceFactory> *);
+
* <code>"java:affine"</code> - [[Uno/Spec/Environment|Environment]] for managing Java JNI objects which are [[Uno/Term/Thread Affine|thread-affine]].
  
    rFactory->set(new MyThreadUnsafeFactory());
+
===[[Uno/Term/Thread Safe|Thread-Safe]]===
 +
Any [[Uno/Spec/Environment|environment]] with neither <code>":unsafe"</code> nor <code>":affine"</code> in its description is a [[Uno/Term/Thread Safe|thread-safe]] environment. Objects managed by such an environment may very well be called directly by concurrent threads. Examples for [[Uno/Term/Thread Safe|thread-safe]] environments are <code>"gcc3"</code> or <code>"java"</code>, and also <code>"gcc3:debug"</code> or <code>"uno:debug"</code>.
 +
 
 +
===[[Uno/Term/Thread Unsafe|Thread-Unsafe]]===
 +
Any environment with an <code>":unsafe"</code> in its description is a [[Uno/Term/Thread Unsafe|thread-unsafe]] environment. Objects managed by such an environment may not be called directly by multiple threads. See the specification of the [[Uno/Spec/Thread Unsafety Bridge|thread-unsafety bridge]] for details.
 +
 
 +
====C++ Example - Activating a [[Uno/Term/Thread Unsafe|thread-unsafe]] Environment====
 +
The semantics of "entering" or "invoking" a [[Uno/Term/Thread Unsafe|thread-unsafe]] environment are the same.
 +
<source lang="cpp">
 +
...
 +
{
 +
  // Enter the "gcc3:unsafe" environment
 +
  cppu::EnvGuard unsafeGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gcc3:unsafe"))));
 +
  // Now we can safely directly call on any object belonging to this environment,
 +
  // no second thread can enter this environment in parallel
 +
  pObj->doSomething();
 +
 
 +
  // We implicity leave the "gcc3:unsafe" environment by destruction of the unsafeGuard
 
}
 
}
 +
...
 +
</source>
 +
Basically, only one thread at a time can have activated any <code>"<OBI>:unsafe"</code> environment in one particular process.
  
// API Implementation
+
===[[Uno/Term/Thread Affine|Thread-Affine]]===
uno::Reference<XSingleServiceFactory> get_a_factory(void)
+
Any [[Uno/Spec/Environment|environment]] with an <code>":affine"</code> in its description is a [[Uno/Term/Thread Affine|thread-affine]] environment. Objects managed by such an [[Uno/Spec/Environment|environment]] may not be called directly by multiple threads. See the specification of the [[Uno/Spec/Thread Affinity Bridge|thread-affinity bridge]] for details.
 +
 
 +
Actually, the semantics of "entering" or "invoking" a thread-affine environment differ. Entering a [[Uno/Term/Thread Affine|thread-affine]] environment is only possible, if no thread has been associated with this environment yet, if a thread has already been associated, the entering thread waits until the already associated thread leaves the [[Uno/Spec/Environment|environment]]. An associated thread may only leave a [[Uno/Term/Thread Affine|thread-affine]] environment, in case no object is more managed (e.g. the last managed object has been removed). Finally, the entering thread becomes the associated thread of the [[Uno/Term/Thread Affine|thread-affine]] environment. All invocations of objects of this [[Uno/Term/Thread Affine|thread-affine]] environment get dispatched into the associated thread.
 +
 
 +
In contrast, "invoking" a [[Uno/Term/Thread Affine|thread-affine]] environment creates a new, dedicated and hidden thread to be associated with it, in case no thread has been associated with it yet, all invocations of objects or functions are then dispatched to this (new) thread. This new thread gets terminated when the last managed objects becomes removed.
 +
 
 +
====C++ Example - Entering a [[Uno/Term/Thread Affine|thread-affine]] Environment====
 +
In the following example, the newly created instance of "MyUnoObject" is guaranteed to only be called by the creating thread. When trying to leave the [[Uno/Term/Thread Affine|thread-affine]] environment, the d'tor of the <code>affineGuard</code> will block as long as objects are managed by this [[Uno/Spec/Environment|environment]], basically ensuring that the objects are still reachable.
 +
 
 +
<source lang="cpp">
 +
class MyUnoObject ...;
 +
 
 +
...
 
{
 
{
   cppu::EnvAwareReference<XSingleServiceFactory> rFactory(new MyThreadUnsafeFactory());
+
   cppu::EnvGuard affineGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gcc3:affine"))));
 +
 
 +
  uno::Reference<XMultiServiceFactory> smgr(...);
  
   uno::Environment(rtl::OUString(RTL_CONSTASCII_PARAM("uno:unsafe"))).invoke(creator, &rFactory);
+
   smgr->createInstanceWithArguments(new MyUnoObject());
  
   return rFactory;
+
   // The implicit leave of the "gcc3:affine" blocks, until all managed objects (MyUnoObjects) are removed.
 
}
 
}
</pre>
+
...
 +
</source>
  
The [http://udk.openoffice.org/source/browse/udk/cppuhelper cppuhelper] library dynamically checks the current environment, and returns matching references, e.g. guaranteeing that [[Uno/Term/Thread Safe | Thread Safe]] and [[Uno/Term/Thread Unsafe | Thread Unsafe]] objects do not get mixed.
+
====C++ Example - Invoking a [[Uno/Term/Thread Affine|thread-affine]] Environment====
 +
This example shows, how to correctly invoke a [[Uno/Term/Thread Affine|thread-affine]] environment, as always, all objects need to be managed properly by their managing [[Uno/Spec/Environment|environments]].
 +
<source lang="cpp">
 +
class MyUnoObject ...;
  
==== Applications ====
+
void doSomething(va_list param)
Applications are basically like libraries, except that no UNO objects are passed in, or are returned. Application code implemented other than in the Thread Safe environment needs to explicitly enter the wished [[Uno/Spec/Purpose Environment|Purpose Environment]] respectively thread related [[Uno/Spec/Environment]] before calling any particular UNO aware [[Uno/Term/Thread Specialized|Thread Specialized]] library. UNO aware libraries may than provide matching objects, depending on their threading architecture. By default, applications start in the [[Uno/Term/Thread Safe|Thread Safe]] environment.
+
{
 +
  XMultiServiceFactory * pSmgr = va_arg(param, XMultiServiceFactory *);
 +
  pSmgr->createInstanceWithArguments(new MyUnoObject());
 +
}
 +
 
 +
...
 +
{
 +
  uno::Environment affineEnv(uno::Environment(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gcc3:affine"))));
 +
 
 +
  uno::Mapping curr2affine(uno::Environment::getCurrent(), affineEnv);
 +
 
 +
  void * affineSmgr = curr2affine.mapInterface(smgr, typeof(smgr));
 +
  affineEnv.invoke(s_doSomething, affineSmgr);
 +
  affineEnv.get()->pExtEnv->releaseInterface(affineSmgr);
 +
}
 +
...
 +
</source>
 +
 
 +
==Helpers==
 +
The helpers ease the mapping of objects from particular purpose [[Uno/Spec/Environment|environment]] to another. Where [[Uno/Spec/Mapping|mappings]] may be independently used to map any object to anywhere, the helpers take into account the current context.
 +
 
 +
===Map Helpers===
 +
 
 +
===Shield Helpers===
 +
====[[Uno/Cpp/Spec/Shield Helpers|C++ Shield Helpers]]====
 +
The "shield" helpers basically allow to shorten the mapping of an object
 +
* from the current (typically [[Uno/Term/Thread Unsafe|thread-unsafe]] or [[Uno/Term/Thread Affine|thread-affine]]) C++ environment, to the [[Uno/Term/Thread Safe|thread-safe]] C++ environment,
 +
* from the [[Uno/Term/Thread Safe|thread-safe]] C++ environment to the current (typically [[Uno/Term/Thread Unsafe|thread-unsafe]] or [[Uno/Term/Thread Affine|thread-affine]]) C++ environment.
 +
 
 +
Please have a look a the [[Uno/Cpp/Spec/Shield Helpers|shield helpers specification]] for more details.
 +
 
 +
=====C++ Example - Map Object to [[Uno/Term/Thread Safe|Thread-Safe]]=====
 +
Do the mapping by hand:
 +
<source lang="cpp">
 +
...
 +
uno::XInterface * pUnsafe_Object ...;
 +
uno::Mapping curr2safe(uno::getCurrentEnvironment(),
 +
                      rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))));
 +
 
 +
uno::XInterface * pSafe_Object = reinterpret_cast<uno::XInterface *>(
 +
                                    curr2safe.mapInterface(
 +
                                      pObject,
 +
                                      getCppuType((uno::Reference<uno::XInterface> *)NULL)
 +
                                    )
 +
                                );
 +
...
 +
</source>
 +
Can be simply replaced with:
 +
<source lang="cpp">
 +
...
 +
uno::XInterface * pUnsafe_Object ...;
 +
uno::XInterface * pSafe_Object = cppu::shield(pUnsafe_Object);
 +
...
 +
</source>
 +
 
 +
=====C++ Example - Map Object from [[Uno/Term/Thread Safe|Thread-Safe]]=====
 +
Do the mapping by hand:
 +
<source lang="cpp">
 +
...
 +
uno::XInterface * pSafe_Object ...;
 +
uno::Mapping safe2curr(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))),
 +
                      uno::getCurrentEnvironment());
 +
 
 +
uno::XInterface * pUnsafe_Object = reinterpret_cast<uno::XInterface *>(
 +
                                    safe2curr.mapInterface(
 +
                                      pObject,
 +
                                      getCppuType((uno::Reference<uno::XInterface> *)NULL)
 +
                                    )
 +
                                  );
 +
...
 +
</source>
 +
Can be simply replaced with:
 +
<source lang="cpp">
 +
...
 +
uno::XInterface * pSafe_Object ...;
 +
uno::XInterface * pUnsafe_Object = cppu::unshield(pSafe_Object);
 +
...
 +
</source>
 +
 
 +
=====C++ Example - Map <code>uno::Any</code> to [[Uno/Term/Thread Safe|Thread-Safe]]=====
 +
Do the mapping by hand:
 +
<source lang="cpp">
 +
...
 +
uno::Any unsafeAny = ...
 +
 
 +
uno::Mapping curr2safe(uno::getCurrentEnvironment(),
 +
                      rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))));
 +
 +
uno::Any safeAny;
 +
uno_any_destruct(&safeAny, (uno_ReleaseFunc)uno::cpp_release);
 +
uno_type_any_constructAndConvert(&safeAny,
 +
                                const_cast<void *>(unsafeAny.getValue()),
 +
                                unsafeAny.getValueTypeRef(),
 +
                                curr2safe.get());
 +
...
 +
</source>
 +
Can be simply replaced with:
 +
<source lang="cpp">]
 +
...
 +
uno::Any unsafeAny = ...
 +
uno::Any safeAny(cppu::shieldAny(unsafeAny));
 +
...
 +
</source>
 +
 
 +
=====C++ Example - Map <code>uno::Any</code> from [[Uno/Term/Thread Safe|Thread-Safe]]=====
 +
Do the mapping by hand:
 +
<source lang="cpp">
 +
...
 +
uno::Any safeAny = ...
 +
 
 +
uno::Mapping safe2curr(uno::getCurrentEnvironment(),
 +
                      rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))));
 +
 +
uno::Any unsafeAny;
 +
uno_any_destruct(&unsafeAny, (uno_ReleaseFunc)uno::cpp_release);
 +
uno_type_any_constructAndConvert(&unsafeAny,
 +
                                const_cast<void *>(safeAny.getValue()),
 +
                                safeAny.getValueTypeRef(),
 +
                                safe2curr.get());
 +
...
 +
</source>
 +
Can be simply replaced with:
 +
<source lang="cpp">
 +
...
 +
uno::Any safeAny = ...
 +
uno::Any unsafeAny(cppu::unshieldAny(safeAny));
 +
...
 +
</source>
 +
 
 +
==Threads==
 +
Thinking about threads, thread related environments and [[Uno/Spec/Object|Uno objects]], we roughly can identify the following types,
 +
* asynchronous threads, which run in the [[Uno/Term/Thread Safe|thread-safe]] environment,
 +
* synchronous threads, which run in a [[Uno/Term/Thread Unsafe|thread-unsafe]] or [[Uno/Term/Thread Affine|thread-affine]] environment,
 +
* hidden threads, which run behind an objects implementation only.
 +
Mixed types are certainly possible.
 +
 
 +
===Asynchronous===
 +
The asynchronous thread holds one or multiple references to thread-safe Uno objects. During its execution it may call on one or another of these objects. Every call may compete with any another threads call to the same particular object. In case a called object is not [[Uno/Term/Thread Safe|thread-safe]] (e.g. [[Uno/Term/Thread Unsafe|thread-unsafe]] or [[Uno/Term/Thread Affine|thread-affine]]), the appropriate [[Uno/Spec/Environment|environment]] becomes activated respectively deactivated implicitly before and after the call.
 +
 
 +
===Synchronous===
 +
The synchronous thread holds one or multiple references to [[Uno/Term/Thread Unsafe|thread-unsafe]] or [[Uno/Term/Thread Affine|thread-affine]] objects. Before actually invoking any object, the thread does activate the (respectively any) managing [[Uno/Spec/Environment|environment]]. The calls are therefor not competing with any other thread and the call sequence is atomic. After a sequence of calls, the thread deactivates the managing [[Uno/Spec/Environment|environment]] again.
 +
 
 +
===Hidden===
 +
The hidden thread is an implementation detail of a particular object only. Proper synchronization (e.g. acquiring / releasing mutexes) is taken care of by the implementer.
 +
 
 +
===C++ Examples===
 +
====Asynchronous Thread====
 +
Do not activate any [[Uno/Spec/Environment|environment]] explicitly, just run in the [[Uno/Term/Thread Safe|thread-safe]] environment. Only implicitly activate other [[Uno/Spec/Environment|environments]] when invoking mapped objects (e.g. [[Uno/Term/Thread Unsafe|thread-unsafe]] object).
 +
 
 +
<source lang="cpp">
 +
class MyThread : public Thread
 +
{
 +
  uno::Reference<...> m_safe_obj; // this points to a "thread-safe" object
 +
 
 +
protected:
 +
  virtual void SAL_CALL run()
 +
  {
 +
    m_safe_obj.doThis();
 +
    m_safe_obj.doThat();
 +
  }
  
===== C++ =====
+
public:
An application implementing thread unsafe objects to be passed to some UNO components may look like this:
+
  MyThread(uno::Reference<...> const & object)
<pre>
+
    : m_safe_obj(cppu::shield(object), SAL_NO_ACQUIRE)
class MyThreadUnsafeObject : ... {
+
   {}
   ...
+
 
};
 
};
 +
</source>
  
SAL_IMPLEMENT_MAIN()
+
====Synchronous Thread====
 +
Just enter an [[Uno/Spec/Environment|environment]] and do all calls while being in it. Obviously, releasing the objects also needs to be done in the environment.
 +
<source lang="cpp">
 +
class MyThread : public Thread
 
{
 
{
   cppu::EnvGuard unsafeGuard(Environment(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("uno:unsafe"))));
+
   uno::Environment   m_refEnv;
 +
  uno::Reference<...> m_unsafe_obj;
  
   // Get a thread unsafe component context.
+
   static void s_clear(va_list param)
  Reference< XComponentContext> xCtxt(cppu::defaultBootstrap_InitialComponentContext());
+
  {
   ...
+
    MyThread * pMyThread = va_arg(param, MyThread *);
  xObject.set(new MyThreadUnsafeObject());
+
    pMyThread->m_unsafe_obj.clear();
   ...
+
  }
   return 0;
+
 
}
+
  static void s_doSomething(va_list param)
</pre>
+
  {
 +
    MyThread * pMyThread = va_arg(param, MyThread *);
 +
    // do not do any slow/blocking operations here, as the target environment is
 +
    // currently activated, and no other thread may enter at the moment...
 +
    m_unsafe_obj->doThis();
 +
    m_unsafe_obj->doThat()
 +
    pMyThread->i_doSomething();
 +
   }
 +
 
 +
public:
 +
  MyThread(uno::Reference<...> const & object);
 +
    : m_unsafe_obj(object), m_refEnv(uno::Environment::getCurrent());
 +
   {}
 +
 
 +
  MyThread::~MyThread()
 +
  {
 +
    // the object needs to be released in the managing environment.
 +
    // unfortunately, there is not yet a SAL_NO_RELEASE
 +
    m_refEnv.invoke(s_clear, this);
 +
   }
 +
 
 +
  virtual void SAL_CALL run()
 +
  {
 +
    m_refEnv.invoke(s_doSomething, this);
 +
  }
 +
};
 +
</source>
 +
 
 +
==Specifications==
 +
The relevant specifications can be found here:
 +
* [[Uno/Spec/Threading-Model]]
 +
** [[Uno/Binary/Spec/Threading-Model]]
 +
** [[Uno/Cpp/Spec/Threading-Model]]
 +
 
 +
In particular:
 +
* [[Uno/Spec/Environment Stack]]
 +
** [[Uno/Binary/Spec/Environment Stack]]
 +
** [[Uno/Cpp/Spec/Environment Stack]]
 +
* [[Uno/Spec/Thread Affinity Bridge]]
 +
** [[Uno/Binary/Spec/Thread Affinity Bridge]]
 +
* [[Uno/Spec/Thread Unsafety Bridge]]
 +
** [[Uno/Binary/Spec/Thread Unsafety Bridge]]
 +
* [[Uno/Spec/Purpose Environment]]
 +
** [[Uno/Binary/Spec/Environment Descriptor]]
 +
** [[Uno/Cpp/Spec/Environment Descriptor]]
 +
* [[Uno/Spec/Cascaded Mapping]]
 +
** [[Uno/Binary/Spec/Cascaded Mapping]]
 +
* [[Uno/Spec/Environment Substitution]]
 +
* ([[Uno/Spec/Implementation Environment]])
 +
** ([[Uno/Binary/Spec/Implementation Environment]])
 +
** ([[Uno/Cpp/Spec/Implementation Environment]])
 +
* [[Uno/Cpp/Spec/Environment Guard]]
  
'''Note:''' The planned introduction of [[Uno/Spec/Implementation Environment | Implementation Environments]] is going to be usable by the application programmer as well. It basically removes the necessity to explicitly enter a particular [[Uno/Spec/Purpose Environment | Purpose Environment]].
+
==See also==
 +
* Using C++ with OOo SDK : [[Using_Cpp_with_the_OOo_SDK|Main Page]]
 +
* [[SDKCppLanguage#Threads|Threads]]
  
== See also ==
 
* Using C++ with OOo SDK : [[Using_Cpp_with_the_OOo_SDK | Main Page]]
 
* [[SDKCppLanguage#Threads | Threads]]
 
  
[[Category:Uno:Article]]
+
[[Category:Uno]]
 +
[[Category:Cpp]]
 +
[[Category:Article]]
 +
[[Category:Tutorial]]
 +
[[Category:Multi-Threading]]

Latest revision as of 17:31, 23 February 2008

Note: The technology described in this article depends on the presence of the extended Binary Uno threading-model, which has been integrated into URE 1.3 (SRC680_m212).

Uno is inherently multi-threaded, every Uno object may be accessed by multiple threads concurrently. The Uno Threading-Model provides support for simplifying multi-thread programming.

There are actually three things important to know about, when doing multi-threading with Uno. These are

  • the dedicated, thread related environments,
  • how to use these environments when creating particular implementations,
  • and certainly, how to use threads wrt. Uno objects.

Environments, mappings and objects are at the heart of Uno, please read Working with Environments, Mappings & Objects for an introduction.

Objects

Going to implement an Uno object, you need to decide on the threading-architecture. You basically have the following choices, the object can either be

Purpose

Thread-Unsafe

Thread unsafe is the choice for most cases. Actually leaving proper synchronization of method calls to the Uno runtime.

Thread-Safe

There are only rare cases where you actually want to implement your object thread-safe. Either

  • your object should or must allow the parallel execution of some of its methods, or
  • you want to avoid any overhead associated with leaving synchronization to the Uno runtime.

One case, where your object must allow the parallel execution of methods is, when you want to be able to abort a running invocation of one of its methods. Uno currently does not offer a mechanism to do this generically, so that particular objects must provide dedicated methods for abortion. An example for this is the util/io/Acceptor implementation.

The overhead for automatic synchronization only affects inter-environment calls. The threading-architecture of a particular application should be designed in a way, that closely connected objects happen to exist in the same Uno environment, basically ensuring a low inter-environment call frequency, converting any potential advantage of self synchronized methods to the reverse.

Note: Scalability may be achieved by the introduction of named environments, actually allowing any number of thread-unsafe purpose environments to exist simultaneously and to be activated by multiple threads independently.

Thread-Affine

Thread-affine objects are rare. In OOo they are needed to encapsulate the Win32 respectively the OLE/COM thread-affinity. See Make Vcl Thread-Transparent for details.

Implementation

Every object needs to be implemented somewhere. Dependent on the location, different actions need to be taken, to ensure correct usage of the object with respect to its threading-architecture.

Components

The easiest way to implement an object is a component, as a component actively provides the managing environments of its objects. This means, that components do not need to ensure proper mapping etc., this is all taken care of by the component loader already.

C++ Example - A thread-unsafe Component

The component_getImplementationEnvironment function of a component does return the single managing environment for all objects provided by this component. The implementation of this function for thread-unsafe objects may look like this:

extern "C" void SAL_CALL component_getImplementationEnvironment(
  sal_Char        const ** ppEnvTypeName, 
  uno_Environment       ** ppEnv
)
{
  *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME ":unsafe";
}
C++ Example - A thread variable Component

A component implementing thread-safe and thread-transparent objects may want to utilize these capabilities by avoiding any mapping, this can be done by implementing the component_getImplementationEnvironmentExt function, instead of the component_getImplementationEnvironment function. The implementation of this function for a thread variable component may look like this:

#include "cppu/EnvDcp.hxx"
 
extern "C" void SAL_CALL component_getImplementationEnvironmentExt(
  sal_Char        const ** ppEnvTypeName, 
  uno_Environment       ** ppEnv,
  sal_Char        const  * pImplName,
  uno_Environment        * pSrcEnv
)
{
  rtl::OUString envName(RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING));
  envName += cppu::EnvDcp::getPurpose(Environment(pSrcEnv).getTypeName());
 
  uno_getEnvironment(ppEnv, envName.pData, NULL);
}

Libraries&Applications

Uno objects may as well be implemented in libraries or applications. Caller and callee must agree one the managing environment for passed or returned objects, to not break Uno/Term/Environment Integrity.

All public Uno libraries always do return appropriate objects, the implementations of the API are only OBI specialized and dynamically map the return or parameter objects according to the callers (purpose) environment.

Note: No convention, except documentation, has yet been introduced to identify any environment specialization of a function.

C++ Example - Function always returning a thread-safe Object

The following example shows a function always returning a thread-safe object, while the objects implementation itself is thread-unsafe. For this function to work properly, the client must have left any thread-unsafe environment.

Callee:

// This function is environment specialized on "c++".
uno::Reference<uno::XInterface> create_threadSafeObject(void) {
  uno::Reference<uno::XInterface>  result_Obj;
 
  // We may want to ensure that we are in the "c++" only environment.
  assert(uno::Environment::getCurrent().getTypeName() == rtl::OUString(RTL_CONSTASCII_PARAM("c++")));
 
  // We may want to open a new scope, to ensure that "result_Obj" does
  // not get destructed while "c++:unsafe" is active.
  {
    // We activate (enter) the "c++:unsafe" environment.
    // Note: Any other environment suiteable for "MyUnsafeObject" would work as well.
    cppu::EnvGuard unsafeGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_PARAM("c++:unsafe"))));
 
    // This reference points to a "thread-unsafe" object.
    uno::Reference<uno::XInterface> unsafeEnv_Obj(new MyUnsafeObject());
 
    // We may do some invocations on "unsafeEnv_Obj" while being in the "unsafe" environment.
    unsafeEnv_Obj->doThis();
    unsafeEnv_Obj->doThat();
 
    // We "shield" the object and assign it to "result_Obj"
    result_Obj.set(cppu::shield(unsafeEnv_Obj), SAL_NO_ACQUIRE);
 
    // We may _not_ activate result_obj, as we are still in the "c++:unsafe" environment.
  }
 
  // Using "result_obj" is fine here.
  return result_Obj;
}

Caller:

...
{
  // We just leave all "purpose" environments here, as "create_threadSafeObject" returns
  // "c++" (thread-safe) objects only.
  cppu::AntiEnvGuard antiGuard;
 
  uno::Reference safe_obj(create_threadSafeObject());
}
...
C++ Example - Function only accepting thread-safe Parameters

In the following example, the called function gets a thread-safe parameter, which needs to be mapped appropriately to the "c++:unsafe" environment, to be able to pass a thread-unsafe object to the set method of the parameter. For the function to work properly, the client must be in the thread-safe environment.

Callee:

// This function is environment specialized on "c++".
void setUnsafeObject(uno::Reference<...> const & rObj) {
  // We may want to ensure that we are in the "c++" only environment.
  assert(uno::Environment::getCurrent().getTypeName() == rtl::OUString(RTL_CONSTASCII_PARAM("c++")));
 
  // We now activate (enter) the "c++:unsafe" environment.
  // Note: Any other environment suiteable for "MyUnsafeObject" would work as well.
  cppu::EnvGuard unsafeGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_PARAM("c++:unsafe"))));
 
  // We "unshield" the parameter.
  uno::Reference<...> unsafeEnv_Obj.set(cppu::unshield(rObj), SAL_NO_ACQUIRE);
 
  // MyUnsafeObj has a C++ OBI and is thread-unsafe
  unsafeEnv_Obj->set(new MyUnsafeObject());
}

Caller:

...
{
  // We leave all "purpose" environments here, as "setUnsafeObject" accepts
  // "c++" (thread-safe) objects only.
  cppu::AntiEnvGuard antiGuard;
 
  uno::Reference<...> obj(...);
  setUnsafeObj(obj);
}
...

Environments

Every Uno reference points to an object with particular characteristics. Among implementing a concrete interface and Object Binary Interface (OBI), the object may have one or multiple "purposes" associated with it. The OBI and the "purposes" are expressed in the descriptor of the objects managing environment, e.g. the environment described by "gcc3:unsafe" manages objects with a GCC3 C++ OBI (if named properly, it would have been called "g++3" or "gpp3"), which are thread-unsafe.

The Uno threading-model introduces thread-affine purpose environments and thread-unsafe purpose environments. Objects not belonging to one of these two purpose environments are assumed to be thread-safe.

Examples:

Thread-Safe

Any environment with neither ":unsafe" nor ":affine" in its description is a thread-safe environment. Objects managed by such an environment may very well be called directly by concurrent threads. Examples for thread-safe environments are "gcc3" or "java", and also "gcc3:debug" or "uno:debug".

Thread-Unsafe

Any environment with an ":unsafe" in its description is a thread-unsafe environment. Objects managed by such an environment may not be called directly by multiple threads. See the specification of the thread-unsafety bridge for details.

C++ Example - Activating a thread-unsafe Environment

The semantics of "entering" or "invoking" a thread-unsafe environment are the same.

...
{
  // Enter the "gcc3:unsafe" environment
  cppu::EnvGuard unsafeGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gcc3:unsafe"))));
  // Now we can safely directly call on any object belonging to this environment,
  // no second thread can enter this environment in parallel
  pObj->doSomething();
 
  // We implicity leave the "gcc3:unsafe" environment by destruction of the unsafeGuard
}
...

Basically, only one thread at a time can have activated any "<OBI>:unsafe" environment in one particular process.

Thread-Affine

Any environment with an ":affine" in its description is a thread-affine environment. Objects managed by such an environment may not be called directly by multiple threads. See the specification of the thread-affinity bridge for details.

Actually, the semantics of "entering" or "invoking" a thread-affine environment differ. Entering a thread-affine environment is only possible, if no thread has been associated with this environment yet, if a thread has already been associated, the entering thread waits until the already associated thread leaves the environment. An associated thread may only leave a thread-affine environment, in case no object is more managed (e.g. the last managed object has been removed). Finally, the entering thread becomes the associated thread of the thread-affine environment. All invocations of objects of this thread-affine environment get dispatched into the associated thread.

In contrast, "invoking" a thread-affine environment creates a new, dedicated and hidden thread to be associated with it, in case no thread has been associated with it yet, all invocations of objects or functions are then dispatched to this (new) thread. This new thread gets terminated when the last managed objects becomes removed.

C++ Example - Entering a thread-affine Environment

In the following example, the newly created instance of "MyUnoObject" is guaranteed to only be called by the creating thread. When trying to leave the thread-affine environment, the d'tor of the affineGuard will block as long as objects are managed by this environment, basically ensuring that the objects are still reachable.

class MyUnoObject ...;
 
...
{
  cppu::EnvGuard affineGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gcc3:affine"))));
 
  uno::Reference<XMultiServiceFactory> smgr(...);
 
  smgr->createInstanceWithArguments(new MyUnoObject());
 
  // The implicit leave of the "gcc3:affine" blocks, until all managed objects (MyUnoObjects) are removed.
}
...

C++ Example - Invoking a thread-affine Environment

This example shows, how to correctly invoke a thread-affine environment, as always, all objects need to be managed properly by their managing environments.

class MyUnoObject ...;
 
void doSomething(va_list param)
{
  XMultiServiceFactory * pSmgr = va_arg(param, XMultiServiceFactory *);
  pSmgr->createInstanceWithArguments(new MyUnoObject());
}
 
...
{
  uno::Environment affineEnv(uno::Environment(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gcc3:affine"))));
 
  uno::Mapping curr2affine(uno::Environment::getCurrent(), affineEnv);
 
  void * affineSmgr = curr2affine.mapInterface(smgr, typeof(smgr));
  affineEnv.invoke(s_doSomething, affineSmgr);
  affineEnv.get()->pExtEnv->releaseInterface(affineSmgr);
}
...

Helpers

The helpers ease the mapping of objects from particular purpose environment to another. Where mappings may be independently used to map any object to anywhere, the helpers take into account the current context.

Map Helpers

Shield Helpers

C++ Shield Helpers

The "shield" helpers basically allow to shorten the mapping of an object

Please have a look a the shield helpers specification for more details.

C++ Example - Map Object to Thread-Safe

Do the mapping by hand:

...
uno::XInterface * pUnsafe_Object ...;
uno::Mapping curr2safe(uno::getCurrentEnvironment(), 
                       rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))));
 
uno::XInterface * pSafe_Object = reinterpret_cast<uno::XInterface *>(
                                    curr2safe.mapInterface(
                                      pObject, 
                                      getCppuType((uno::Reference<uno::XInterface> *)NULL)
                                    )
                                 );
...

Can be simply replaced with:

...
uno::XInterface * pUnsafe_Object ...;
uno::XInterface * pSafe_Object = cppu::shield(pUnsafe_Object);
...
C++ Example - Map Object from Thread-Safe

Do the mapping by hand:

...
uno::XInterface * pSafe_Object ...;
uno::Mapping safe2curr(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))),
                       uno::getCurrentEnvironment());
 
uno::XInterface * pUnsafe_Object = reinterpret_cast<uno::XInterface *>(
                                     safe2curr.mapInterface(
                                       pObject, 
                                       getCppuType((uno::Reference<uno::XInterface> *)NULL)
                                     )
                                   );
...

Can be simply replaced with:

...
uno::XInterface * pSafe_Object ...;
uno::XInterface * pUnsafe_Object = cppu::unshield(pSafe_Object);
...
C++ Example - Map uno::Any to Thread-Safe

Do the mapping by hand:

...
uno::Any unsafeAny = ...
 
uno::Mapping curr2safe(uno::getCurrentEnvironment(),
                       rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))));
 
uno::Any safeAny;
uno_any_destruct(&safeAny, (uno_ReleaseFunc)uno::cpp_release);
uno_type_any_constructAndConvert(&safeAny,
                                 const_cast<void *>(unsafeAny.getValue()),
                                 unsafeAny.getValueTypeRef(),
                                 curr2safe.get());
...

Can be simply replaced with:

]
...
uno::Any unsafeAny = ...
uno::Any safeAny(cppu::shieldAny(unsafeAny));
...
C++ Example - Map uno::Any from Thread-Safe

Do the mapping by hand:

...
uno::Any safeAny = ...
 
uno::Mapping safe2curr(uno::getCurrentEnvironment(),
                       rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(CPPU_STRINGIFY(CPPU_ENV))));
 
uno::Any unsafeAny;
uno_any_destruct(&unsafeAny, (uno_ReleaseFunc)uno::cpp_release);
uno_type_any_constructAndConvert(&unsafeAny,
                                 const_cast<void *>(safeAny.getValue()),
                                 safeAny.getValueTypeRef(),
                                 safe2curr.get());
...

Can be simply replaced with:

...
uno::Any safeAny = ...
uno::Any unsafeAny(cppu::unshieldAny(safeAny));
...

Threads

Thinking about threads, thread related environments and Uno objects, we roughly can identify the following types,

  • asynchronous threads, which run in the thread-safe environment,
  • synchronous threads, which run in a thread-unsafe or thread-affine environment,
  • hidden threads, which run behind an objects implementation only.

Mixed types are certainly possible.

Asynchronous

The asynchronous thread holds one or multiple references to thread-safe Uno objects. During its execution it may call on one or another of these objects. Every call may compete with any another threads call to the same particular object. In case a called object is not thread-safe (e.g. thread-unsafe or thread-affine), the appropriate environment becomes activated respectively deactivated implicitly before and after the call.

Synchronous

The synchronous thread holds one or multiple references to thread-unsafe or thread-affine objects. Before actually invoking any object, the thread does activate the (respectively any) managing environment. The calls are therefor not competing with any other thread and the call sequence is atomic. After a sequence of calls, the thread deactivates the managing environment again.

Hidden

The hidden thread is an implementation detail of a particular object only. Proper synchronization (e.g. acquiring / releasing mutexes) is taken care of by the implementer.

C++ Examples

Asynchronous Thread

Do not activate any environment explicitly, just run in the thread-safe environment. Only implicitly activate other environments when invoking mapped objects (e.g. thread-unsafe object).

class MyThread : public Thread 
{
  uno::Reference<...> m_safe_obj; // this points to a "thread-safe" object
 
protected:
  virtual void SAL_CALL run()
  {
    m_safe_obj.doThis();
    m_safe_obj.doThat();
  }
 
public:
  MyThread(uno::Reference<...> const & object)
    : m_safe_obj(cppu::shield(object), SAL_NO_ACQUIRE)
  {}
};

Synchronous Thread

Just enter an environment and do all calls while being in it. Obviously, releasing the objects also needs to be done in the environment.

class MyThread : public Thread 
{
  uno::Environment    m_refEnv;
  uno::Reference<...> m_unsafe_obj;
 
  static void s_clear(va_list param)
  {
    MyThread * pMyThread = va_arg(param, MyThread *);
    pMyThread->m_unsafe_obj.clear();
  }
 
  static void s_doSomething(va_list param)
  {
    MyThread * pMyThread = va_arg(param, MyThread *);
    // do not do any slow/blocking operations here, as the target environment is
    // currently activated, and no other thread may enter at the moment...
    m_unsafe_obj->doThis();
    m_unsafe_obj->doThat();  
    pMyThread->i_doSomething();
  }
 
public:
  MyThread(uno::Reference<...> const & object);
    : m_unsafe_obj(object), m_refEnv(uno::Environment::getCurrent());
  {}
 
  MyThread::~MyThread() 
  {
    // the object needs to be released in the managing environment.
    // unfortunately, there is not yet a SAL_NO_RELEASE
    m_refEnv.invoke(s_clear, this);
  }
 
  virtual void SAL_CALL run()
  {
    m_refEnv.invoke(s_doSomething, this);
  }
};

Specifications

The relevant specifications can be found here:

In particular:

See also

Personal tools