mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	Move some `#include <stdbool.h>` after `#include "Python.h"` when `pyconfig.h` is not included first and when we are in a platform-agnostic context. This is to avoid having features defined by `stdbool.h` before those decided by `Python.h`.
		
			
				
	
	
		
			193 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "Python.h"
 | 
						|
 | 
						|
#include "pycore_index_pool.h"
 | 
						|
#include "pycore_lock.h"
 | 
						|
 | 
						|
#include <stdbool.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
 |