xref: /aoo41x/main/sal/inc/rtl/instance.hxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #if !defined INCLUDED_RTL_INSTANCE_HXX
29 #define INCLUDED_RTL_INSTANCE_HXX
30 
31 #include "osl/doublecheckedlocking.h"
32 #include "osl/getglobalmutex.hxx"
33 
34 namespace {
35 
36 /** A non-broken version of the double-checked locking pattern.
37 
38     See
39     <http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html>
40     for a description of double-checked locking, why it is broken, and how it
41     can be fixed.  Always use this template instead of spelling out the
42     double-checked locking pattern explicitly, and only in those rare cases
43     where that is not possible and you have to spell it out explicitly, at
44     least call OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER() at the right
45     places.  That way, all platform-dependent code to make double-checked
46     locking work can be kept in one place.
47 
48     Usage scenarios:
49 
50     1  Static instance (most common case)
51 
52     Pattern:
53 
54       T * getInstance()
55       {
56           static T * pInstance = 0;
57           if (!pInstance)
58           {
59               ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
60               if (!pInstance)
61               {
62                   static T aInstance;
63                   pInstance = &aInstance;
64               }
65           }
66           return pInstance;
67       }
68 
69     Code:
70 
71       #include "rtl/instance.hxx"
72       #include "osl/getglobalmutex.hxx"
73 
74       namespace {
75           struct Init
76           {
77               T * operator()()
78               {
79                   static T aInstance;
80                   return &aInstance;
81               }
82           };
83       }
84 
85       T * getInstance()
86       {
87           return rtl_Instance< T, Init, ::osl::MutexGuard,
88                                ::osl::GetGlobalMutex >::create(
89               Init(), ::osl::GetGlobalMutex());
90       }
91 
92     2  Dynamic instance
93 
94     Pattern:
95 
96       T * getInstance()
97       {
98           static T * pInstance = 0;
99           if (!pInstance)
100           {
101               ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
102               if (!pInstance)
103                   pInstance = new T;
104           }
105           return pInstance;
106       }
107 
108     Code:
109 
110       #include "rtl/instance.hxx"
111       #include "osl/getglobalmutex.hxx"
112 
113       namespace {
114           struct Init
115           {
116               T * operator()()
117               {
118                   return new T;
119               }
120           };
121       }
122 
123       T * getInstance()
124       {
125           return rtl_Instance< T, Init, ::osl::MutexGuard,
126                                ::osl::GetGlobalMutex >::create(
127               Init(), ::osl::GetGlobalMutex());
128       }
129 
130     3  Other guard/mutex
131 
132     Pattern:
133 
134       T * getInstance()
135       {
136           static T * pInstance = 0;
137           if (!pInstance)
138           {
139               SomeGuard aGuard(pSomeMutex);
140               if (!pInstance)
141               {
142                   static T aInstance;
143                   pInstance = &aInstance;
144               }
145           }
146           return pInstance;
147       }
148 
149     Code:
150 
151       #include "rtl/instance.hxx"
152 
153       namespace {
154           struct InitInstance
155           {
156               T * operator()()
157               {
158                   static T aInstance;
159                   return &aInstance;
160               }
161           };
162 
163           struct InitGuard
164           {
165               SomeMutex * operator()()
166               {
167                   return pSomeMutex;
168               }
169           };
170       }
171 
172       T * getInstance()
173       {
174           return rtl_Instance< T, InitInstance,
175                                SomeGuard, InitGuard >::create(
176               InitInstance(), InitMutex());
177       }
178 
179     4  Calculate extra data
180 
181     Pattern:
182 
183       T * getInstance()
184       {
185           static T * pInstance = 0;
186           if (!pInstance)
187           {
188               Data aData(...);
189               ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
190               if (!pInstance)
191               {
192                   static T aInstance(aData);
193                   pInstance = &aInstance;
194               }
195           }
196           return pInstance;
197       }
198 
199     Code:
200 
201       #include "rtl/instance.hxx"
202       #include "osl/getglobalmutex.hxx"
203 
204       namespace {
205           struct InitInstance
206           {
207               T * operator()()
208               {
209                   static T aInstance;
210                   return &aInstance;
211               }
212           }
213 
214           struct InitData
215           {
216               Data const & operator()()
217               {
218                   return ...;
219               }
220           }
221       }
222 
223       T * getInstance()
224       {
225           return rtl_Instance< T, InitInstance,
226                                ::osl::Mutex, ::osl::GetGlobalMutex,
227                                Data, InitData >::create(
228               InitInstance(), ::osl::GetGlobalMutex(), InitData());
229       }
230 
231     Some comments:
232 
233     For any instantiation of rtl_Instance, at most one call to a create method
234     may occur in the program code:  Each occurance of a create method within
235     the program code is supposed to return a fresh object instance on the
236     first call, and that same object instance on subsequent calls; but
237     independent occurances of create methods are supposed to return
238     independent object instances.  Since there is a one-to-one correspondence
239     between object instances and instantiations of rtl_Instance, the
240     requirement should be clear.  One measure to enforce the requirement is
241     that rtl_Instance lives in an unnamed namespace, so that instantiations of
242     rtl_Instance in different translation units will definitely be different
243     instantiations.  A drawback of that measure is that the name of the class
244     needs a funny "hand coded" prefix "rtl_" instead of a proper namespace
245     prefix like "::rtl::".
246 
247     A known problem with this template is when two occurences of calls to
248     create methods with identical template arguments appear in one translation
249     unit.  Those two places will share a single object instance.  This can be
250     avoided by using different Init structs (see the above code samples) in
251     the two places.
252 
253     There is no need to make m_pInstance volatile, in order to avoid usage of
254     stale copies of m_pInstance:  At the first check, a thread will see that
255     m_pInstance contains either 0 or a valid pointer.  If it contains a valid
256     pointer, it cannot be stale, and that pointer is used.  If it contains 0,
257     acquiring the mutex will ensure that the second check sees a non-stale
258     value in all cases.
259 
260     On some compilers, the create methods would not be inlined if they
261     contained any static variables, so m_pInstance is made a class member
262     instead (and the create methods are inlined).  But on MSC, the definition
263     of the class member m_pInstance would cause compilation to fail with an
264     internal compiler error.  Since MSC is able to inline methods containing
265     static variables, m_pInstance is moved into the methods there.  Note that
266     this only works well because for any instantiation of rtl_Instance at most
267     one call to a create method should be present, anyway.
268  */
269 template< typename Inst, typename InstCtor,
270           typename Guard, typename GuardCtor,
271           typename Data = int, typename DataCtor = int >
272 class rtl_Instance
273 {
274 public:
275     static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor)
276     {
277 #if defined _MSC_VER
278         static Inst * m_pInstance = 0;
279 #endif // _MSC_VER
280         Inst * p = m_pInstance;
281         if (!p)
282         {
283             Guard aGuard(aGuardCtor());
284             p = m_pInstance;
285             if (!p)
286             {
287                 p = aInstCtor();
288                 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
289                 m_pInstance = p;
290             }
291         }
292         else
293         {
294             OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
295         }
296         return p;
297     }
298 
299     static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor,
300                                 DataCtor aDataCtor)
301     {
302 #if defined _MSC_VER
303         static Inst * m_pInstance = 0;
304 #endif // _MSC_VER
305         Inst * p = m_pInstance;
306         if (!p)
307         {
308             Data aData(aDataCtor());
309             Guard aGuard(aGuardCtor());
310             p = m_pInstance;
311             if (!p)
312             {
313                 p = aInstCtor(aData);
314                 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
315                 m_pInstance = p;
316             }
317         }
318         else
319         {
320             OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
321         }
322         return p;
323     }
324 
325 private:
326 #if !defined _MSC_VER
327     static Inst * m_pInstance;
328 #endif // _MSC_VER
329 };
330 
331 #if !defined _MSC_VER
332 template< typename Inst, typename InstCtor,
333           typename Guard, typename GuardCtor,
334           typename Data, typename DataCtor >
335 Inst *
336 rtl_Instance< Inst, InstCtor, Guard, GuardCtor, Data, DataCtor >::m_pInstance
337 = 0;
338 #endif // _MSC_VER
339 
340 }
341 
342 namespace rtl {
343 
344 /** Helper base class for a late-initialized (default-constructed)
345     static variable, implementing the double-checked locking pattern correctly.
346 
347     @derive
348     Derive from this class (common practice), e.g.
349     <pre>
350     struct MyStatic : public rtl::Static<MyType, MyStatic> {};
351     ...
352     MyType & rStatic = MyStatic::get();
353     ...
354     </pre>
355 
356     @tplparam T
357               variable's type
358     @tplparam Unique
359               Implementation trick to make the inner static holder unique,
360               using the outer class
361               (the one that derives from this base class)
362 */
363 template<typename T, typename Unique>
364 class Static {
365 public:
366     /** Gets the static.  Mutual exclusion is performed using the
367         osl global mutex.
368 
369         @return
370                 static variable
371     */
372     static T & get() {
373         return *rtl_Instance<
374             T, StaticInstance,
375             ::osl::MutexGuard, ::osl::GetGlobalMutex >::create(
376                 StaticInstance(), ::osl::GetGlobalMutex() );
377     }
378 private:
379     struct StaticInstance {
380         T * operator () () {
381             static T instance;
382             return &instance;
383         }
384     };
385 };
386 
387 /** Helper class for a late-initialized static aggregate, e.g. an array,
388     implementing the double-checked locking pattern correctly.
389 
390     @tplparam T
391               aggregate's element type
392     @tplparam InitAggregate
393               initializer functor class
394 */
395 template<typename T, typename InitAggregate>
396 class StaticAggregate {
397 public:
398     /** Gets the static aggregate, late-initializing.
399         Mutual exclusion is performed using the osl global mutex.
400 
401         @return
402                 aggregate
403     */
404     static T * get() {
405         return rtl_Instance<
406             T, InitAggregate,
407             ::osl::MutexGuard, ::osl::GetGlobalMutex >::create(
408                 InitAggregate(), ::osl::GetGlobalMutex() );
409     }
410 };
411 
412 /** Helper base class for a late-initialized static variable,
413     implementing the double-checked locking pattern correctly.
414 
415     @derive
416     Derive from this class (common practice),
417     providing an initializer functor class, e.g.
418     <pre>
419     struct MyStatic : public rtl::StaticWithInit<MyType, MyStatic> {
420         MyType operator () () {
421             ...
422             return MyType( ... );
423         }
424     };
425     ...
426     MyType & rStatic = MyStatic::get();
427     ...
428     </pre>
429 
430     @tplparam T
431               variable's type
432     @tplparam InitData
433               initializer functor class
434     @tplparam Unique
435               Implementation trick to make the inner static holder unique,
436               using the outer class
437               (the one that derives from this base class).
438               Default is InitData (common practice).
439     @tplparam Data
440               Initializer functor's return type.
441               Default is T (common practice).
442 */
443 template<typename T, typename InitData,
444          typename Unique = InitData, typename Data = T>
445 class StaticWithInit {
446 public:
447     /** Gets the static.  Mutual exclusion is performed using the
448         osl global mutex.
449 
450         @return
451                 static variable
452     */
453     static T & get() {
454         return *rtl_Instance<
455             T, StaticInstanceWithInit,
456             ::osl::MutexGuard, ::osl::GetGlobalMutex,
457             Data, InitData >::create( StaticInstanceWithInit(),
458                                       ::osl::GetGlobalMutex(),
459                                       InitData() );
460     }
461 private:
462     struct StaticInstanceWithInit {
463         T * operator () ( Data d ) {
464             static T instance(d);
465             return &instance;
466         }
467     };
468 };
469 
470 } // namespace rtl
471 
472 #endif // INCLUDED_RTL_INSTANCE_HXX
473