summaryrefslogtreecommitdiffstats
path: root/Python/pyarena.c
blob: a262115bbf3564c42b411cd7ed744fc485df7bb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "Python.h"
#include "pyarena.h"

/* An arena list is a linked list that can store either pointers or
   PyObjects.  The type is clear from context.
 */

typedef struct _arena_list {
  struct _arena_list *al_next;
  void *al_pointer;
} PyArenaList;

/* There are two linked lists in an arena, one for malloc pointers and
   one for PyObject.  For each list, there is a pointer to the head
   and to the tail.  The head is used to free the list.  The tail is
   used to add a new element to the list.

   The list always keeps one un-used node at the end of the list.
*/

struct _arena {
  PyArenaList *a_malloc_head;
  PyArenaList *a_malloc_tail;
  PyArenaList *a_object_head;
  PyArenaList *a_object_tail;
};

static PyArenaList*
PyArenaList_New(void) 
{
  PyArenaList *alist = (PyArenaList *)malloc(sizeof(PyArenaList));
  if (!alist)
    return NULL;

  alist->al_next = NULL;
  alist->al_pointer = NULL;
  return alist;
}

static void
PyArenaList_FreeObject(PyArenaList *alist) 
{
  while (alist) {
    PyArenaList *prev;
    Py_XDECREF((PyObject *)alist->al_pointer);
    alist->al_pointer = NULL;
    prev = alist;
    alist = alist->al_next;
    free(prev);
  }
}

static void
PyArenaList_FreeMalloc(PyArenaList *alist)
{
  while (alist) {
    PyArenaList *prev;
    if (alist->al_pointer) {
      free(alist->al_pointer);
    }
    alist->al_pointer = NULL;
    prev = alist;
    alist = alist->al_next;
    free(prev);
  }
}


PyArena *
PyArena_New()
{
  PyArena* arena = (PyArena *)malloc(sizeof(PyArena));
  if (!arena)
    return NULL;

  arena->a_object_head = PyArenaList_New();
  arena->a_object_tail = arena->a_object_head;
  arena->a_malloc_head = PyArenaList_New();
  arena->a_malloc_tail = arena->a_malloc_head;
  return arena;
}

void
PyArena_Free(PyArena *arena)
{
  assert(arena);
  PyArenaList_FreeObject(arena->a_object_head);
  PyArenaList_FreeMalloc(arena->a_malloc_head);
  free(arena);
}

void *
PyArena_Malloc(PyArena *arena, size_t size) 
{
  /* A better implementation might actually use an arena.  The current
     approach is just a trivial implementation of the API that allows
     it to be tested.
  */
  void *p;
  assert(size != 0);
  p = malloc(size);
  if (p)
    PyArena_AddMallocPointer(arena, p);
  return p;
}

int
PyArena_AddMallocPointer(PyArena *arena, void *pointer) 
{
  PyArenaList *tail = arena->a_malloc_tail;
  assert(pointer);
  assert(tail->al_pointer != pointer);
  tail->al_next = PyArenaList_New();
  tail->al_pointer = pointer;
  arena->a_malloc_tail = tail->al_next;
  return 1;
}

int
PyArena_AddPyObject(PyArena *arena, PyObject *pointer) 
{
  PyArenaList *tail = arena->a_object_tail;
  assert(pointer);
  tail->al_next = PyArenaList_New();
  tail->al_pointer = pointer;
  arena->a_object_tail = tail->al_next;
  return 1;
}