]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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 | // This is a tentative, partial implementation. | |
29 | // Status: | |
30 | // module scope: constructs, optional cleanup | |
31 | // thread scope: constructs, optional cleanup | |
32 | // process scope: not implemented (obsolete implementation, unused) | |
33 | // system scope: not implemented (probably never will) | |
34 | // | |
35 | // @@@ Assumption: {bool,T*} atomic unless PTHREAD_STRICT | |
36 | // | |
37 | #include <security_utilities/globalizer.h> | |
38 | #include <security_utilities/debugging.h> | |
39 | #include <cstdlib> | |
40 | #include <stdexcept> | |
41 | ||
42 | // | |
43 | // The Error class thrown if Nexus operations fail | |
44 | // | |
45 | GlobalNexus::Error::~Error() throw() | |
46 | { | |
47 | } | |
48 | ||
49 | ||
50 | // | |
51 | // The long (and possibly contentious) path of ModuleNexus() | |
52 | // | |
53 | // Briefly, the trick here is to go through a three-stage sequence | |
54 | // to lazily construct a unique singleton object, no matter how many | |
55 | // threads all of a sudden decide they need it. | |
56 | // State sequence: | |
57 | // State 0: pointer == 0, not initialized, idle | |
58 | // State 1: pointer == mutexp | 0x1, where mutexp points to a Mutex | |
59 | // used to serialize construction of the singleton object | |
60 | // State 2: pointer == &singleton, and we're done | |
61 | // | |
62 | // TAKE NOTE: | |
63 | // This code is optimized with a particular issue in mind: when placed | |
64 | // into static storage (as ModuleNexi are wont to), it should not require | |
65 | // dynamic initialization. This is important because our code is, in effect, | |
66 | // linked into just about every program in the system. The price we pay | |
67 | // for this coolness is | |
68 | // (a) This won't work *except* in static storage (not on stack or heap) | |
69 | // (b) We slightly fracture portability (see below) | |
70 | // This has been considered Worth It, at least for now. Before you throw | |
71 | // up and throw this code out, please try to figure out whether you know | |
72 | // the Whole Story. Thank you. | |
73 | // | |
74 | // WARNING: | |
75 | // This code makes the following non-portable assumptions: | |
76 | // (a) NULL == 0 (binary representation of NULL pointer is zero value) | |
77 | // (b) Pointers acquired from new have at least their LSB zero (are at | |
78 | // least two-byte aligned). | |
79 | // It seems like it's been a while since anyone made a machine/runtime that | |
80 | // violated either of those. But you have been warned. | |
81 | // | |
82 | void *ModuleNexusCommon::create(void *(*make)()) | |
83 | { | |
84 | sync++; // keep mutex alive if needed | |
85 | retry: | |
86 | void *initialPointer = Atomic<void *>::load(pointer); // latch pointer | |
87 | if (!initialPointer || (uintptr_t(initialPointer) & 0x1)) { | |
88 | Mutex *mutex; | |
89 | if (initialPointer == 0) { | |
90 | mutex = new Mutex; | |
91 | mutex->lock(); | |
92 | if (!Atomic<void *>::casb(0, (void *)(uintptr_t(mutex) | 0x1), pointer)) { | |
93 | // somebody beat us to the lead - back off | |
94 | mutex->unlock(); | |
95 | delete mutex; | |
96 | goto retry; | |
97 | } | |
98 | // we have the ball | |
99 | try { | |
100 | void *singleton = make(); | |
101 | pointer = singleton; | |
102 | // we need a write barrier here, but the mutex->unlock below provides it for free | |
103 | } catch (...) { | |
104 | secdebug("nexus", "ModuleNexus %p construction failed", this); | |
105 | mutex->unlock(); | |
106 | if (--sync == 0) { | |
107 | delete mutex; | |
108 | pointer = 0; | |
109 | } | |
110 | throw; | |
111 | } | |
112 | } else { | |
113 | mutex = reinterpret_cast<Mutex *>(uintptr_t(initialPointer) & ~0x1); | |
114 | mutex->lock(); // we'll wait here | |
115 | } | |
116 | mutex->unlock(); | |
117 | //@@@ retry if not resolved -- or fail here (with "object can't be built") | |
118 | if (--sync == 0) | |
119 | delete mutex; | |
120 | } | |
121 | return pointer; | |
122 | } | |
123 | ||
124 | ||
125 | // thread nexus static globals | |
126 | ModuleNexus<Mutex> ThreadNexusBase::mInstanceLock; | |
127 | ||
128 | // Thread nexus globals | |
129 | ModuleNexus<RetentionSet> ThreadNexusBase::mInstances; | |
130 | ||
131 | // | |
132 | // Process nexus operation | |
133 | // | |
134 | ProcessNexusBase::ProcessNexusBase(const char *identifier) | |
135 | { | |
136 | const char *env = getenv(identifier); | |
137 | if (env == NULL) { // perhaps we're first... | |
138 | auto_ptr<Store> store(new Store); | |
139 | char form[2*sizeof(Store *) + 2]; | |
140 | sprintf(form, "*%p", &store); | |
141 | setenv(identifier, form, 0); // do NOT overwrite... | |
142 | env = getenv(identifier); // ... and refetch to resolve races | |
143 | if (sscanf(env, "*%p", &mStore) != 1) | |
144 | throw std::runtime_error("environment communication failed"); | |
145 | if (mStore == store.get()) // we won the race... | |
146 | store.release(); // ... so keep the store | |
147 | } else | |
148 | if (sscanf(env, "*%p", &mStore) != 1) | |
149 | throw std::runtime_error("environment communication failed"); | |
150 | } |