]>
Commit | Line | Data |
---|---|---|
bac41a7b A |
1 | /* |
2 | * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | ||
19 | // | |
20 | // globalizer - multiscope globalization services. | |
21 | // | |
22 | // This is a tentative, partial implementation. | |
23 | // Status: | |
24 | // module scope: constructs, optional cleanup | |
25 | // thread scope: constructs, optional cleanup | |
26 | // process scope: not implemented (obsolete implementation, unused) | |
27 | // system scope: not implemented (probably never will) | |
28 | // | |
29 | // @@@ Assumption: {bool,T*} atomic unless PTHREAD_STRICT | |
30 | // | |
bac41a7b | 31 | #include <Security/globalizer.h> |
29654253 | 32 | #include <Security/debugging.h> |
bac41a7b A |
33 | #include <cstdlib> |
34 | ||
35 | ||
36 | // | |
37 | // The Error class thrown if Nexus operations fail | |
38 | // | |
29654253 | 39 | GlobalNexus::Error::~Error() throw() |
bac41a7b A |
40 | { |
41 | } | |
42 | ||
43 | ||
44 | // | |
45 | // The long (and possibly contentious) path of ModuleNexus() | |
46 | // | |
47 | // Briefly, the trick here is to go through a three-stage sequence | |
48 | // to lazily construct a unique singleton object, no matter how many | |
49 | // threads all of a sudden decide they need it. | |
50 | // State sequence: | |
51 | // State 0: pointer == 0, not initialized, idle | |
52 | // State 1: pointer == mutexp | 0x1, where mutexp points to a Mutex | |
53 | // used to serialize construction of the singleton object | |
54 | // State 2: pointer == &singleton, and we're done | |
55 | // | |
56 | // TAKE NOTE: | |
57 | // This code is optimized with a particular issue in mind: when placed | |
58 | // into static storage (as ModuleNexi are wont to), it should not require | |
59 | // dynamic initialization. This is important because our code is, in effect, | |
60 | // linked into just about every program in the system. The price we pay | |
61 | // for this coolness is | |
62 | // (a) This won't work *except* in static storage (not on stack or heap) | |
63 | // (b) We slightly fracture portability (see below) | |
64 | // This has been considered Worth It, at least for now. Before you throw | |
65 | // up and throw this code out, please try to figure out whether you know | |
66 | // the Whole Story. Thank you. | |
67 | // | |
68 | // WARNING: | |
69 | // This code makes the following non-portable assumptions: | |
70 | // (a) NULL == 0 (binary representation of NULL pointer is zero value) | |
29654253 A |
71 | // (b) Pointers acquired from new have at least their LSB zero (are at |
72 | // least two-byte aligned). | |
bac41a7b A |
73 | // It seems like it's been a while since anyone made a machine/runtime that |
74 | // violated either of those. But you have been warned. | |
75 | // | |
76 | #if defined(_HAVE_ATOMIC_OPERATIONS) | |
77 | ||
78 | AtomicWord ModuleNexusCommon::create(void *(*make)()) | |
79 | { | |
80 | sync++; // keep mutex alive if needed | |
81 | retry: | |
82 | AtomicWord initialPointer = pointer; // latch pointer | |
83 | if (!initialPointer || (initialPointer & 0x1)) { | |
84 | Mutex *mutex; | |
85 | if (initialPointer == 0) { | |
86 | mutex = new Mutex(false); // don't bother debugging this one | |
87 | mutex->lock(); | |
88 | if (atomicStore(pointer, AtomicWord(mutex) | 0x1, 0) != 0) { | |
89 | // somebody beat us to the lead - back off | |
90 | mutex->unlock(); | |
91 | delete mutex; | |
92 | goto retry; | |
93 | } | |
94 | // we have the ball | |
95 | try { | |
96 | void *singleton = make(); | |
97 | pointer = AtomicWord(singleton); | |
98 | // we need a write barrier here, but the mutex->unlock below provides it for free | |
bac41a7b | 99 | } catch (...) { |
df0e469f | 100 | secdebug("nexus", "ModuleNexus %p construction failed", this); |
bac41a7b | 101 | mutex->unlock(); |
29654253 A |
102 | if (--sync == 0) { |
103 | delete mutex; | |
104 | pointer = 0; | |
105 | } | |
bac41a7b A |
106 | throw; |
107 | } | |
108 | } else { | |
29654253 | 109 | mutex = reinterpret_cast<Mutex *>(initialPointer & ~0x1); |
bac41a7b A |
110 | mutex->lock(); // we'll wait here |
111 | } | |
112 | mutex->unlock(); | |
113 | //@@@ retry if not resolved -- or fail here (with "object can't be built") | |
114 | if (--sync == 0) | |
115 | delete mutex; | |
116 | } | |
117 | return pointer; | |
118 | } | |
119 | ||
120 | #endif //_HAVE_ATOMIC_OPERATIONS | |
121 | ||
122 | ||
123 | // | |
124 | // Process nexus operation | |
125 | // | |
126 | ProcessNexusBase::ProcessNexusBase(const char *identifier) | |
127 | { | |
128 | const char *env = getenv(identifier); | |
129 | if (env == NULL) { // perhaps we're first... | |
130 | auto_ptr<Store> store(new Store); | |
131 | char form[2*sizeof(Store *) + 2]; | |
132 | sprintf(form, "*%p", &store); | |
133 | setenv(identifier, form, 0); // do NOT overwrite... | |
134 | env = getenv(identifier); // ... and refetch to resolve races | |
135 | if (sscanf(env, "*%p", &mStore) != 1) | |
136 | CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR /*"environment communication failed" */); | |
137 | if (mStore == store.get()) // we won the race... | |
138 | store.release(); // ... so keep the store | |
139 | } else | |
140 | if (sscanf(env, "*%p", &mStore) != 1) | |
141 | CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR /*"environment communication failed"*/); | |
142 | } |