mirror of
				https://github.com/python/cpython.git
				synced 2025-10-26 11:14:33 +00:00 
			
		
		
		
	
		
			
	
	
		
			194 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			194 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | #include <stdbool.h>
 | ||
|  | 
 | ||
|  | #include "Python.h"
 | ||
|  | 
 | ||
|  | #include "pycore_index_pool.h"
 | ||
|  | #include "pycore_lock.h"
 | ||
|  | 
 | ||
|  | #ifdef Py_GIL_DISABLED
 | ||
|  | 
 | ||
|  | static inline void | ||
|  | swap(int32_t *values, Py_ssize_t i, Py_ssize_t j) | ||
|  | { | ||
|  |     int32_t tmp = values[i]; | ||
|  |     values[i] = values[j]; | ||
|  |     values[j] = tmp; | ||
|  | } | ||
|  | 
 | ||
|  | static bool | ||
|  | heap_try_swap(_PyIndexHeap *heap, Py_ssize_t i, Py_ssize_t j) | ||
|  | { | ||
|  |     if (i < 0 || i >= heap->size) { | ||
|  |         return 0; | ||
|  |     } | ||
|  |     if (j < 0 || j >= heap->size) { | ||
|  |         return 0; | ||
|  |     } | ||
|  |     if (i <= j) { | ||
|  |         if (heap->values[i] <= heap->values[j]) { | ||
|  |             return 0; | ||
|  |         } | ||
|  |     } | ||
|  |     else if (heap->values[j] <= heap->values[i]) { | ||
|  |         return 0; | ||
|  |     } | ||
|  |     swap(heap->values, i, j); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | static inline Py_ssize_t | ||
|  | parent(Py_ssize_t i) | ||
|  | { | ||
|  |     return (i - 1) / 2; | ||
|  | } | ||
|  | 
 | ||
|  | static inline Py_ssize_t | ||
|  | left_child(Py_ssize_t i) | ||
|  | { | ||
|  |     return 2 * i + 1; | ||
|  | } | ||
|  | 
 | ||
|  | static inline Py_ssize_t | ||
|  | right_child(Py_ssize_t i) | ||
|  | { | ||
|  |     return 2 * i + 2; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | heap_add(_PyIndexHeap *heap, int32_t val) | ||
|  | { | ||
|  |     assert(heap->size < heap->capacity); | ||
|  |     // Add val to end
 | ||
|  |     heap->values[heap->size] = val; | ||
|  |     heap->size++; | ||
|  |     // Sift up
 | ||
|  |     for (Py_ssize_t cur = heap->size - 1; cur > 0; cur = parent(cur)) { | ||
|  |         if (!heap_try_swap(heap, cur, parent(cur))) { | ||
|  |             break; | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static Py_ssize_t | ||
|  | heap_min_child(_PyIndexHeap *heap, Py_ssize_t i) | ||
|  | { | ||
|  |     if (left_child(i) < heap->size) { | ||
|  |         if (right_child(i) < heap->size) { | ||
|  |             Py_ssize_t lval = heap->values[left_child(i)]; | ||
|  |             Py_ssize_t rval = heap->values[right_child(i)]; | ||
|  |             return lval < rval ? left_child(i) : right_child(i); | ||
|  |         } | ||
|  |         return left_child(i); | ||
|  |     } | ||
|  |     else if (right_child(i) < heap->size) { | ||
|  |         return right_child(i); | ||
|  |     } | ||
|  |     return -1; | ||
|  | } | ||
|  | 
 | ||
|  | static int32_t | ||
|  | heap_pop(_PyIndexHeap *heap) | ||
|  | { | ||
|  |     assert(heap->size > 0); | ||
|  |     // Pop smallest and replace with the last element
 | ||
|  |     int32_t result = heap->values[0]; | ||
|  |     heap->values[0] = heap->values[heap->size - 1]; | ||
|  |     heap->size--; | ||
|  |     // Sift down
 | ||
|  |     for (Py_ssize_t cur = 0; cur < heap->size;) { | ||
|  |         Py_ssize_t min_child = heap_min_child(heap, cur); | ||
|  |         if (min_child > -1 && heap_try_swap(heap, cur, min_child)) { | ||
|  |             cur = min_child; | ||
|  |         } | ||
|  |         else { | ||
|  |             break; | ||
|  |         } | ||
|  |     } | ||
|  |     return result; | ||
|  | } | ||
|  | 
 | ||
|  | static int | ||
|  | heap_ensure_capacity(_PyIndexHeap *heap, Py_ssize_t limit) | ||
|  | { | ||
|  |     assert(limit > 0); | ||
|  |     if (heap->capacity > limit) { | ||
|  |         return 0; | ||
|  |     } | ||
|  |     Py_ssize_t new_capacity = heap->capacity ? heap->capacity : 1024; | ||
|  |     while (new_capacity && new_capacity < limit) { | ||
|  |         new_capacity <<= 1; | ||
|  |     } | ||
|  |     if (!new_capacity) { | ||
|  |         return -1; | ||
|  |     } | ||
|  |     int32_t *new_values = PyMem_RawCalloc(new_capacity, sizeof(int32_t)); | ||
|  |     if (new_values == NULL) { | ||
|  |         return -1; | ||
|  |     } | ||
|  |     if (heap->values != NULL) { | ||
|  |         memcpy(new_values, heap->values, heap->capacity); | ||
|  |         PyMem_RawFree(heap->values); | ||
|  |     } | ||
|  |     heap->values = new_values; | ||
|  |     heap->capacity = new_capacity; | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | heap_fini(_PyIndexHeap *heap) | ||
|  | { | ||
|  |     if (heap->values != NULL) { | ||
|  |         PyMem_RawFree(heap->values); | ||
|  |         heap->values = NULL; | ||
|  |     } | ||
|  |     heap->size = -1; | ||
|  |     heap->capacity = -1; | ||
|  | } | ||
|  | 
 | ||
|  | #define LOCK_POOL(pool) PyMutex_LockFlags(&pool->mutex, _Py_LOCK_DONT_DETACH)
 | ||
|  | #define UNLOCK_POOL(pool) PyMutex_Unlock(&pool->mutex)
 | ||
|  | 
 | ||
|  | int32_t | ||
|  | _PyIndexPool_AllocIndex(_PyIndexPool *pool) | ||
|  | { | ||
|  |     LOCK_POOL(pool); | ||
|  |     int32_t index; | ||
|  |     _PyIndexHeap *free_indices = &pool->free_indices; | ||
|  |     if (free_indices->size == 0) { | ||
|  |         // No free indices. Make sure the heap can always store all of the
 | ||
|  |         // indices that have been allocated to avoid having to allocate memory
 | ||
|  |         // (which can fail) when freeing an index. Freeing indices happens when
 | ||
|  |         // threads are being destroyed, which makes error handling awkward /
 | ||
|  |         // impossible. This arrangement shifts handling of allocation failures
 | ||
|  |         // to when indices are allocated, which happens at thread creation,
 | ||
|  |         // where we are better equipped to deal with failure.
 | ||
|  |         if (heap_ensure_capacity(free_indices, pool->next_index + 1) < 0) { | ||
|  |             UNLOCK_POOL(pool); | ||
|  |             PyErr_NoMemory(); | ||
|  |             return -1; | ||
|  |         } | ||
|  |         index = pool->next_index++; | ||
|  |     } | ||
|  |     else { | ||
|  |         index = heap_pop(free_indices); | ||
|  |     } | ||
|  |     UNLOCK_POOL(pool); | ||
|  |     return index; | ||
|  | } | ||
|  | 
 | ||
|  | void | ||
|  | _PyIndexPool_FreeIndex(_PyIndexPool *pool, int32_t index) | ||
|  | { | ||
|  |     LOCK_POOL(pool); | ||
|  |     heap_add(&pool->free_indices, index); | ||
|  |     UNLOCK_POOL(pool); | ||
|  | } | ||
|  | 
 | ||
|  | void | ||
|  | _PyIndexPool_Fini(_PyIndexPool *pool) | ||
|  | { | ||
|  |     heap_fini(&pool->free_indices); | ||
|  | } | ||
|  | 
 | ||
|  | #endif  // Py_GIL_DISABLED
 |