guard
#include "1.h"
#include "pthread.h"
static pthread_mutex_t __guard_mutex;
static pthread_once_t __once_control = PTHREAD_ONCE_INIT;
static void makeRecusiveMutex() // 将 __guard_mutex 初始化为递归锁
{
pthread_mutexattr_t recursiveMutexAttr;
pthread_mutexattr_init(&recursiveMutexAttr);
pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr);
}
//__attribute__((noinline))
static pthread_mutex_t* guard_mutex()
{
pthread_once(&__once_control, &makeRecusiveMutex); // 一次性初始化 __guard_mutex
return &__guard_mutex;
}
// helper functions for getting/setting flags in guard_object
static bool initializerHasRun(uint64_t* guard_object)
{
// 取最低字节作为是否已初始化的标志
return ( *((uint8_t*)guard_object) != 0 );
}
static void setInitializerHasRun(uint64_t* guard_object)
{
*((uint8_t*)guard_object) = 1;
}
static bool inUse(uint64_t* guard_object)
{
// 取次低字节作为 guard_object 是否正在被当前线程使用的标志
return ( ((uint8_t*)guard_object)[1] != 0 );
}
static void setInUse(uint64_t* guard_object)
{
((uint8_t*)guard_object)[1] = 1;
}
static void setNotInUse(uint64_t* guard_object)
{
((uint8_t*)guard_object)[1] = 0;
}
//
// Returns 1 if the caller needs to run the initializer and then either
// call __cxa_guard_release() or __cxa_guard_abort(). If zero is returned,
// then the initializer has already been run. This function blocks
// if another thread is currently running the initializer. This function
// aborts if called again on the same guard object without an intervening
// call to __cxa_guard_release() or __cxa_guard_abort().
//
int __cxa_guard_acquire(uint64_t* guard_object)
{
// Double check that the initializer has not already been run
if ( initializerHasRun(guard_object) ) // 如果对象已被初始化
return 0;
// We now need to acquire a lock that allows only one thread
// to run the initializer. If a different thread calls
// __cxa_guard_acquire() with the same guard object, we want
// that thread to block until this thread is done running the
// initializer and calls __cxa_guard_release(). But if the same
// thread calls __cxa_guard_acquire() with the same guard object,
// we want to abort.
// To implement this we have one global pthread recursive mutex
// shared by all guard objects, but only one at a time.
int result = ::pthread_mutex_lock(guard_mutex());
if ( result != 0 ) {
// abort_message("__cxa_guard_acquire(): pthread_mutex_lock "
// "failed with %d\n", result);
}
// At this point all other threads will block in __cxa_guard_acquire()
// Check if another thread has completed initializer run
if ( initializerHasRun(guard_object) ) { // 再次判断, 对象是否已被其他线程初始化
int result = ::pthread_mutex_unlock(guard_mutex());
if ( result != 0 ) {
// abort_message("__cxa_guard_acquire(): pthread_mutex_unlock "
// "failed with %d\n", result);
}
return 0;
}
// The pthread mutex is recursive to allow other lazy initialized
// function locals to be evaluated during evaluation of this one.
// But if the same thread can call __cxa_guard_acquire() on the
// *same* guard object again, we call abort();
if ( inUse(guard_object) ) { // 防止同一线程对对象多次初始化
// abort_message("__cxa_guard_acquire(): initializer for function "
// "local static variable called enclosing function\n");
}
// mark this guard object as being in use
setInUse(guard_object);
// return non-zero to tell caller to run initializer
return 1;
}
//
// Sets the first byte of the guard_object to a non-zero value.
// Releases any locks acquired by __cxa_guard_acquire().
//
void __cxa_guard_release(uint64_t* guard_object)
{
// first mark initalizer as having been run, so
// other threads won‘t try to re-run it.
setInitializerHasRun(guard_object);
// release global mutex
int result = ::pthread_mutex_unlock(guard_mutex());
if ( result != 0 ) {
// abort_message("__cxa_guard_acquire(): pthread_mutex_unlock "
// "failed with %d\n", result);
}
}
//
// Releases any locks acquired by __cxa_guard_acquire().
//
//void __cxxabiv1::__cxa_guard_abort(uint64_t* guard_object) // 初始化异常时被调用
//{
// int result = ::pthread_mutex_unlock(guard_mutex());
// if ( result != 0 ) {
// abort_message("__cxa_guard_abort(): pthread_mutex_unlock "
// "failed with %d\n", result);
// }
// // now reset state, so possible to try to initialize again
// setNotInUse(guard_object);
//}