blob: e9bf81d951ebec749aca5a7d8dc303bbf6b06d84 [file] [log] [blame]
henrike@webrtc.org315282c2011-12-09 17:46:20 +00001/*
andrew@webrtc.orge713fd02012-04-10 07:13:46 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org315282c2011-12-09 17:46:20 +00003 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
kjellanderd56d68c2015-11-02 02:12:41 -080011#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_
12#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_
henrike@webrtc.org315282c2011-12-09 17:46:20 +000013
14#include <assert.h>
15
kthelgasond701dfd2017-03-27 07:24:57 -070016#include "webrtc/base/criticalsection.h"
henrike@webrtc.org315282c2011-12-09 17:46:20 +000017#ifdef _WIN32
Henrik Kjellander98f53512015-10-28 18:17:40 +010018#include "webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h"
henrike@webrtc.org315282c2011-12-09 17:46:20 +000019#endif
20
21namespace webrtc {
22
23enum CountOperation {
24 kRelease,
25 kAddRef,
26 kAddRefNoCreate
27};
28enum CreateOperation {
29 kInstanceExists,
30 kCreate,
31 kDestroy
32};
33
34template <class T>
35// Construct On First Use idiom. Avoids
36// "static initialization order fiasco".
37static T* GetStaticInstance(CountOperation count_operation) {
38 // TODO (hellner): use atomic wrapper instead.
39 static volatile long instance_count = 0;
40 static T* volatile instance = NULL;
41 CreateOperation state = kInstanceExists;
42#ifndef _WIN32
kthelgasond701dfd2017-03-27 07:24:57 -070043 rtc::CriticalSection crit_sect;
44 rtc::CritScope lock(&crit_sect);
henrike@webrtc.org315282c2011-12-09 17:46:20 +000045
46 if (count_operation ==
47 kAddRefNoCreate && instance_count == 0) {
48 return NULL;
49 }
50 if (count_operation ==
51 kAddRef ||
52 count_operation == kAddRefNoCreate) {
53 instance_count++;
54 if (instance_count == 1) {
55 state = kCreate;
56 }
57 } else {
58 instance_count--;
59 if (instance_count == 0) {
60 state = kDestroy;
61 }
62 }
63 if (state == kCreate) {
64 instance = T::CreateInstance();
65 } else if (state == kDestroy) {
66 T* old_instance = instance;
67 instance = NULL;
68 // The state will not change past this point. Release the critical
69 // section while deleting the object in case it would be blocking on
70 // access back to this object. (This is the case for the tracing class
71 // since the thread owned by the tracing class also traces).
72 // TODO(hellner): this is a bit out of place but here goes, de-couple
73 // thread implementation with trace implementation.
kthelgasond701dfd2017-03-27 07:24:57 -070074 crit_sect.Leave();
henrike@webrtc.org315282c2011-12-09 17:46:20 +000075 if (old_instance) {
76 delete old_instance;
77 }
78 // Re-acquire the lock since the scoped critical section will release
79 // it.
kthelgasond701dfd2017-03-27 07:24:57 -070080 crit_sect.Enter();
henrike@webrtc.org315282c2011-12-09 17:46:20 +000081 return NULL;
82 }
83#else // _WIN32
84 if (count_operation ==
85 kAddRefNoCreate && instance_count == 0) {
86 return NULL;
87 }
88 if (count_operation == kAddRefNoCreate) {
89 if (1 == InterlockedIncrement(&instance_count)) {
90 // The instance has been destroyed by some other thread. Rollback.
91 InterlockedDecrement(&instance_count);
92 assert(false);
93 return NULL;
94 }
95 // Sanity to catch corrupt state.
96 if (instance == NULL) {
97 assert(false);
98 InterlockedDecrement(&instance_count);
99 return NULL;
100 }
101 } else if (count_operation == kAddRef) {
102 if (instance_count == 0) {
103 state = kCreate;
104 } else {
105 if (1 == InterlockedIncrement(&instance_count)) {
106 // InterlockedDecrement because reference count should not be
107 // updated just yet (that's done when the instance is created).
108 InterlockedDecrement(&instance_count);
109 state = kCreate;
110 }
111 }
112 } else {
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000113 int new_value = InterlockedDecrement(&instance_count);
114 if (new_value == 0) {
henrike@webrtc.org315282c2011-12-09 17:46:20 +0000115 state = kDestroy;
116 }
117 }
118
119 if (state == kCreate) {
120 // Create instance and let whichever thread finishes first assign its
121 // local copy to the global instance. All other threads reclaim their
122 // local copy.
123 T* new_instance = T::CreateInstance();
124 if (1 == InterlockedIncrement(&instance_count)) {
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000125 InterlockedExchangePointer(reinterpret_cast<void * volatile*>(&instance),
andrew@webrtc.orge713fd02012-04-10 07:13:46 +0000126 new_instance);
henrike@webrtc.org315282c2011-12-09 17:46:20 +0000127 } else {
128 InterlockedDecrement(&instance_count);
129 if (new_instance) {
130 delete static_cast<T*>(new_instance);
131 }
132 }
henrike@webrtc.org315282c2011-12-09 17:46:20 +0000133 } else if (state == kDestroy) {
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000134 T* old_value = static_cast<T*>(InterlockedExchangePointer(
135 reinterpret_cast<void * volatile*>(&instance), NULL));
henrike@webrtc.org315282c2011-12-09 17:46:20 +0000136 if (old_value) {
137 delete static_cast<T*>(old_value);
138 }
139 return NULL;
140 }
141#endif // #ifndef _WIN32
142 return instance;
143}
144
145} // namspace webrtc
146
kjellanderd56d68c2015-11-02 02:12:41 -0800147#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_