1/* 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25/* 26 * globalizer - multiscope globalization services 27 */ 28#ifndef _H_GLOBALIZER 29#define _H_GLOBALIZER 30 31#include <security_utilities/threading.h> 32#include <memory> 33#include <set> 34#include <dispatch/dispatch.h> 35#include <libkern/OSAtomic.h> 36 37namespace Security { 38 39 40// 41// GlobalNexus is the common superclass of all globality scopes. 42// A Nexus is an *access point* to the *single* object of a given 43// type in the Nexus's particular scope. 44// 45class GlobalNexus { 46public: 47 class Error : public std::exception { 48 public: 49 virtual ~Error() throw(); 50 const char * const message; 51 Error(const char *m) : message(m) { } 52 const char *what() const throw() { return message; } 53 }; 54}; 55 56 57class ModuleNexusCommon : public GlobalNexus { 58private: 59 void do_create(void *(*make)()); 60 61protected: 62 void *create(void *(*make)()); 63 void lock() {OSSpinLockLock(&access);} 64 void unlock() {OSSpinLockUnlock(&access);} 65 66protected: 67 // all of these will be statically initialized to zero 68 void *pointer; 69 dispatch_once_t once; 70 OSSpinLock access; 71}; 72 73template <class Type> 74class ModuleNexus : public ModuleNexusCommon { 75public: 76 Type &operator () () 77 { 78 lock(); 79 80 try 81 { 82 if (pointer == NULL) 83 { 84 pointer = create(make); 85 } 86 87 unlock(); 88 } 89 catch (...) 90 { 91 unlock(); 92 throw; 93 } 94 95 return *reinterpret_cast<Type *>(pointer); 96 } 97 98 // does the object DEFINITELY exist already? 99 bool exists() const 100 { 101 bool result; 102 lock(); 103 result = pointer != NULL; 104 unlock(); 105 return result; 106 } 107 108 // destroy the object (if any) and start over - not really thread-safe 109 void reset() 110 { 111 lock(); 112 if (pointer != NULL) 113 { 114 delete reinterpret_cast<Type *>(pointer); 115 pointer = NULL; 116 once = 0; 117 } 118 unlock(); 119 } 120 121private: 122 static void *make() { return new Type; } 123}; 124 125template <class Type> 126class CleanModuleNexus : public ModuleNexus<Type> { 127public: 128 ~CleanModuleNexus() 129 { 130 secdebug("nexus", "ModuleNexus %p destroyed object 0x%x", 131 this, ModuleNexus<Type>::pointer); 132 delete reinterpret_cast<Type *>(ModuleNexus<Type>::pointer); 133 } 134}; 135 136typedef std::set<void*> RetentionSet; 137 138// 139// A thread-scope nexus is tied to a particular native thread AND 140// a particular nexus object. Its scope is all code in any one thread 141// that access that particular Nexus object. Any number of Nexus objects 142// can exist, and each implements a different scope for each thread. 143// NOTE: ThreadNexus is dynamically constructed. If you want static, 144// zero-initialization ThreadNexi, put them inside a ModuleNexus. 145// 146template <class Type> 147class ThreadNexus : public GlobalNexus { 148public: 149 ThreadNexus() : mSlot(true) { } 150 151 Type &operator () () 152 { 153 // no thread contention here! 154 if (Type *p = mSlot) 155 return *p; 156 mSlot = new Type; 157 return *mSlot; 158 } 159 160private: 161 PerThreadPointer<Type> mSlot; 162}; 163 164 165// 166// A ProcessNexus is global within a single process, regardless of 167// load module boundaries. You can have any number of ProcessNexus 168// scopes, each identified by a C string (compared by value, not pointer). 169// 170class ProcessNexusBase : public GlobalNexus { 171protected: 172 ProcessNexusBase(const char *identifier); 173 174 struct Store { 175 void *mObject; 176 Mutex mLock; 177 }; 178 Store *mStore; 179}; 180 181template <class Type> 182class ProcessNexus : public ProcessNexusBase { 183public: 184 ProcessNexus(const char *identifier) : ProcessNexusBase(identifier) { } 185 Type &operator () (); 186 187private: 188 Type *mObject; 189}; 190 191template <class Type> 192Type &ProcessNexus<Type>::operator () () 193{ 194#if !defined(PTHREAD_STRICT) 195 // not strictly kosher POSIX, but pointers are usually atomic types 196 if (mStore->mObject) 197 return *reinterpret_cast<Type *>(mStore->mObject); 198#endif 199 StLock<Mutex> _(mStore->mLock); 200 if (mStore->mObject == NULL) 201 mStore->mObject = new Type; 202 return *reinterpret_cast<Type *>(mStore->mObject); 203}; 204 205 206} // end namespace Security 207 208#endif //_H_GLOBALIZER 209