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_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
18#include "fix_interlocked_exchange_pointer_win.h"
19#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());
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 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)) {
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_INTERFACE_STATICINSTANCETEMPLATE_H_
154