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