Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #ifndef INCLUDED_RTL_INSTANCE_HXX
21 : #define INCLUDED_RTL_INSTANCE_HXX
22 :
23 : #include "sal/config.h"
24 :
25 : #include "osl/doublecheckedlocking.h"
26 : #include "osl/getglobalmutex.hxx"
27 :
28 : namespace {
29 :
30 : /** A non-broken version of the double-checked locking pattern.
31 :
32 : See
33 : <http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html>
34 : for a description of double-checked locking, why it is broken, and how it
35 : can be fixed. Always use this template instead of spelling out the
36 : double-checked locking pattern explicitly, and only in those rare cases
37 : where that is not possible and you have to spell it out explicitly, at
38 : least call OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER() at the right
39 : places. That way, all platform-dependent code to make double-checked
40 : locking work can be kept in one place.
41 :
42 : Usage scenarios:
43 :
44 : 1 Static instance (most common case)
45 :
46 : Pattern:
47 :
48 : T * getInstance()
49 : {
50 : static T * pInstance = 0;
51 : if (!pInstance)
52 : {
53 : ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
54 : if (!pInstance)
55 : {
56 : static T aInstance;
57 : pInstance = &aInstance;
58 : }
59 : }
60 : return pInstance;
61 : }
62 :
63 : Code:
64 :
65 : #include "rtl/instance.hxx"
66 : #include "osl/getglobalmutex.hxx"
67 :
68 : namespace {
69 : struct Init
70 : {
71 : T * operator()()
72 : {
73 : static T aInstance;
74 : return &aInstance;
75 : }
76 : };
77 : }
78 :
79 : T * getInstance()
80 : {
81 : return rtl_Instance< T, Init, ::osl::MutexGuard,
82 : ::osl::GetGlobalMutex >::create(
83 : Init(), ::osl::GetGlobalMutex());
84 : }
85 :
86 : 2 Dynamic instance
87 :
88 : Pattern:
89 :
90 : T * getInstance()
91 : {
92 : static T * pInstance = 0;
93 : if (!pInstance)
94 : {
95 : ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
96 : if (!pInstance)
97 : pInstance = new T;
98 : }
99 : return pInstance;
100 : }
101 :
102 : Code:
103 :
104 : #include "rtl/instance.hxx"
105 : #include "osl/getglobalmutex.hxx"
106 :
107 : namespace {
108 : struct Init
109 : {
110 : T * operator()()
111 : {
112 : return new T;
113 : }
114 : };
115 : }
116 :
117 : T * getInstance()
118 : {
119 : return rtl_Instance< T, Init, ::osl::MutexGuard,
120 : ::osl::GetGlobalMutex >::create(
121 : Init(), ::osl::GetGlobalMutex());
122 : }
123 :
124 : 3 Other guard/mutex
125 :
126 : Pattern:
127 :
128 : T * getInstance()
129 : {
130 : static T * pInstance = 0;
131 : if (!pInstance)
132 : {
133 : SomeGuard aGuard(pSomeMutex);
134 : if (!pInstance)
135 : {
136 : static T aInstance;
137 : pInstance = &aInstance;
138 : }
139 : }
140 : return pInstance;
141 : }
142 :
143 : Code:
144 :
145 : #include "rtl/instance.hxx"
146 :
147 : namespace {
148 : struct InitInstance
149 : {
150 : T * operator()()
151 : {
152 : static T aInstance;
153 : return &aInstance;
154 : }
155 : };
156 :
157 : struct InitGuard
158 : {
159 : SomeMutex * operator()()
160 : {
161 : return pSomeMutex;
162 : }
163 : };
164 : }
165 :
166 : T * getInstance()
167 : {
168 : return rtl_Instance< T, InitInstance,
169 : SomeGuard, InitGuard >::create(
170 : InitInstance(), InitMutex());
171 : }
172 :
173 : 4 Calculate extra data
174 :
175 : Pattern:
176 :
177 : T * getInstance()
178 : {
179 : static T * pInstance = 0;
180 : if (!pInstance)
181 : {
182 : Data aData(...);
183 : ::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());
184 : if (!pInstance)
185 : {
186 : static T aInstance(aData);
187 : pInstance = &aInstance;
188 : }
189 : }
190 : return pInstance;
191 : }
192 :
193 : Code:
194 :
195 : #include "rtl/instance.hxx"
196 : #include "osl/getglobalmutex.hxx"
197 :
198 : namespace {
199 : struct InitInstance
200 : {
201 : T * operator()()
202 : {
203 : static T aInstance;
204 : return &aInstance;
205 : }
206 : }
207 :
208 : struct InitData
209 : {
210 : Data const & operator()()
211 : {
212 : return ...;
213 : }
214 : }
215 : }
216 :
217 : T * getInstance()
218 : {
219 : return rtl_Instance< T, InitInstance,
220 : ::osl::Mutex, ::osl::GetGlobalMutex,
221 : Data, InitData >::create(
222 : InitInstance(), ::osl::GetGlobalMutex(), InitData());
223 : }
224 :
225 : Some comments:
226 :
227 : For any instantiation of rtl_Instance, at most one call to a create method
228 : may occur in the program code: Each occurrence of a create method within
229 : the program code is supposed to return a fresh object instance on the
230 : first call, and that same object instance on subsequent calls; but
231 : independent occurrences of create methods are supposed to return
232 : independent object instances. Since there is a one-to-one correspondence
233 : between object instances and instantiations of rtl_Instance, the
234 : requirement should be clear. One measure to enforce the requirement is
235 : that rtl_Instance lives in an unnamed namespace, so that instantiations of
236 : rtl_Instance in different translation units will definitely be different
237 : instantiations. A drawback of that measure is that the name of the class
238 : needs a funny "hand coded" prefix "rtl_" instead of a proper namespace
239 : prefix like "::rtl::".
240 :
241 : A known problem with this template is when two occurrences of calls to
242 : create methods with identical template arguments appear in one translation
243 : unit. Those two places will share a single object instance. This can be
244 : avoided by using different Init structs (see the above code samples) in
245 : the two places.
246 :
247 : There is no need to make m_pInstance volatile, in order to avoid usage of
248 : stale copies of m_pInstance: At the first check, a thread will see that
249 : m_pInstance contains either 0 or a valid pointer. If it contains a valid
250 : pointer, it cannot be stale, and that pointer is used. If it contains 0,
251 : acquiring the mutex will ensure that the second check sees a non-stale
252 : value in all cases.
253 :
254 : On some compilers, the create methods would not be inlined if they
255 : contained any static variables, so m_pInstance is made a class member
256 : instead (and the create methods are inlined). But on MSC, the definition
257 : of the class member m_pInstance would cause compilation to fail with an
258 : internal compiler error. Since MSC is able to inline methods containing
259 : static variables, m_pInstance is moved into the methods there. Note that
260 : this only works well because for any instantiation of rtl_Instance at most
261 : one call to a create method should be present, anyway.
262 : */
263 : template< typename Inst, typename InstCtor,
264 : typename Guard, typename GuardCtor,
265 : typename Data = int, typename DataCtor = int >
266 : class rtl_Instance
267 : {
268 : public:
269 531269 : static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor)
270 : {
271 : #if defined _MSC_VER
272 : static Inst * m_pInstance = 0;
273 : #endif // _MSC_VER
274 531269 : Inst * p = m_pInstance;
275 531269 : if (!p)
276 : {
277 477 : Guard aGuard(aGuardCtor());
278 477 : p = m_pInstance;
279 477 : if (!p)
280 : {
281 477 : p = aInstCtor();
282 : OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
283 477 : m_pInstance = p;
284 477 : }
285 : }
286 : else
287 : {
288 : OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
289 : }
290 531269 : return p;
291 : }
292 :
293 207 : static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor,
294 : DataCtor aDataCtor)
295 : {
296 : #if defined _MSC_VER
297 : static Inst * m_pInstance = 0;
298 : #endif // _MSC_VER
299 207 : Inst * p = m_pInstance;
300 207 : if (!p)
301 : {
302 105 : Data aData(aDataCtor());
303 210 : Guard aGuard(aGuardCtor());
304 105 : p = m_pInstance;
305 105 : if (!p)
306 : {
307 105 : p = aInstCtor(aData);
308 : OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
309 105 : m_pInstance = p;
310 105 : }
311 : }
312 : else
313 : {
314 : OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
315 : }
316 207 : return p;
317 : }
318 :
319 : static inline Inst * create(InstCtor aInstCtor, GuardCtor aGuardCtor,
320 : const Data &rData)
321 : {
322 : #if defined _MSC_VER
323 : static Inst * m_pInstance = 0;
324 : #endif // _MSC_VER
325 : Inst * p = m_pInstance;
326 : if (!p)
327 : {
328 : Guard aGuard(aGuardCtor());
329 : p = m_pInstance;
330 : if (!p)
331 : {
332 : p = aInstCtor(rData);
333 : OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
334 : m_pInstance = p;
335 : }
336 : }
337 : else
338 : {
339 : OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
340 : }
341 : return p;
342 : }
343 :
344 : private:
345 : #if !defined _MSC_VER
346 : static Inst * m_pInstance;
347 : #endif // _MSC_VER
348 : };
349 :
350 : #if !defined _MSC_VER
351 : template< typename Inst, typename InstCtor,
352 : typename Guard, typename GuardCtor,
353 : typename Data, typename DataCtor >
354 : Inst *
355 : rtl_Instance< Inst, InstCtor, Guard, GuardCtor, Data, DataCtor >::m_pInstance
356 : = 0;
357 : #endif // _MSC_VER
358 :
359 : }
360 :
361 : namespace rtl {
362 :
363 : /** Helper base class for a late-initialized (default-constructed)
364 : static variable, implementing the double-checked locking pattern correctly.
365 :
366 : @derive
367 : Derive from this class (common practice), e.g.
368 : <pre>
369 : struct MyStatic : public rtl::Static<MyType, MyStatic> {};
370 : ...
371 : MyType & rStatic = MyStatic::get();
372 : ...
373 : </pre>
374 :
375 : @tparam T
376 : variable's type
377 : @tparam Unique
378 : Implementation trick to make the inner static holder unique,
379 : using the outer class
380 : (the one that derives from this base class)
381 : */
382 : #if HAVE_THREADSAFE_STATICS
383 : template<typename T, typename Unique>
384 : class Static {
385 : public:
386 : /** Gets the static. Mutual exclusion is implied by a functional
387 : -fthreadsafe-statics
388 :
389 : @return
390 : static variable
391 : */
392 28374859 : static T & get() {
393 28374859 : static T instance;
394 28374859 : return instance;
395 : }
396 : };
397 : #else
398 : template<typename T, typename Unique>
399 : class Static {
400 : public:
401 : /** Gets the static. Mutual exclusion is performed using the
402 : osl global mutex.
403 :
404 : @return
405 : static variable
406 : */
407 : static T & get() {
408 : return *rtl_Instance<
409 : T, StaticInstance,
410 : ::osl::MutexGuard, ::osl::GetGlobalMutex >::create(
411 : StaticInstance(), ::osl::GetGlobalMutex() );
412 : }
413 : private:
414 : struct StaticInstance {
415 : T * operator () () {
416 : static T instance;
417 : return &instance;
418 : }
419 : };
420 : };
421 : #endif
422 :
423 : /** Helper base class for a late-initialized (default-constructed)
424 : static variable, implementing the double-checked locking pattern correctly.
425 :
426 : @derive
427 : Derive from this class (common practice), e.g.
428 : <pre>
429 : struct MyStatic : public rtl::Static<MyType, MyStatic> {};
430 : ...
431 : MyType & rStatic = MyStatic::get();
432 : ...
433 : </pre>
434 :
435 : @tparam T
436 : variable's type
437 : @tparam Unique
438 : Implementation trick to make the inner static holder unique,
439 : using the outer class
440 : (the one that derives from this base class)
441 : */
442 : #if HAVE_THREADSAFE_STATICS
443 : template<typename T, typename Data, typename Unique>
444 : class StaticWithArg {
445 : public:
446 : /** Gets the static. Mutual exclusion is implied by a functional
447 : -fthreadsafe-statics
448 :
449 : @return
450 : static variable
451 : */
452 297065 : static T & get(const Data& rData) {
453 297065 : static T instance(rData);
454 297065 : return instance;
455 : }
456 :
457 : /** Gets the static. Mutual exclusion is implied by a functional
458 : -fthreadsafe-statics
459 :
460 : @return
461 : static variable
462 : */
463 8647 : static T & get(Data& rData) {
464 8647 : static T instance(rData);
465 8647 : return instance;
466 : }
467 : };
468 : #else
469 : template<typename T, typename Data, typename Unique>
470 : class StaticWithArg {
471 : public:
472 : /** Gets the static. Mutual exclusion is performed using the
473 : osl global mutex.
474 :
475 : @return
476 : static variable
477 : */
478 : static T & get(const Data& rData) {
479 : return *rtl_Instance<
480 : T, StaticInstanceWithArg,
481 : ::osl::MutexGuard, ::osl::GetGlobalMutex,
482 : Data >::create( StaticInstanceWithArg(),
483 : ::osl::GetGlobalMutex(),
484 : rData );
485 : }
486 :
487 : /** Gets the static. Mutual exclusion is performed using the
488 : osl global mutex.
489 :
490 : @return
491 : static variable
492 : */
493 : static T & get(Data& rData) {
494 : return *rtl_Instance<
495 : T, StaticInstanceWithArg,
496 : ::osl::MutexGuard, ::osl::GetGlobalMutex,
497 : Data >::create( StaticInstanceWithArg(),
498 : ::osl::GetGlobalMutex(),
499 : rData );
500 : }
501 : private:
502 : struct StaticInstanceWithArg {
503 : T * operator () (const Data& rData) {
504 : static T instance(rData);
505 : return &instance;
506 : }
507 :
508 : T * operator () (Data& rData) {
509 : static T instance(rData);
510 : return &instance;
511 : }
512 : };
513 : };
514 : #endif
515 :
516 : /** Helper class for a late-initialized static aggregate, e.g. an array,
517 : implementing the double-checked locking pattern correctly.
518 :
519 : @tparam T
520 : aggregate's element type
521 : @tparam InitAggregate
522 : initializer functor class
523 : */
524 : #if HAVE_THREADSAFE_STATICS
525 : template<typename T, typename InitAggregate>
526 : class StaticAggregate {
527 : public:
528 : /** Gets the static aggregate, late-initializing.
529 : Mutual exclusion is implied by a functional
530 : -fthreadsafe-statics
531 :
532 : @return
533 : aggregate
534 : */
535 12702034 : static T * get() {
536 12702034 : static T *instance = InitAggregate()();
537 12702034 : return instance;
538 : }
539 : };
540 : #else
541 : template<typename T, typename InitAggregate>
542 : class StaticAggregate {
543 : public:
544 : /** Gets the static aggregate, late-initializing.
545 : Mutual exclusion is performed using the osl global mutex.
546 :
547 : @return
548 : aggregate
549 : */
550 : static T * get() {
551 : return rtl_Instance<
552 : T, InitAggregate,
553 : ::osl::MutexGuard, ::osl::GetGlobalMutex >::create(
554 : InitAggregate(), ::osl::GetGlobalMutex() );
555 : }
556 : };
557 : #endif
558 : /** Helper base class for a late-initialized static variable,
559 : implementing the double-checked locking pattern correctly.
560 :
561 : @derive
562 : Derive from this class (common practice),
563 : providing an initializer functor class, e.g.
564 : <pre>
565 : struct MyStatic : public rtl::StaticWithInit<MyType, MyStatic> {
566 : MyType operator () () {
567 : ...
568 : return MyType( ... );
569 : }
570 : };
571 : ...
572 : MyType & rStatic = MyStatic::get();
573 : ...
574 : </pre>
575 :
576 : @tparam T
577 : variable's type
578 : @tparam InitData
579 : initializer functor class
580 : @tparam Unique
581 : Implementation trick to make the inner static holder unique,
582 : using the outer class
583 : (the one that derives from this base class).
584 : Default is InitData (common practice).
585 : @tparam Data
586 : Initializer functor's return type.
587 : Default is T (common practice).
588 : */
589 : #if HAVE_THREADSAFE_STATICS
590 : template<typename T, typename InitData,
591 : typename Unique = InitData, typename Data = T>
592 : class StaticWithInit {
593 : public:
594 : /** Gets the static. Mutual exclusion is implied by a functional
595 : -fthreadsafe-statics
596 :
597 : @return
598 : static variable
599 : */
600 26854827 : static T & get() {
601 26854827 : static T instance = InitData()();
602 26854827 : return instance;
603 : }
604 : };
605 : #else
606 : template<typename T, typename InitData,
607 : typename Unique = InitData, typename Data = T>
608 : class StaticWithInit {
609 : public:
610 : /** Gets the static. Mutual exclusion is performed using the
611 : osl global mutex.
612 :
613 : @return
614 : static variable
615 : */
616 : static T & get() {
617 : return *rtl_Instance<
618 : T, StaticInstanceWithInit,
619 : ::osl::MutexGuard, ::osl::GetGlobalMutex,
620 : Data, InitData >::create( StaticInstanceWithInit(),
621 : ::osl::GetGlobalMutex(),
622 : InitData() );
623 : }
624 : private:
625 : struct StaticInstanceWithInit {
626 : T * operator () ( Data d ) {
627 : static T instance(d);
628 : return &instance;
629 : }
630 : };
631 : };
632 : #endif
633 : } // namespace rtl
634 :
635 : #endif // INCLUDED_RTL_INSTANCE_HXX
636 :
637 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|