1 krisbash 1.1 /*============================================================================
2 * Copyright (C) Microsoft Corporation, All rights reserved.
3 *============================================================================
4 */
5
6 #include <malloc.h>
7 #include <MI.h>
8 #include "miapi_common.h"
9 #include "SafeHandle.h"
10 #include <pal/atomic.h>
11 #include <pal/format.h>
12 #include <base/log.h>
13
14
15
16
17 /* This should never be needed anywhere else but in this file which
18 * is why it is not in the header.
19 */
20 void ThunkHandle_Initialize(_Inout_ ThunkHandle *thunkHandle, _Inout_ ThunkHandleManager *manager, MI_Boolean firstTime);
21
22 krisbash 1.1
23 /* PUBLIC: Initialize
24 * Initialize the free-list. Just sets up the lockless list.
25 */
26 void ThunkHandleManager_Initialize(_Inout_ ThunkHandleManager *manager)
27 {
28 SList_Init(&manager->freeList);
29 }
30
31 /* PUBLIC: DeInitialize
32 * Delete all handles from the free list.
33 * Word of warning:
34 > This should not be done unless all handles are really finished with.
35 > Otherwise someone will just end up adding something else and that
36 > is likely to be leaked.
37 */
38 void ThunkHandleManager_DeInitialize(_Inout_ ThunkHandleManager *manager)
39 {
40 SListEntry *item = NULL;
41 do
42 {
43 krisbash 1.1 item = SList_PopAtomic(&manager->freeList);
44 if (item)
45 {
46 _aligned_free(item);
47 }
48 } while (item != NULL);
49 }
50
51 /* PRIVATE: RecycleHandle except it only called from RealHandle.
52 * RecycleHandle is not called by anything but the RealHandle itself.
53 * Not following this rule is likely to cause problems. The refcount
54 * on the object must get to zero before it is safe to put it back
55 * in the free-list.
56 */
57 void ThunkHandleManager_RecycleHandle(_Inout_ ThunkHandle *thunkHandle)
58 {
59 /* Invlidate this handle by bumping the thunk handle version */
60 Atomic_Inc(&thunkHandle->version);
61
62 /* Add thunk handle to free list */
63 SList_PushAtomic(&thunkHandle->handleManager->freeList, &thunkHandle->u.freeListData);
64 krisbash 1.1 }
65
66 /* PUBLIC: GetHandle
67 * Application that needs a new handle will case this. It will get one
68 * from the free-list if one is available, otherwise it allocates and
69 * initializes it.
70 * There is a bootstrapping problem for the Application that means we
71 * may need to allocate a handle even through there is no free-list.
72 * RETURN: 0 = failed, 1 = succeeded
73 */
74 MI_Result ThunkHandleManager_GetHandle(_Inout_ ThunkHandleManager *manager, _Outptr_ ThunkHandle **thunkHandle)
75 {
76 /* Get handle from list if there is any there */
77 *thunkHandle = (ThunkHandle*) SList_PopAtomic(&manager->freeList);
78 if (*thunkHandle == NULL)
79 {
80 /* Not there, allocate a new one using alligned memory */
81 *thunkHandle = (ThunkHandle *) _aligned_malloc(sizeof(ThunkHandle), MEMORY_ALLOCATION_ALIGNMENT);
82 if (*thunkHandle)
83 {
84 ThunkHandle_Initialize(*thunkHandle, manager, MI_TRUE);
85 krisbash 1.1 }
86 else
87 {
88 return MI_RESULT_SERVER_LIMITS_EXCEEDED;
89 }
90 }
91 else
92 {
93 ThunkHandle_Initialize(*thunkHandle, manager, MI_FALSE);
94 }
95
96 return MI_RESULT_OK;
97 }
98
99 /* Defines for the RealHandle for determining if it has
100 * been shutdown or not.
101 */
102 #define ActiveBit 0x80000000
103 #define CountMask 0x7fffffff
104
105 /* PRIVATE: RealHandle_Initialize, except should only ever be called from free-list
106 krisbash 1.1 * Initialize the RealHandle structure
107 */
108 void ThunkHandle_Initialize(_Inout_ ThunkHandle *thunkHandle, _Inout_ ThunkHandleManager *manager, MI_Boolean firstTime)
109 {
110 ptrdiff_t version = 1;
111 if (MI_FALSE == firstTime)
112 {
113 version = thunkHandle->version;
114 }
115
116 memset(thunkHandle, 0, sizeof(ThunkHandle));
117 thunkHandle->version = version;
118 thunkHandle->refcount = (ptrdiff_t)(ActiveBit + 1);
119 thunkHandle->handleManager = manager;
120 }
121
122 /* PUBLIC: RealHandle_AddRef
123 * Bump the refcount on the object. This can fail if shutdown
124 * has already been called. Return code MUST ALWAYS be called.
125 */
126 _Check_return_ int ThunkHandle_AddRef(_Inout_ ThunkHandle *thunkHandle)
127 krisbash 1.1 {
128 ptrdiff_t n;
129 for (n = thunkHandle->refcount; n & ActiveBit; n = thunkHandle->refcount)
130 {
131 if (Atomic_CompareAndSwap(&thunkHandle->refcount, n, n + 1) == n)
132 {
133 /*We incremented the count and we weren't cancelled so return success.*/
134 return 1;
135 }
136 }
137 /*Another thread called Shutdown() so return false.*/
138 return 0;
139 }
140
141 _Check_return_ int ThunkHandle_AddRef_ForCompletionCallback(_Inout_ ThunkHandle *thunkHandle)
142 {
143 ptrdiff_t n;
144 /* Checking for atleast one reference before increasing the refcount, irrespective of acitve bit set or not */
145 for (n = thunkHandle->refcount; n & (~ActiveBit); n = thunkHandle->refcount)
146 {
147 if (Atomic_CompareAndSwap(&thunkHandle->refcount, n, n + 1) == n)
148 krisbash 1.1 {
149 /*We incremented the count and we weren't cancelled so return success.*/
150 return 1;
151 }
152 }
153 /*Another thread called Shutdown() so return false.*/
154 trace_MIThunkAfterShutdown(thunkHandle);
155 return 0;
156 }
157
158 /* PUBLIC: RealHandle_Release
159 * Atomically releases a reference.
160 * Once all AddRef() calls have been released and Shutdown() is called,
161 * the last thread out recycles the handle
162 */
163 long ThunkHandle_Release(_Inout_ ThunkHandle *thunkHandle)
164 {
165 ptrdiff_t n;
166
167 //NitsAssert(thunkHandle->refcount, L"invalid refcount");
168
169 krisbash 1.1 n = Atomic_Dec(&thunkHandle->refcount);
170
171 if (n == 0)
172 {
173 /* Copy so can recycle before calling destructor */
174 ThunkHandle tmpHandle = *thunkHandle;
175
176 /* Object is now free to add back to pool and bump version number */
177 ThunkHandleManager_RecycleHandle(thunkHandle);
178
179 /* Call the destructor to tear down the thunked object */
180 if (tmpHandle.destructor)
181 {
182 tmpHandle.destructor(&tmpHandle);
183 }
184
185 }
186
187 return n & CountMask;
188 }
189
190 krisbash 1.1 /* PUBLIC: RealHandle_Shutdown
191 * Atomically sets the object to the cancelled state.
192 * The first thread releases the reference from the
193 * constructor.
194 */
195 int ThunkHandle_Shutdown(
196 _Inout_ ThunkHandle *handle,
197 _In_opt_ void (*destructor)(_In_ ThunkHandle *thunkHandle))
198 {
199 ptrdiff_t n;
200
201 trace_MIShuttingDownThunkHandle(handle);
202
203 for (n = handle->refcount; n & ActiveBit; n = handle->refcount)
204 {
205 if (Atomic_CompareAndSwap(&handle->refcount, n, n & CountMask) == n)
206 {
207 /* We cleared ActiveBit. This code runs only once. */
208 handle->destructor = destructor;
209 ThunkHandle_Release(handle);
210 return 1;
211 krisbash 1.1 }
212 }
213
214 /*Another thread cleared ActiveBit.*/
215 return 0;
216 }
217
218
219 void ThunkHandle_FromGeneric(_Inout_ GenericHandle *genericHandle, _Outptr_result_maybenull_ ThunkHandle **thunkHandle)
220 {
221 *thunkHandle = NULL;
222
223 if (genericHandle->thunkHandle == NULL)
224 {
225 /* invalid handle */
226 trace_MIThunk_InvalidHandle(genericHandle);
227 return;
228 }
229 if (genericHandle->version != genericHandle->thunkHandle->version)
230 {
231 /* invalid handle version */
232 krisbash 1.1 trace_MIThunk_OldVersion(genericHandle);
233 return;
234 }
235
236 if (!ThunkHandle_AddRef(genericHandle->thunkHandle))
237 {
238 /* Handle is shutdown */
239 trace_MIThunk_AlreadyShutdown(genericHandle);
240 return;
241 }
242
243 if (genericHandle->version != genericHandle->thunkHandle->version)
244 {
245 /* Need another version check */
246 trace_MIThunk_OldVersion(genericHandle);
247 return;
248 }
249
250 *thunkHandle = genericHandle->thunkHandle;
251 }
252
253 krisbash 1.1 void ThunkHandle_FromGeneric_ForCompletionCallback(_Inout_ GenericHandle *genericHandle, _Outptr_result_maybenull_ ThunkHandle **thunkHandle)
254 {
255 *thunkHandle = NULL;
256
257 if (genericHandle->thunkHandle == NULL)
258 {
259 /* invalid handle */
260 trace_MIThunk_InvalidHandle(genericHandle);
261 return;
262 }
263 if (genericHandle->version != genericHandle->thunkHandle->version)
264 {
265 /* invalid handle version */
266 trace_MIThunk_OldVersion(genericHandle);
267 return;
268 }
269
270 if (!ThunkHandle_AddRef_ForCompletionCallback(genericHandle->thunkHandle))
271 {
272 /* Handle is shutdown */
273 trace_MIThunk_AlreadyShutdown(genericHandle);
274 krisbash 1.1 return;
275 }
276
277 if (genericHandle->version != genericHandle->thunkHandle->version)
278 {
279 /* Need another version check */
280 trace_MIThunk_OldVersion(genericHandle);
281 return;
282 }
283
284 *thunkHandle = genericHandle->thunkHandle;
285 }
286
|