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