1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
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_INCLUDE_STATIC_INSTANCE_H_
12 #define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_
13 
14 #include <assert.h>
15 
16 #include "webrtc/system_wrappers/include/critical_section_wrapper.h"
17 #ifdef _WIN32
18 #include "webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h"
19 #endif
20 
21 namespace webrtc {
22 
23 enum CountOperation {
24   kRelease,
25   kAddRef,
26   kAddRefNoCreate
27 };
28 enum CreateOperation {
29   kInstanceExists,
30   kCreate,
31   kDestroy
32 };
33 
34 template <class T>
35 // Construct On First Use idiom. Avoids
36 // "static initialization order fiasco".
GetStaticInstance(CountOperation count_operation)37 static 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());
50   CriticalSectionScoped lock(crit_sect);
51 
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 new_value = InterlockedDecrement(&instance_count);
120     if (new_value == 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)) {
131       InterlockedExchangePointer(reinterpret_cast<void * volatile*>(&instance),
132                                  new_instance);
133     } else {
134       InterlockedDecrement(&instance_count);
135       if (new_instance) {
136         delete static_cast<T*>(new_instance);
137       }
138     }
139   } 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_INCLUDE_STATIC_INSTANCE_H_
154