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 occurance 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 occurances 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 occurences 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