blob: b670f969c9fd5a03dec3a3c54e67c854e07ece15 [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
11#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATICINSTANCETEMPLATE_H_
12#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATICINSTANCETEMPLATE_H_
13
14#include <assert.h>
15
16#include "critical_section_wrapper.h"
17#ifdef _WIN32
andrew@webrtc.org59ccd5c2011-12-15 00:17:43 +000018#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
43 // This memory is staticly allocated once. The application does not try to
44 // free this memory. This approach is taken to avoid issues with
45 // destruction order for statically allocated memory. The memory will be
46 // reclaimed by the OS and memory leak tools will not recognize memory
47 // reachable from statics leaked so no noise is added by doing this.
48 static CriticalSectionWrapper* crit_sect(
49 CriticalSectionWrapper::CreateCriticalSection());
henrike@webrtc.orgbfa80ce2011-12-15 17:59:58 +000050 CriticalSectionScoped lock(crit_sect);
henrike@webrtc.org315282c2011-12-09 17:46:20 +000051
52 if (count_operation ==
53 kAddRefNoCreate && instance_count == 0) {
54 return NULL;
55 }
56 if (count_operation ==
57 kAddRef ||
58 count_operation == kAddRefNoCreate) {
59 instance_count++;
60 if (instance_count == 1) {
61 state = kCreate;
62 }
63 } else {
64 instance_count--;
65 if (instance_count == 0) {
66 state = kDestroy;
67 }
68 }
69 if (state == kCreate) {
70 instance = T::CreateInstance();
71 } else if (state == kDestroy) {
72 T* old_instance = instance;
73 instance = NULL;
74 // The state will not change past this point. Release the critical
75 // section while deleting the object in case it would be blocking on
76 // access back to this object. (This is the case for the tracing class
77 // since the thread owned by the tracing class also traces).
78 // TODO(hellner): this is a bit out of place but here goes, de-couple
79 // thread implementation with trace implementation.
80 crit_sect->Leave();
81 if (old_instance) {
82 delete old_instance;
83 }
84 // Re-acquire the lock since the scoped critical section will release
85 // it.
86 crit_sect->Enter();
87 return NULL;
88 }
89#else // _WIN32
90 if (count_operation ==
91 kAddRefNoCreate && instance_count == 0) {
92 return NULL;
93 }
94 if (count_operation == kAddRefNoCreate) {
95 if (1 == InterlockedIncrement(&instance_count)) {
96 // The instance has been destroyed by some other thread. Rollback.
97 InterlockedDecrement(&instance_count);
98 assert(false);
99 return NULL;
100 }
101 // Sanity to catch corrupt state.
102 if (instance == NULL) {
103 assert(false);
104 InterlockedDecrement(&instance_count);
105 return NULL;
106 }
107 } else if (count_operation == kAddRef) {
108 if (instance_count == 0) {
109 state = kCreate;
110 } else {
111 if (1 == InterlockedIncrement(&instance_count)) {
112 // InterlockedDecrement because reference count should not be
113 // updated just yet (that's done when the instance is created).
114 InterlockedDecrement(&instance_count);
115 state = kCreate;
116 }
117 }
118 } else {
119 int newValue = InterlockedDecrement(&instance_count);
120 if (newValue == 0) {
121 state = kDestroy;
122 }
123 }
124
125 if (state == kCreate) {
126 // Create instance and let whichever thread finishes first assign its
127 // local copy to the global instance. All other threads reclaim their
128 // local copy.
129 T* new_instance = T::CreateInstance();
130 if (1 == InterlockedIncrement(&instance_count)) {
andrew@webrtc.orge713fd02012-04-10 07:13:46 +0000131 InterlockedExchangePointer(reinterpret_cast<void* volatile*>(&instance),
132 new_instance);
henrike@webrtc.org315282c2011-12-09 17:46:20 +0000133 } else {
134 InterlockedDecrement(&instance_count);
135 if (new_instance) {
136 delete static_cast<T*>(new_instance);
137 }
138 }
henrike@webrtc.org315282c2011-12-09 17:46:20 +0000139 } else if (state == kDestroy) {
140 T* old_value = static_cast<T*> (InterlockedExchangePointer(
141 reinterpret_cast<void* volatile*>(&instance), NULL));
142 if (old_value) {
143 delete static_cast<T*>(old_value);
144 }
145 return NULL;
146 }
147#endif // #ifndef _WIN32
148 return instance;
149}
150
151} // namspace webrtc
152
153#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATICINSTANCETEMPLATE_H_