(file) Return to SafeHandle.c CVS log (file) (dir) Up to [OMI] / omi / miapi

  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              

ViewCVS 0.9.2