Home | History | Annotate | Download | only in interface
      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 
     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".
     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 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