]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_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. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include <libkern/OSAtomic.h> | |
30 | ||
31 | enum { | |
32 | false = 0, | |
33 | true = 1 | |
34 | }; | |
35 | ||
36 | #ifndef NULL | |
37 | #define NULL ((void *)0) | |
38 | #endif | |
39 | ||
40 | /* | |
41 | * atomic operations | |
42 | * these are _the_ atomic operations, currently cast atop CompareAndSwap, | |
43 | * which is implemented in assembler. if we are worried about the cost of | |
44 | * this layering (we shouldn't be), then all this stuff could be | |
45 | * implemented in assembler, as it is in MacOS8/9 | |
46 | * (derived from SuperMario/NativeLibs/IO/DriverServices/Synchronization.s, | |
47 | * which I wrote for NuKernel in a previous life with a different last name...) | |
48 | * | |
49 | * native Boolean CompareAndSwap(UInt32 oldValue, UInt32 newValue, UInt32 * oldValuePtr); | |
50 | * | |
51 | * We've since implemented a few more of these -- OSAddAtomic, OSDequeueAtomic, | |
52 | * OSEnqueueAtomic etc -- in assembler, either for speed or correctness. See also the | |
53 | * commpage atomic operations, and the platform specific versions. | |
54 | * Like standards, there are a lot of atomic ops to choose from! | |
55 | */ | |
56 | ||
57 | #if defined(__i386__) || defined(__x86_64__) | |
58 | /* Implemented in assembly for i386 and x86_64 */ | |
59 | #else | |
60 | #error Unsupported arch | |
61 | #endif | |
62 | ||
63 | #undef OSIncrementAtomic | |
64 | SInt32 OSIncrementAtomic(volatile SInt32 * value) | |
65 | { | |
66 | return OSAddAtomic(1, value); | |
67 | } | |
68 | ||
69 | #undef OSDecrementAtomic | |
70 | SInt32 OSDecrementAtomic(volatile SInt32 * value) | |
71 | { | |
72 | return OSAddAtomic(-1, value); | |
73 | } | |
74 | ||
75 | static UInt32 OSBitwiseAtomic(UInt32 and_mask, UInt32 or_mask, UInt32 xor_mask, volatile UInt32 * value) | |
76 | { | |
77 | UInt32 oldValue; | |
78 | UInt32 newValue; | |
79 | ||
80 | do { | |
81 | oldValue = *value; | |
82 | newValue = ((oldValue & and_mask) | or_mask) ^ xor_mask; | |
83 | } while (! OSCompareAndSwap(oldValue, newValue, value)); | |
84 | ||
85 | return oldValue; | |
86 | } | |
87 | ||
88 | #undef OSBitAndAtomic | |
89 | UInt32 OSBitAndAtomic(UInt32 mask, volatile UInt32 * value) | |
90 | { | |
91 | return OSBitwiseAtomic(mask, 0, 0, value); | |
92 | } | |
93 | ||
94 | #undef OSBitOrAtomic | |
95 | UInt32 OSBitOrAtomic(UInt32 mask, volatile UInt32 * value) | |
96 | { | |
97 | return OSBitwiseAtomic((UInt32) -1, mask, 0, value); | |
98 | } | |
99 | ||
100 | #undef OSBitXorAtomic | |
101 | UInt32 OSBitXorAtomic(UInt32 mask, volatile UInt32 * value) | |
102 | { | |
103 | return OSBitwiseAtomic((UInt32) -1, 0, mask, value); | |
104 | } | |
105 | ||
106 | static Boolean OSCompareAndSwap8(UInt8 oldValue8, UInt8 newValue8, volatile UInt8 * value8) | |
107 | { | |
108 | UInt32 mask = 0x000000ff; | |
109 | UInt32 alignment = (UInt32)((unsigned long) value8) & (sizeof(UInt32) - 1); | |
110 | UInt32 shiftValues = (24 << 24) | (16 << 16) | (8 << 8); | |
111 | int shift = (UInt32) *(((UInt8 *) &shiftValues) + alignment); | |
112 | volatile UInt32 * value32 = (volatile UInt32 *) ((uintptr_t)value8 - alignment); | |
113 | UInt32 oldValue; | |
114 | UInt32 newValue; | |
115 | ||
116 | mask <<= shift; | |
117 | ||
118 | oldValue = *value32; | |
119 | oldValue = (oldValue & ~mask) | (oldValue8 << shift); | |
120 | newValue = (oldValue & ~mask) | (newValue8 << shift); | |
121 | ||
122 | return OSCompareAndSwap(oldValue, newValue, value32); | |
123 | } | |
124 | ||
125 | static Boolean OSTestAndSetClear(UInt32 bit, Boolean wantSet, volatile UInt8 * startAddress) | |
126 | { | |
127 | UInt8 mask = 1; | |
128 | UInt8 oldValue; | |
129 | UInt8 wantValue; | |
130 | ||
131 | startAddress += (bit / 8); | |
132 | mask <<= (7 - (bit % 8)); | |
133 | wantValue = wantSet ? mask : 0; | |
134 | ||
135 | do { | |
136 | oldValue = *startAddress; | |
137 | if ((oldValue & mask) == wantValue) { | |
138 | break; | |
139 | } | |
140 | } while (! OSCompareAndSwap8(oldValue, (oldValue & ~mask) | wantValue, startAddress)); | |
141 | ||
142 | return (oldValue & mask) == wantValue; | |
143 | } | |
144 | ||
145 | Boolean OSTestAndSet(UInt32 bit, volatile UInt8 * startAddress) | |
146 | { | |
147 | return OSTestAndSetClear(bit, true, startAddress); | |
148 | } | |
149 | ||
150 | Boolean OSTestAndClear(UInt32 bit, volatile UInt8 * startAddress) | |
151 | { | |
152 | return OSTestAndSetClear(bit, false, startAddress); | |
153 | } | |
154 | ||
155 | /* | |
156 | * silly unaligned versions | |
157 | */ | |
158 | ||
159 | SInt8 OSIncrementAtomic8(volatile SInt8 * value) | |
160 | { | |
161 | return OSAddAtomic8(1, value); | |
162 | } | |
163 | ||
164 | SInt8 OSDecrementAtomic8(volatile SInt8 * value) | |
165 | { | |
166 | return OSAddAtomic8(-1, value); | |
167 | } | |
168 | ||
169 | SInt8 OSAddAtomic8(SInt32 amount, volatile SInt8 * value) | |
170 | { | |
171 | SInt8 oldValue; | |
172 | SInt8 newValue; | |
173 | ||
174 | do { | |
175 | oldValue = *value; | |
176 | newValue = oldValue + amount; | |
177 | } while (! OSCompareAndSwap8((UInt8) oldValue, (UInt8) newValue, (volatile UInt8 *) value)); | |
178 | ||
179 | return oldValue; | |
180 | } | |
181 | ||
182 | static UInt8 OSBitwiseAtomic8(UInt32 and_mask, UInt32 or_mask, UInt32 xor_mask, volatile UInt8 * value) | |
183 | { | |
184 | UInt8 oldValue; | |
185 | UInt8 newValue; | |
186 | ||
187 | do { | |
188 | oldValue = *value; | |
189 | newValue = ((oldValue & and_mask) | or_mask) ^ xor_mask; | |
190 | } while (! OSCompareAndSwap8(oldValue, newValue, value)); | |
191 | ||
192 | return oldValue; | |
193 | } | |
194 | ||
195 | UInt8 OSBitAndAtomic8(UInt32 mask, volatile UInt8 * value) | |
196 | { | |
197 | return OSBitwiseAtomic8(mask, 0, 0, value); | |
198 | } | |
199 | ||
200 | UInt8 OSBitOrAtomic8(UInt32 mask, volatile UInt8 * value) | |
201 | { | |
202 | return OSBitwiseAtomic8((UInt32) -1, mask, 0, value); | |
203 | } | |
204 | ||
205 | UInt8 OSBitXorAtomic8(UInt32 mask, volatile UInt8 * value) | |
206 | { | |
207 | return OSBitwiseAtomic8((UInt32) -1, 0, mask, value); | |
208 | } | |
209 | ||
210 | static Boolean OSCompareAndSwap16(UInt16 oldValue16, UInt16 newValue16, volatile UInt16 * value16) | |
211 | { | |
212 | UInt32 mask = 0x0000ffff; | |
213 | UInt32 alignment = (UInt32)((unsigned long) value16) & (sizeof(UInt32) - 1); | |
214 | UInt32 shiftValues = (16 << 24) | (16 << 16); | |
215 | UInt32 shift = (UInt32) *(((UInt8 *) &shiftValues) + alignment); | |
216 | volatile UInt32 * value32 = (volatile UInt32 *) (((unsigned long) value16) - alignment); | |
217 | UInt32 oldValue; | |
218 | UInt32 newValue; | |
219 | ||
220 | mask <<= shift; | |
221 | ||
222 | oldValue = *value32; | |
223 | oldValue = (oldValue & ~mask) | (oldValue16 << shift); | |
224 | newValue = (oldValue & ~mask) | (newValue16 << shift); | |
225 | ||
226 | return OSCompareAndSwap(oldValue, newValue, value32); | |
227 | } | |
228 | ||
229 | SInt16 OSIncrementAtomic16(volatile SInt16 * value) | |
230 | { | |
231 | return OSAddAtomic16(1, value); | |
232 | } | |
233 | ||
234 | SInt16 OSDecrementAtomic16(volatile SInt16 * value) | |
235 | { | |
236 | return OSAddAtomic16(-1, value); | |
237 | } | |
238 | ||
239 | SInt16 OSAddAtomic16(SInt32 amount, volatile SInt16 * value) | |
240 | { | |
241 | SInt16 oldValue; | |
242 | SInt16 newValue; | |
243 | ||
244 | do { | |
245 | oldValue = *value; | |
246 | newValue = oldValue + amount; | |
247 | } while (! OSCompareAndSwap16((UInt16) oldValue, (UInt16) newValue, (volatile UInt16 *) value)); | |
248 | ||
249 | return oldValue; | |
250 | } | |
251 | ||
252 | static UInt16 OSBitwiseAtomic16(UInt32 and_mask, UInt32 or_mask, UInt32 xor_mask, volatile UInt16 * value) | |
253 | { | |
254 | UInt16 oldValue; | |
255 | UInt16 newValue; | |
256 | ||
257 | do { | |
258 | oldValue = *value; | |
259 | newValue = ((oldValue & and_mask) | or_mask) ^ xor_mask; | |
260 | } while (! OSCompareAndSwap16(oldValue, newValue, value)); | |
261 | ||
262 | return oldValue; | |
263 | } | |
264 | ||
265 | UInt16 OSBitAndAtomic16(UInt32 mask, volatile UInt16 * value) | |
266 | { | |
267 | return OSBitwiseAtomic16(mask, 0, 0, value); | |
268 | } | |
269 | ||
270 | UInt16 OSBitOrAtomic16(UInt32 mask, volatile UInt16 * value) | |
271 | { | |
272 | return OSBitwiseAtomic16((UInt32) -1, mask, 0, value); | |
273 | } | |
274 | ||
275 | UInt16 OSBitXorAtomic16(UInt32 mask, volatile UInt16 * value) | |
276 | { | |
277 | return OSBitwiseAtomic16((UInt32) -1, 0, mask, value); | |
278 | } | |
279 |