@@ -312,52 +312,100 @@ namespace
312
312
GC_TRIGGERS;
313
313
MODE_COOPERATIVE;
314
314
PRECONDITION (!IsLockHeld ());
315
+ PRECONDITION (!(withFlags & ExternalObjectContext::Flags_Collected));
316
+ PRECONDITION (!(withFlags & ExternalObjectContext::Flags_Detached));
315
317
POSTCONDITION (RETVAL != NULL );
316
318
}
317
319
CONTRACT_END;
318
320
319
321
struct
320
322
{
323
+ PTRARRAYREF arrRefTmp;
321
324
PTRARRAYREF arrRef;
322
325
} gc;
323
326
::ZeroMemory (&gc, sizeof (gc));
324
327
GCPROTECT_BEGIN (gc);
325
328
329
+ // Only add objects that are in the correct thread
330
+ // context, haven't been detached from the cache, and have
331
+ // the appropriate flags set.
332
+ // Define a macro predicate since it used in multiple places.
333
+ // If an instance is in the hashmap, it is active. This invariant
334
+ // holds because the GC is what marks and removes from the cache.
335
+ #define SELECT_OBJECT (XX ) XX->ThreadContext == threadContext \
336
+ && !XX->IsSet (ExternalObjectContext::Flags_Detached) \
337
+ && (withFlags == ExternalObjectContext::Flags_None || XX->IsSet (withFlags))
338
+
339
+ // Determine the count of objects to return.
340
+ SIZE_T objCountMax = 0 ;
341
+ {
342
+ LockHolder lock (this );
343
+ Iterator end = _hashMap.End ();
344
+ for (Iterator curr = _hashMap.Begin (); curr != end; ++curr)
345
+ {
346
+ ExternalObjectContext* inst = *curr;
347
+ if (SELECT_OBJECT (inst))
348
+ {
349
+ objCountMax++;
350
+ }
351
+ }
352
+ }
353
+
354
+ // Allocate enumerable type to return.
355
+ gc.arrRef = (PTRARRAYREF)AllocateObjectArray ((DWORD)objCountMax, g_pObjectClass);
356
+
326
357
CQuickArrayList<ExternalObjectContext*> localList;
327
358
328
- // Determine objects to return
359
+ // Iterate over the hashmap again while populating the above array
360
+ // using the same predicate as before and holding onto context instances.
361
+ SIZE_T objCount = 0 ;
362
+ if (0 < objCountMax)
329
363
{
330
364
LockHolder lock (this );
331
365
Iterator end = _hashMap.End ();
332
366
for (Iterator curr = _hashMap.Begin (); curr != end; ++curr)
333
367
{
334
368
ExternalObjectContext* inst = *curr;
335
-
336
- // Only add objects that are in the correct thread
337
- // context and have the appropriate flags set.
338
- if (inst->ThreadContext == threadContext
339
- && (withFlags == ExternalObjectContext::Flags_None || inst->IsSet (withFlags)))
369
+ if (SELECT_OBJECT (inst))
340
370
{
341
371
localList.Push (inst);
342
- STRESS_LOG1 (LF_INTEROP, LL_INFO100, " Add EOC to Enumerable: 0x%p\n " , inst);
372
+
373
+ gc.arrRef ->SetAt (objCount, inst->GetObjectRef ());
374
+ objCount++;
375
+
376
+ STRESS_LOG1 (LF_INTEROP, LL_INFO1000, " Add EOC to Enumerable: 0x%p\n " , inst);
343
377
}
378
+
379
+ // There is a chance more objects were added to the hash while the
380
+ // lock was released during array allocation. Once we hit the computed max
381
+ // we stop to avoid looking longer than needed.
382
+ if (objCount == objCountMax)
383
+ break ;
344
384
}
345
385
}
346
386
347
- // Allocate enumerable type to return.
348
- gc.arrRef = (PTRARRAYREF)AllocateObjectArray ((DWORD)localList.Size (), g_pObjectClass);
349
-
350
- // Insert objects into enumerable.
351
- // The ExternalObjectContexts in the hashmap are only
352
- // removed and associated objects collected during a GC. Since
353
- // this code is running in Cooperative mode they will never
354
- // be null.
355
- for (SIZE_T i = 0 ; i < localList.Size (); i++)
387
+ #undef SELECT_OBJECT
388
+
389
+ // During the allocation of the array to return, a GC could have
390
+ // occurred and objects detached from this cache. In order to avoid
391
+ // having null array elements we will allocate a new array.
392
+ // This subsequent allocation is okay because the array we are
393
+ // replacing extends all object lifetimes.
394
+ if (objCount < objCountMax)
356
395
{
357
- ExternalObjectContext* inst = localList[i];
358
- gc.arrRef ->SetAt (i, inst->GetObjectRef ());
396
+ gc.arrRefTmp = (PTRARRAYREF)AllocateObjectArray ((DWORD)objCount, g_pObjectClass);
397
+
398
+ void * dest = gc.arrRefTmp ->GetDataPtr ();
399
+ void * src = gc.arrRef ->GetDataPtr ();
400
+ SIZE_T elementSize = gc.arrRef ->GetComponentSize ();
401
+
402
+ memmoveGCRefs (dest, src, objCount * elementSize);
403
+ gc.arrRef = gc.arrRefTmp ;
359
404
}
360
405
406
+ // All objects are now referenced from the array so won't be collected
407
+ // at this point. This means we can safely iterate over the ExternalObjectContext
408
+ // instances.
361
409
{
362
410
// Separate the wrapper from the tracker runtime prior to
363
411
// passing them onto the caller. This call is okay to make
0 commit comments