VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/handletablectx.cpp@ 17951

Last change on this file since 17951 was 10790, checked in by vboxsync, 16 years ago

IPRT: Fixed an overlooked race found by the threaded testcase.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.5 KB
Line 
1/* $Id: handletablectx.cpp 10790 2008-07-21 18:43:39Z vboxsync $ */
2/** @file
3 * IPRT - Handle Tables.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include <iprt/handletable.h>
35#include <iprt/mem.h>
36#include <iprt/spinlock.h>
37#include <iprt/err.h>
38#include <iprt/assert.h>
39#include <iprt/param.h>
40#include <iprt/string.h>
41#include <iprt/asm.h>
42#include "internal/magics.h"
43#include "handletable.h"
44
45
46RTDECL(int) RTHandleTableAllocWithCtx(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, uint32_t *ph)
47{
48 /* validate the input */
49 PRTHANDLETABLEINT pThis = (PRTHANDLETABLEINT)hHandleTable;
50 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
51 AssertReturn(pThis->u32Magic == RTHANDLETABLE_MAGIC, VERR_INVALID_HANDLE);
52 AssertReturn(pThis->fFlags & RTHANDLETABLE_FLAGS_CONTEXT, VERR_INVALID_FUNCTION);
53 AssertReturn(!RTHT_IS_FREE(pvObj), VERR_INVALID_PARAMETER);
54 AssertPtrReturn(ph, VERR_INVALID_POINTER);
55 *ph = pThis->uBase - 1;
56
57 /*
58 * Allocation loop.
59 */
60 RTSPINLOCKTMP Tmp;
61 rtHandleTableLock(pThis, &Tmp);
62
63 int rc;
64 do
65 {
66 /*
67 * Try grab a free entry from the head of the free list.
68 */
69 uint32_t i = pThis->iFreeHead;
70 if (i != NIL_RTHT_INDEX)
71 {
72 PRTHTENTRYFREE pFree = (PRTHTENTRYFREE)rtHandleTableLookupWithCtxIdx(pThis, i);
73 Assert(pFree);
74 if (i == pThis->iFreeTail)
75 pThis->iFreeTail = pThis->iFreeHead = NIL_RTHT_INDEX;
76 else
77 pThis->iFreeHead = RTHT_GET_FREE_IDX(pFree);
78 pThis->cCurAllocated++;
79 Assert(pThis->cCurAllocated <= pThis->cCur);
80
81 /*
82 * Setup the entry and return.
83 */
84 PRTHTENTRYCTX pEntry = (PRTHTENTRYCTX)pFree;
85 pEntry->pvObj = pvObj;
86 pEntry->pvCtx = pvCtx;
87 *ph = i + pThis->uBase;
88 rc = VINF_SUCCESS;
89 }
90 /*
91 * Must expand the handle table, unless it's full.
92 */
93 else if (pThis->cCur >= pThis->cMax)
94 {
95 rc = VERR_NO_MORE_HANDLES;
96 Assert(pThis->cCur == pThis->cCurAllocated);
97 }
98 else
99 {
100 /*
101 * Do we have to expand the 1st level table too?
102 */
103 uint32_t const iLevel1 = pThis->cCur / RTHT_LEVEL2_ENTRIES;
104 uint32_t cLevel1 = iLevel1 >= pThis->cLevel1
105 ? pThis->cLevel1 + PAGE_SIZE / sizeof(void *)
106 : 0;
107 if (cLevel1 > pThis->cMax / RTHT_LEVEL2_ENTRIES)
108 cLevel1 = pThis->cMax / RTHT_LEVEL2_ENTRIES;
109 Assert(!cLevel1 || pThis->cMax / RTHT_LEVEL2_ENTRIES >= RTHT_LEVEL1_DYN_ALLOC_THRESHOLD);
110
111 /* leave the lock (never do fancy stuff from behind a spinlock). */
112 rtHandleTableUnlock(pThis, &Tmp);
113
114 /*
115 * Do the allocation(s).
116 */
117 rc = VERR_TRY_AGAIN;
118 void **papvLevel1 = NULL;
119 if (cLevel1)
120 {
121 papvLevel1 = (void **)RTMemAlloc(sizeof(void *) * cLevel1);
122 if (!papvLevel1)
123 return VERR_NO_MEMORY;
124 }
125
126 PRTHTENTRYCTX paTable = (PRTHTENTRYCTX)RTMemAlloc(sizeof(*paTable) * RTHT_LEVEL2_ENTRIES);
127 if (!paTable)
128 {
129 RTMemFree(papvLevel1);
130 return VERR_NO_MEMORY;
131 }
132
133 /* re-enter the lock. */
134 rtHandleTableLock(pThis, &Tmp);
135
136 /*
137 * Insert the new bits, but be a bit careful as someone might have
138 * raced us expanding the table.
139 */
140 /* deal with the 1st level lookup expansion first */
141 if (cLevel1)
142 {
143 Assert(papvLevel1);
144 if (cLevel1 > pThis->cLevel1)
145 {
146 /* Replace the 1st level table. */
147 memcpy(papvLevel1, pThis->papvLevel1, sizeof(void *) * pThis->cLevel1);
148 memset(&papvLevel1[pThis->cLevel1], 0, sizeof(void *) * (cLevel1 - pThis->cLevel1));
149 pThis->cLevel1 = cLevel1;
150 void **papvTmp = pThis->papvLevel1;
151 pThis->papvLevel1 = papvLevel1;
152 papvLevel1 = papvTmp;
153 }
154
155 /* free the obsolete one (outside the lock of course) */
156 rtHandleTableUnlock(pThis, &Tmp);
157 RTMemFree(papvLevel1);
158 rtHandleTableLock(pThis, &Tmp);
159 }
160
161 /* insert the table we allocated. */
162 uint32_t iLevel1New = pThis->cCur / RTHT_LEVEL2_ENTRIES;
163 if ( iLevel1New < pThis->cLevel1
164 && pThis->cCur < pThis->cMax)
165 {
166 pThis->papvLevel1[iLevel1New] = paTable;
167
168 /* link all entries into a free list. */
169 Assert(!(pThis->cCur % RTHT_LEVEL2_ENTRIES));
170 for (uint32_t i = 0; i < RTHT_LEVEL2_ENTRIES - 1; i++)
171 {
172 RTHT_SET_FREE_IDX((PRTHTENTRYFREE)&paTable[i], i + 1 + pThis->cCur);
173 paTable[i].pvCtx = (void *)~(uintptr_t)7;
174 }
175 RTHT_SET_FREE_IDX((PRTHTENTRYFREE)&paTable[RTHT_LEVEL2_ENTRIES - 1], NIL_RTHT_INDEX);
176 paTable[RTHT_LEVEL2_ENTRIES - 1].pvCtx = (void *)~(uintptr_t)7;
177
178 /* join the free list with the other. */
179 if (pThis->iFreeTail == NIL_RTHT_INDEX)
180 pThis->iFreeHead = pThis->cCur;
181 else
182 {
183 PRTHTENTRYFREE pPrev = (PRTHTENTRYFREE)rtHandleTableLookupWithCtxIdx(pThis, pThis->iFreeTail);
184 Assert(pPrev);
185 RTHT_SET_FREE_IDX(pPrev, pThis->cCur);
186 }
187 pThis->iFreeTail = pThis->cCur + RTHT_LEVEL2_ENTRIES - 1;
188
189 pThis->cCur += RTHT_LEVEL2_ENTRIES;
190 }
191 else
192 {
193 /* free the table (raced someone, and we lost). */
194 rtHandleTableUnlock(pThis, &Tmp);
195 RTMemFree(paTable);
196 rtHandleTableLock(pThis, &Tmp);
197 }
198
199 rc = VERR_TRY_AGAIN;
200 }
201 } while (rc == VERR_TRY_AGAIN);
202
203 rtHandleTableUnlock(pThis, &Tmp);
204
205 return rc;
206}
207
208
209RTDECL(void *) RTHandleTableLookupWithCtx(RTHANDLETABLE hHandleTable, uint32_t h, void *pvCtx)
210{
211 /* validate the input */
212 PRTHANDLETABLEINT pThis = (PRTHANDLETABLEINT)hHandleTable;
213 AssertPtrReturn(pThis, NULL);
214 AssertReturn(pThis->u32Magic == RTHANDLETABLE_MAGIC, NULL);
215 AssertReturn(pThis->fFlags & RTHANDLETABLE_FLAGS_CONTEXT, NULL);
216
217 void *pvObj = NULL;
218
219 /* acquire the lock */
220 RTSPINLOCKTMP Tmp;
221 rtHandleTableLock(pThis, &Tmp);
222
223 /*
224 * Perform the lookup and retaining.
225 */
226 PRTHTENTRYCTX pEntry = rtHandleTableLookupWithCtx(pThis, h);
227 if (pEntry && pEntry->pvCtx == pvCtx)
228 {
229 pvObj = pEntry->pvObj;
230 if (!RTHT_IS_FREE(pvObj))
231 {
232 if (pThis->pfnRetain)
233 {
234 int rc = pThis->pfnRetain(hHandleTable, pEntry->pvObj, pvCtx, pThis->pvRetainUser);
235 if (RT_FAILURE(rc))
236 pvObj = NULL;
237 }
238 }
239 else
240 pvObj = NULL;
241 }
242
243 /* release the lock */
244 rtHandleTableUnlock(pThis, &Tmp);
245 return pvObj;
246}
247
248
249RTDECL(void *) RTHandleTableFreeWithCtx(RTHANDLETABLE hHandleTable, uint32_t h, void *pvCtx)
250{
251 /* validate the input */
252 PRTHANDLETABLEINT pThis = (PRTHANDLETABLEINT)hHandleTable;
253 AssertPtrReturn(pThis, NULL);
254 AssertReturn(pThis->u32Magic == RTHANDLETABLE_MAGIC, NULL);
255 AssertReturn(pThis->fFlags & RTHANDLETABLE_FLAGS_CONTEXT, NULL);
256
257 void *pvObj = NULL;
258
259 /* acquire the lock */
260 RTSPINLOCKTMP Tmp;
261 rtHandleTableLock(pThis, &Tmp);
262
263 /*
264 * Perform the lookup and retaining.
265 */
266 PRTHTENTRYCTX pEntry = rtHandleTableLookupWithCtx(pThis, h);
267 if (pEntry && pEntry->pvCtx == pvCtx)
268 {
269 pvObj = pEntry->pvObj;
270 if (!RTHT_IS_FREE(pvObj))
271 {
272 if (pThis->pfnRetain)
273 {
274 int rc = pThis->pfnRetain(hHandleTable, pEntry->pvObj, pvCtx, pThis->pvRetainUser);
275 if (RT_FAILURE(rc))
276 pvObj = NULL;
277 }
278
279 /*
280 * Link it into the free list.
281 */
282 if (pvObj)
283 {
284 pEntry->pvCtx = (void *)~(uintptr_t)7;
285
286 PRTHTENTRYFREE pFree = (PRTHTENTRYFREE)pEntry;
287 RTHT_SET_FREE_IDX(pFree, NIL_RTHT_INDEX);
288
289 uint32_t const i = h - pThis->uBase;
290 if (pThis->iFreeTail == NIL_RTHT_INDEX)
291 pThis->iFreeHead = pThis->iFreeTail = i;
292 else
293 {
294 PRTHTENTRYFREE pPrev = (PRTHTENTRYFREE)rtHandleTableLookupWithCtxIdx(pThis, pThis->iFreeTail);
295 Assert(pPrev);
296 RTHT_SET_FREE_IDX(pPrev, i);
297 pThis->iFreeTail = i;
298 }
299
300 Assert(pThis->cCurAllocated > 0);
301 pThis->cCurAllocated--;
302 }
303 }
304 else
305 pvObj = NULL;
306 }
307
308 /* release the lock */
309 rtHandleTableUnlock(pThis, &Tmp);
310 return pvObj;
311}
312
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette