fastqueue-lib 0.0.16__tar.gz → 0.0.18__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {fastqueue-lib-0.0.16/fastqueue_lib.egg-info → fastqueue_lib-0.0.18}/PKG-INFO +10 -8
  2. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/README.md +2 -5
  3. fastqueue_lib-0.0.18/fastqueue/__init__.py +3 -0
  4. fastqueue_lib-0.0.18/fastqueue/__init__.pyi +39 -0
  5. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/fastqueue/prototypes.py +21 -28
  6. fastqueue_lib-0.0.18/fastqueue/py.typed +0 -0
  7. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18/fastqueue_lib.egg-info}/PKG-INFO +10 -8
  8. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/SOURCES.txt +3 -1
  9. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/pyproject.toml +1 -1
  10. fastqueue_lib-0.0.18/src/ccqueue.cpp +131 -0
  11. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/src/cllqueue.cpp +28 -12
  12. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/src/fastqueue.c +324 -383
  13. fastqueue_lib-0.0.18/tests/test_memory.py +1030 -0
  14. fastqueue_lib-0.0.18/tests/test_queue.py +673 -0
  15. fastqueue-lib-0.0.16/fastqueue/__init__.py +0 -3
  16. fastqueue-lib-0.0.16/fastqueue.py +0 -144
  17. fastqueue-lib-0.0.16/src/ccqueue.cpp +0 -89
  18. fastqueue-lib-0.0.16/tests/test_queue.py +0 -248
  19. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/LICENSE +0 -0
  20. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/MANIFEST.in +0 -0
  21. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/dependency_links.txt +0 -0
  22. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/requires.txt +0 -0
  23. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/fastqueue_lib.egg-info/top_level.txt +0 -0
  24. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/setup.cfg +0 -0
  25. {fastqueue-lib-0.0.16 → fastqueue_lib-0.0.18}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: fastqueue-lib
3
- Version: 0.0.16
3
+ Version: 0.0.18
4
4
  Summary: Fast single ended queue library for python
5
5
  Author-email: Matthew Taylor <matthew.taylor.andre@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/MatthewAndreTaylor/fastqueue
@@ -19,9 +19,14 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
19
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Requires-Python: >=3.7
21
21
  Description-Content-Type: text/markdown
22
+ License-File: LICENSE
22
23
  Provides-Extra: examples
24
+ Requires-Dist: matplotlib; extra == "examples"
25
+ Requires-Dist: numpy; extra == "examples"
26
+ Requires-Dist: pandas; extra == "examples"
23
27
  Provides-Extra: tests
24
- License-File: LICENSE
28
+ Requires-Dist: pytest; extra == "tests"
29
+ Dynamic: license-file
25
30
 
26
31
  # fastqueue
27
32
 
