@@ -295,4 +295,123 @@ TEST(HandleAllocatorTest, HandleExhaustion)
295295 EXPECT_FALSE (allocator.anyHandleAvailableForAllocation ());
296296}
297297
298+ // Verifies that released handles are reused in FIFO order.
299+ TEST (HandleAllocatorTest, ReleaseThenReuseInFIFO)
300+ {
301+ gl::HandleAllocator allocator (1000 );
302+
303+ // Allocate handles 1-11. The next unallocated index is 12.
304+ GLuint handles[11 ];
305+ for (int i = 0 ; i < 11 ; i++)
306+ {
307+ EXPECT_TRUE (allocator.allocate (&handles[i]));
308+ }
309+
310+ // Release handles in a non-sorted order. None is adjacent to 12, so all go into mReleasedList
311+ // rather than being consolidated into mUnallocatedList.
312+ constexpr GLuint kReleaseOrder [] = {3 , 1 , 5 , 2 , 4 , 8 , 6 , 10 , 7 , 9 };
313+ for (GLuint i : kReleaseOrder )
314+ {
315+ allocator.release (i);
316+ }
317+
318+ // Each allocation should return the next handle in the same order they were released.
319+ for (GLuint expected : kReleaseOrder )
320+ {
321+ GLuint handle = 0 ;
322+ EXPECT_TRUE (allocator.allocate (&handle));
323+ EXPECT_EQ (expected, handle);
324+ }
325+ }
326+
327+ // Verifies that reserve() correctly removes handles from the released list even when the released
328+ // list has many entries.
329+ TEST (HandleAllocatorTest, ReserveFromLargeReleasedList)
330+ {
331+ gl::HandleAllocator allocator (1000 );
332+
333+ // Need to allocate 101 to push the next unallocated index to 102, so it is no longer adjacent
334+ // to 1-100.
335+ GLuint prevHandle;
336+ for (GLuint i = 1 ; i <= 101 ; i++)
337+ {
338+ EXPECT_TRUE (allocator.allocate (&prevHandle));
339+ }
340+
341+ // Release 1-100. All should go to mReleasedList.
342+ for (GLuint i = 1 ; i <= 100 ; i++)
343+ {
344+ allocator.release (i);
345+ }
346+
347+ // Reserve some specific handles.
348+ allocator.reserve (50 );
349+ allocator.reserve (75 );
350+ allocator.reserve (25 );
351+
352+ // Verify those reserved handles are not allocated.
353+ GLuint handle;
354+ for (GLuint i = 1 ; i <= 100 ; i++)
355+ {
356+ if (allocator.allocate (&handle))
357+ {
358+ EXPECT_NE (25u , handle);
359+ EXPECT_NE (50u , handle);
360+ EXPECT_NE (75u , handle);
361+ }
362+ }
363+ }
364+
365+ // Verifies that interleaved release and allocate operations work correctly.
366+ // Simulates handle usage patterns similar to real-world resource management.
367+ TEST (HandleAllocatorTest, InterleavedReleaseAllocate)
368+ {
369+ gl::HandleAllocator allocator (kMaxHandleForTesting );
370+ constexpr GLuint kPoolSize = 16 ;
371+
372+ // Allocate more handles than the pool to prevent consolidation.
373+ std::vector<GLuint> pool;
374+ for (GLuint i = 0 ; i < kPoolSize ; i++)
375+ {
376+ GLuint handle;
377+ EXPECT_TRUE (allocator.allocate (&handle));
378+ pool.push_back (handle);
379+ }
380+
381+ // Keep the next unallocated handle advanced.
382+ GLuint gapHandle;
383+ EXPECT_TRUE (allocator.allocate (&gapHandle));
384+
385+ // Release half of the pool.
386+ std::vector<GLuint> releasedHandles;
387+ for (GLuint i = 0 ; i < kPoolSize / 2 ; i++)
388+ {
389+ allocator.release (pool[i]);
390+ releasedHandles.push_back (pool[i]);
391+ }
392+
393+ // Release more handles.
394+ std::vector<GLuint> releasedHandles2;
395+ for (GLuint i = kPoolSize / 2 ; i < kPoolSize ; i++)
396+ {
397+ allocator.release (pool[i]);
398+ releasedHandles2.push_back (pool[i]);
399+ }
400+
401+ // Allocate them back. It should return in FIFO order.
402+ for (GLuint handle : releasedHandles)
403+ {
404+ GLuint newHandle;
405+ EXPECT_TRUE (allocator.allocate (&newHandle));
406+ EXPECT_EQ (handle, newHandle);
407+ }
408+
409+ for (GLuint handle : releasedHandles2)
410+ {
411+ GLuint newHandle;
412+ EXPECT_TRUE (allocator.allocate (&newHandle));
413+ EXPECT_EQ (handle, newHandle);
414+ }
415+ }
416+
298417} // anonymous namespace
0 commit comments