@@ -31,7 +36,7 @@ License-File: LICENSE
31
36
  [![PyPI](https://img.shields.io/pypi/v/fastqueue-lib.svg)](https://pypi.org/project/fastqueue-lib/)
32
37
 
33
38
 
34
- Single ended fast queue's built in C tuned for python.
39
+ Single-ended fast queues built in C tuned for Python.
35
40
 
36
41
  ## Requirements
37
42
 
@@ -39,7 +44,7 @@ Single ended fast queue's built in C tuned for python.
39
44
 
40
45
  ## Installation
41
46
 
42
- To install fastqueue, using pip:
47
+ To install fastqueue, using [pip](https://pypi.org/project/fastqueue-lib):
43
48
 
44
49
  ```bash
45
50
  pip install fastqueue-lib
@@ -93,9 +98,6 @@ True
93
98
  ['🚒', '🛴', '🚅', '🚗']
94
99
  ```
95
100
 
96
- Another alternative is `fastqueue.LockQueue()` which supports all queue operations.
97
- `fastqueue.LockQueue()` is built as a thread-safe alternative to the other queue types.
98
-
99
101
  ## Example Benchmarks
100
102
 
101
103
  ### Queue operations
@@ -6,7 +6,7 @@
6
6
  [![PyPI](https://img.shields.io/pypi/v/fastqueue-lib.svg)](https://pypi.org/project/fastqueue-lib/)
7
7
 
8
8
 
9
- Single ended fast queue's built in C tuned for python.
9
+ Single-ended fast queues built in C tuned for Python.
10
10
 
11
11
  ## Requirements
12
12
 
@@ -14,7 +14,7 @@ Single ended fast queue's built in C tuned for python.
14
14
 
15
15
  ## Installation
16
16
 
17
- To install fastqueue, using pip:
17
+ To install fastqueue, using [pip](https://pypi.org/project/fastqueue-lib):
18
18
 
19
19
  ```bash
20
20
  pip install fastqueue-lib
@@ -68,9 +68,6 @@ True
68
68
  ['🚒', '🛴', '🚅', '🚗']
69
69
  ```
70
70
 
71
- Another alternative is `fastqueue.LockQueue()` which supports all queue operations.
72
- `fastqueue.LockQueue()` is built as a thread-safe alternative to the other queue types.
73
-
74
71
  ## Example Benchmarks
75
72
 
76
73
  ### Queue operations
@@ -0,0 +1,3 @@
1
+ __all__ = "Queue", "QueueC"
2
+
3
+ from _fastqueue import Queue, QueueC
@@ -0,0 +1,39 @@
1
+ """
2
+ fastqueue
3
+ =====
4
+
5
+ * Queue
6
+ * QueueC
7
+
8
+ """
9
+ from typing import Any, Optional
10
+ from collections.abc import Iterable
11
+ from typing_extensions import Self
12
+
13
+ __all__ = "Queue", "QueueC"
14
+
15
+ class Queue:
16
+ def __init__(self, iterable: Optional[Iterable] = None) -> None: ...
17
+ def enqueue(self, item: Any) -> None: ...
18
+ def dequeue(self) -> Any: ...
19
+ def extend(self, items: Iterable[Any]) -> None: ...
20
+ def clear(self) -> None: ...
21
+ def __len__(self) -> int: ...
22
+ def __getitem__(self, index: int) -> Any: ...
23
+ def __setitem__(self, index: int, item: Any) -> None: ...
24
+ def __contains__(self, item: Any) -> bool: ...
25
+ def copy(self) -> Self: ...
26
+ def __copy__(self) -> Self: ...
27
+
28
+ class QueueC:
29
+ def __init__(self, iterable: Optional[Iterable] = None) -> None: ...
30
+ def enqueue(self, item: Any) -> None: ...
31
+ def dequeue(self) -> Any: ...
32
+ def extend(self, items: Iterable[Any]) -> None: ...
33
+ def clear(self) -> None: ...
34
+ def __len__(self) -> int: ...
35
+ def __getitem__(self, index: int) -> Any: ...
36
+ def __setitem__(self, index: int, item: Any) -> None: ...
37
+ def __contains__(self, item: Any) -> bool: ...
38
+ def copy(self) -> Self: ...
39
+ def __copy__(self) -> Self: ...
@@ -27,20 +27,21 @@ class LLNodeQueue(Structure):
27
27
 
28
28
 
29
29
  __libfile = Path(__file__).parent / "cllqueue.so"
30
- llqueue_lib = CDLL(str(__libfile))
30
+ # Native calls own Python references and must keep the GIL.
31
+ llqueue_lib = PyDLL(str(__libfile))
31
32
 
32
33
  llqueue_lib.new_node_queue.argtypes = []
33
34
  llqueue_lib.new_node_queue.restype = POINTER(LLNodeQueue)
34
35
 
35
- llqueue_lib.is_empty.argtypes = [POINTER(LLNodeQueue)]
36
- llqueue_lib.is_empty.restype = c_bool
37
-
38
36
  llqueue_lib.enqueue.argtypes = [POINTER(LLNodeQueue), py_object]
39
37
  llqueue_lib.enqueue.restype = None
40
38
 
41
39
  llqueue_lib.dequeue.argtypes = [POINTER(LLNodeQueue)]
42
40
  llqueue_lib.dequeue.restype = py_object
43
41
 
42
+ llqueue_lib.clear_q.argtypes = [POINTER(LLNodeQueue)]
43
+ llqueue_lib.clear_q.restype = None
44
+
44
45
  llqueue_lib.free_q.argtypes = [POINTER(LLNodeQueue)]
45
46
  llqueue_lib.free_q.restype = None
46
47
 
@@ -52,9 +53,6 @@ class LLQueue:
52
53
  def __init__(self):
53
54
  self.queue = llqueue_lib.new_node_queue()
54
55
 
55
- def is_empty(self) -> bool:
56
- return llqueue_lib.is_empty(self.queue)
57
-
58
56
  def __len__(self) -> int:
59
57
  return self.queue.contents.length
60
58
 
@@ -62,44 +60,43 @@ class LLQueue:
62
60
  llqueue_lib.enqueue(self.queue, item)
63
61
 
64
62
  def dequeue(self) -> Any:
65
- if self.is_empty():
66
- raise IndexError
67
63
  return llqueue_lib.dequeue(self.queue)
68
64
 
69
65
  def clear(self):
70
- llqueue_lib.free_q(self.queue)
71
- self.queue = llqueue_lib.new__queue()
66
+ llqueue_lib.clear_q(self.queue)
72
67
 
73
68
  def __del__(self):
74
- llqueue_lib.free_q(self.queue)
69
+ queue = getattr(self, "queue", None)
70
+ self.queue = None
71
+ llqueue_lib.free_q(queue)
75
72
 
76
73
 
77
74
  # Matt's Contiguous Queue
78
75
  class ContiguousQueue(Structure):
79
76
  _fields_ = [
80
77
  ("objects", POINTER(py_object)),
81
- ("length", c_int),
82
- ("capacity", c_int),
83
- ("front", c_int),
84
- ("back", c_int),
78
+ ("length", c_size_t),
79
+ ("capacity", c_size_t),
80
+ ("front", c_size_t),
81
+ ("back", c_size_t),
85
82
  ]
86
83
 
87
84
 
88
85
  __libfile = Path(__file__).parent / "ccqueue.so"
89
- cqueue_lib = CDLL(str(__libfile))
86
+ cqueue_lib = PyDLL(str(__libfile))
90
87
 
91
88
  cqueue_lib.new__queue.argtypes = []
92
89
  cqueue_lib.new__queue.restype = POINTER(ContiguousQueue)
93
90
 
94
- cqueue_lib.is_empty.argtypes = [POINTER(ContiguousQueue)]
95
- cqueue_lib.is_empty.restype = c_bool
96
-
97
91
  cqueue_lib.enqueue.argtypes = [POINTER(ContiguousQueue), py_object]
98
92
  cqueue_lib.enqueue.restype = None
99
93
 
100
94
  cqueue_lib.dequeue.argtypes = [POINTER(ContiguousQueue)]
101
95
  cqueue_lib.dequeue.restype = py_object
102
96
 
97
+ cqueue_lib.clear_q.argtypes = [POINTER(ContiguousQueue)]
98
+ cqueue_lib.clear_q.restype = None
99
+
103
100
  cqueue_lib.free_q.argtypes = [POINTER(ContiguousQueue)]
104
101
  cqueue_lib.free_q.restype = None
105
102
 
@@ -111,9 +108,6 @@ class ContQueue:
111
108
  def __init__(self):
112
109
  self.queue = cqueue_lib.new__queue()
113
110
 
114
- def is_empty(self) -> bool:
115
- return cqueue_lib.is_empty(self.queue)
116
-
117
111
  def __len__(self) -> int:
118
112
  return self.queue.contents.length
119
113
 
@@ -121,13 +115,12 @@ class ContQueue:
121
115
  cqueue_lib.enqueue(self.queue, item)
122
116
 
123
117
  def dequeue(self) -> Any:
124
- if self.is_empty():
125
- raise IndexError
126
118
  return cqueue_lib.dequeue(self.queue)
127
119
 
128
120
  def clear(self):
129
- cqueue_lib.free_q(self.queue)
130
- self.queue = cqueue_lib.new__queue()
121
+ cqueue_lib.clear_q(self.queue)
131
122
 
132
123
  def __del__(self):
133
- cqueue_lib.free_q(self.queue)
124
+ queue = getattr(self, "queue", None)
125
+ self.queue = None
126
+ cqueue_lib.free_q(queue)
File without changes
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: fastqueue-lib
3
- Version: 0.0.16
3
+ Version: 0.0.18
4
4
  Summary: Fast single ended queue library for python
5
5
  Author-email: Matthew Taylor <matthew.taylor.andre@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/MatthewAndreTaylor/fastqueue
@@ -19,9 +19,14 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
19
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Requires-Python: >=3.7
21
21
  Description-Content-Type: text/markdown
22
+ License-File: LICENSE
22
23
  Provides-Extra: examples
24
+ Requires-Dist: matplotlib; extra == "examples"
25
+ Requires-Dist: numpy; extra == "examples"
26
+ Requires-Dist: pandas; extra == "examples"
23
27
  Provides-Extra: tests
24
- License-File: LICENSE
28
+ Requires-Dist: pytest; extra == "tests"
29
+ Dynamic: license-file
25
30
 
26
31
  # fastqueue
27
32
 
@@ -31,7 +36,7 @@ License-File: LICENSE
31
36
  [![PyPI](https://img.shields.io/pypi/v/fastqueue-lib.svg)](https://pypi.org/project/fastqueue-lib/)
32
37
 
33
38
 
34
- Single ended fast queue's built in C tuned for python.
39
+ Single-ended fast queues built in C tuned for Python.
35
40
 
36
41
  ## Requirements
37
42
 
@@ -39,7 +44,7 @@ Single ended fast queue's built in C tuned for python.
39
44
 
40
45
  ## Installation
41
46
 
42
- To install fastqueue, using pip:
47
+ To install fastqueue, using [pip](https://pypi.org/project/fastqueue-lib):
43
48
 
44
49
  ```bash
45
50
  pip install fastqueue-lib
@@ -93,9 +98,6 @@ True
93
98
  ['🚒', '🛴', '🚅', '🚗']
94
99
  ```
95
100
 
96
- Another alternative is `fastqueue.LockQueue()` which supports all queue operations.
97
- `fastqueue.LockQueue()` is built as a thread-safe alternative to the other queue types.
98
-
99
101
  ## Example Benchmarks
100
102
 
101
103
  ### Queue operations
@@ -1,11 +1,12 @@
1
1
  LICENSE
2
2
  MANIFEST.in
3
3
  README.md
4
- fastqueue.py
5
4
  pyproject.toml
6
5
  setup.py
7
6
  fastqueue/__init__.py
7
+ fastqueue/__init__.pyi
8
8
  fastqueue/prototypes.py
9
+ fastqueue/py.typed
9
10
  fastqueue_lib.egg-info/PKG-INFO
10
11
  fastqueue_lib.egg-info/SOURCES.txt
11
12
  fastqueue_lib.egg-info/dependency_links.txt
@@ -14,4 +15,5 @@ fastqueue_lib.egg-info/top_level.txt
14
15
  src/ccqueue.cpp
15
16
  src/cllqueue.cpp
16
17
  src/fastqueue.c
18
+ tests/test_memory.py
17
19
  tests/test_queue.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fastqueue-lib"
7
- version = "0.0.16"
7
+ version = "0.0.18"
8
8
  description="Fast single ended queue library for python"
9
9
  authors = [
10
10
  {name = "Matthew Taylor", email = "matthew.taylor.andre@gmail.com"},
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Copyright (c) 2023 Matthew Andre Taylor
3
+ */
4
+ #ifdef _WIN32
5
+ #define QUEUE_LIBRARY_API extern "C" __declspec(dllexport)
6
+ #else
7
+ #define QUEUE_LIBRARY_API extern "C"
8
+ #endif
9
+
10
+ #include <Python.h>
11
+ #include <stdlib.h>
12
+
13
+ // Matt's C Contiguous Queue
14
+ QUEUE_LIBRARY_API typedef struct queue {
15
+ PyObject** objects;
16
+ size_t length;
17
+ size_t capacity;
18
+ size_t front;
19
+ size_t back;
20
+ } queue_t;
21
+
22
+ QUEUE_LIBRARY_API queue_t* new__queue() {
23
+ queue_t* queue = (queue_t*)malloc(sizeof(queue_t));
24
+ if (queue == NULL) {
25
+ PyErr_NoMemory();
26
+ return NULL;
27
+ }
28
+ queue->length = 0;
29
+ queue->capacity = 256;
30
+ queue->front = 0;
31
+ queue->back = queue->capacity - 1;
32
+ queue->objects = (PyObject**)malloc(256 * sizeof(PyObject*));
33
+ if (queue->objects == NULL) {
34
+ free(queue);
35
+ PyErr_NoMemory();
36
+ return NULL;
37
+ }
38
+ return queue;
39
+ }
40
+
41
+ int resize(queue_t* queue, size_t newCapacity) {
42
+ if (newCapacity > PY_SSIZE_T_MAX / sizeof(PyObject*)) {
43
+ PyErr_NoMemory();
44
+ return -1;
45
+ }
46
+ PyObject** newObjects = (PyObject**)malloc(newCapacity * sizeof(PyObject*));
47
+ if (newObjects == NULL) {
48
+ PyErr_NoMemory();
49
+ return -1;
50
+ }
51
+ for (size_t i = 0; i < queue->length; ++i) {
52
+ newObjects[i] = queue->objects[(queue->front + i) % queue->capacity];
53
+ }
54
+
55
+ free(queue->objects);
56
+ queue->objects = newObjects;
57
+ queue->front = 0;
58
+ queue->back = (queue->length + newCapacity - 1) % newCapacity;
59
+ queue->capacity = newCapacity;
60
+ return 0;
61
+ }
62
+
63
+ QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
64
+ if (queue == NULL) {
65
+ PyErr_SetString(PyExc_RuntimeError, "Queue is not allocated");
66
+ return;
67
+ }
68
+ if (object == Py_None)
69
+ return;
70
+
71
+ if (queue->length == queue->capacity) {
72
+ if (queue->capacity > PY_SSIZE_T_MAX / sizeof(PyObject*) / 2) {
73
+ PyErr_NoMemory();
74
+ return;
75
+ }
76
+ if (resize(queue, queue->capacity ? queue->capacity * 2 : 256) < 0)
77
+ return;
78
+ }
79
+
80
+ Py_INCREF(object);
81
+ queue->front = (queue->front + queue->capacity - 1) % queue->capacity;
82
+ queue->objects[queue->front] = object;
83
+ queue->length++;
84
+ }
85
+
86
+ QUEUE_LIBRARY_API PyObject* dequeue(queue_t* queue) {
87
+ if (queue == NULL || queue->length == 0) {
88
+ PyErr_SetString(PyExc_IndexError, "dequeue from an empty queue");
89
+ return NULL;
90
+ }
91
+
92
+ PyObject* object = queue->objects[queue->back];
93
+ queue->back = (queue->back + queue->capacity - 1) % queue->capacity;
94
+ queue->length--;
95
+ return object;
96
+ }
97
+
98
+ QUEUE_LIBRARY_API void clear_q(queue_t* queue) {
99
+ if (queue == NULL || queue->length == 0) {
100
+ return;
101
+ }
102
+
103
+ PyObject** objects = queue->objects;
104
+ size_t length = queue->length;
105
+ size_t capacity = queue->capacity;
106
+ size_t front = queue->front;
107
+ // Detach before decrefs so callbacks cannot overwrite the old payloads.
108
+ queue->objects = NULL;
109
+ queue->length = queue->capacity = queue->front = queue->back = 0;
110
+ for (size_t i = 0; i < length; ++i) {
111
+ Py_DECREF(objects[(front + i) % capacity]);
112
+ }
113
+ if (queue->objects == NULL) {
114
+ queue->objects = objects;
115
+ queue->capacity = capacity;
116
+ queue->front = 0;
117
+ queue->back = capacity - 1;
118
+ } else {
119
+ free(objects);
120
+ }
121
+ }
122
+
123
+ QUEUE_LIBRARY_API void free_q(queue_t* queue) {
124
+ if (queue == NULL) {
125
+ return;
126
+ }
127
+
128
+ clear_q(queue);
129
+ free(queue->objects);
130
+ free(queue);
131
+ }
@@ -8,7 +8,6 @@
8
8
  #endif
9
9
 
10
10
  #include <Python.h>
11
- #include <stdio.h>
12
11
  #include <stdlib.h>
13
12
 
14
13
  // Matt's C Linked List Queue
@@ -26,8 +25,10 @@ QUEUE_LIBRARY_API typedef struct queue {
26
25
 
27
26
  node_t* new_node(PyObject* object) {
28
27
  node_t* node = (node_t*)malloc(sizeof(node_t));
29
- if (node == NULL)
28
+ if (node == NULL) {
29
+ PyErr_NoMemory();
30
30
  return NULL;
31
+ }
31
32
 
32
33
  Py_INCREF(object);
33
34
  node->object = object;
@@ -37,25 +38,27 @@ node_t* new_node(PyObject* object) {
37
38
 
38
39
  QUEUE_LIBRARY_API queue_t* new_node_queue() {
39
40
  queue_t* queue = (queue_t*)malloc(sizeof(queue_t));
41
+ if (queue == NULL) {
42
+ PyErr_NoMemory();
43
+ return NULL;
44
+ }
40
45
  queue->front = NULL;
41
46
  queue->back = NULL;
42
47
  queue->length = 0;
43
48
  return queue;
44
49
  }
45
50
 
46
- QUEUE_LIBRARY_API int is_empty(queue_t* queue) {
51
+ QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
47
52
  if (queue == NULL) {
48
- perror("Queue not allocated");
49
- return 1;
53
+ PyErr_SetString(PyExc_RuntimeError, "Queue is not allocated");
54
+ return;
50
55
  }
51
- return queue->length == 0;
52
- }
53
-
54
- QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
55
56
  if (object == Py_None)
56
57
  return;
57
58
 
58
59
  node_t* node = new_node(object);
60
+ if (node == NULL)
61
+ return;
59
62
  if (queue->back == NULL) {
60
63
  queue->front = node;
61
64
  queue->back = node;
@@ -67,8 +70,10 @@ QUEUE_LIBRARY_API void enqueue(queue_t* queue, PyObject* object) {
67
70
  }
68
71
 
69
72
  QUEUE_LIBRARY_API PyObject* dequeue(queue_t* queue) {
70
- if (is_empty(queue))
73
+ if (queue == NULL || queue->length == 0) {
74
+ PyErr_SetString(PyExc_IndexError, "dequeue from an empty queue");
71
75
  return NULL;
76
+ }
72
77
 
73
78
  node_t* temp = queue->front;
74
79
  PyObject* object = temp->object;
@@ -80,13 +85,16 @@ QUEUE_LIBRARY_API PyObject* dequeue(queue_t* queue) {
80
85
  return object;
81
86
  }
82
87
 
83
- QUEUE_LIBRARY_API void free_q(queue_t* queue) {
88
+ QUEUE_LIBRARY_API void clear_q(queue_t* queue) {
84
89
  if (queue == NULL) {
85
- perror("Queue not allocated");
86
90
  return;
87
91
  }
88
92
 
89
93
  node_t* current = queue->front;
94
+ // Detach before decrefs so finalizers can safely reuse this queue.
95
+ queue->front = NULL;
96
+ queue->back = NULL;
97
+ queue->length = 0;
90
98
  node_t* next;
91
99
  while (current != NULL) {
92
100
  next = current->next;
@@ -94,5 +102,13 @@ QUEUE_LIBRARY_API void free_q(queue_t* queue) {
94
102
  free(current);
95
103
  current = next;
96
104
  }
105
+ }
106
+
107
+ QUEUE_LIBRARY_API void free_q(queue_t* queue) {
108
+ if (queue == NULL) {
109
+ return;
110
+ }
111
+
112
+ clear_q(queue);
97
113
  free(queue);
98
114
  }