queue.c
6.61 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
* Copyright (C) 1998 by the Board of Trustees
* of Leland Stanford Junior University.
* Copyright (C) 1998 Digital Equipment Corporation
*
* This file is part of the SimOS distribution.
* See LICENSE file for terms of the license.
*
*/
/*
* Routines for generic queue manipulation.
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "gamma.h"
#include "export.h"
/* These routines are generic in the sense that they manipulate
* arbitrary structures. The only requirement is that the first
* two fields in a structure correspond to the next and prev
* fields. This implies that a structure can be on at most one
* queue at any time.
*
* For more general (and more efficient) manipulation of queues, use
* the inline queue macros.
*
* Each queue has a head node which must be initialized with init_q().
*/
/* Initialize the head node for a queue. */
void
init_q (qnode_ptr qhead)
{
qhead->next = qhead;
qhead->prev = qhead;
}
/* returns true if the node is on the queue */
int
onqueue_q (qnode_ptr qhead, qnode_ptr pnode)
{
qnode_ptr pnext;
for (pnext = qhead->next; pnext != qhead; pnext = pnext->next)
if (pnext == pnode)
return 1;
return 0;
}
/* add a node to the tail of a queue */
void
enqueue_q (qnode_ptr qhead, qnode_ptr pnew)
{
/* link it into the list */
pnew->next = qhead;
pnew->prev = qhead->prev;
qhead->prev->next = pnew;
qhead->prev = pnew;
}
/* remove and return a node from the head of the queue */
qnode_ptr
dequeue_q (qnode_ptr qhead)
{
qnode_ptr pnode;
pnode = qhead->next;
if (pnode != qhead) {
qhead->next = pnode->next;
pnode->next->prev = qhead;
return pnode;
}
return NULL;
}
/* remove a node from a queue */
void
remove_q (qnode_ptr pnode)
{
pnode->next->prev = pnode->prev;
pnode->prev->next = pnode->next;
}
void
q_debug (qnode_ptr q)
{
qnode_ptr pnode;
pnode = q;
printf ("0x%p", pnode);
for (pnode = pnode->next; pnode != q; pnode = pnode->next)
printf (" --next--> 0x%p", pnode);
printf ("\n");
pnode = q;
printf ("0x%p", pnode);
for (pnode = pnode->prev; pnode != q; pnode = pnode->prev)
printf (" --prev--> 0x%p", pnode);
printf ("\n");
}
/* new_item() and free_item() are routines for managing memory allocation.
* Any type of struct may be allocated and freed with these routines.
* To use these routines, define a variable to serve as the "free list"
* pointer. Inline macro versions of these functions are faster but
* confuse dbx. If the macros are used and "DEBUG" is also defined,
* then the macros use these functions instead of inlining the code.
* This allows the macros to be used with debugging.
*
* new_item() uses the first field of the "item" (an arbitrary struct)
* to store the pointer to the next free item when it is on the free
* list. This field may be used for anything when it is not on the
* free list. So the item must be large enough to store a pointer.
*
* new_item() takes 3 arguments:
* free_item_ptr is the address of the "free list" pointer, initially NULL
* item_size is the size in bytes of an item
* name is the name of the calling routine
* new_item() does not zero the item. To allocate an item whose fields are
* initialized to zero, use new_zitem().
*
* free_item() puts an item back on the free list, so that it can be
* reused later without having to call malloc(). Items are never "freed"
* in the sense of returning them via "free()". Once an item is allocated,
* it is never returned to the free pool used by malloc().
*
* Example:
*
* void *Foo_free;
* #define FOO_SIZE (sizeof(struct foo))
* bar()
* {
* struct foo *pfoo;
* pfoo = (struct foo *) new_item(&Foo_free, FOO_SIZE, "bar");
* free_item(&Foo_free, pfoo);
* }
*
* To use the macro versions (notice the different calling sequence):
*
* bar()
* {
* struct foo *pfoo;
* NEW_ITEM(Foo_free, FOO_SIZE, pfoo, "bar");
* FREE_ITEM(Foo_free, pfoo);
* }
*/
qnode_ptr
new_item (void *free_item_ptr, size_t item_size, char *name)
{
int i;
qnode_ptr new_ptr, ptmp;
#ifdef SLOW_MEMORY
new_ptr = (qnode_ptr) malloc (item_size);
#else
/* reduce calls to malloc and make more efficient use of space
* by allocating several structs at once
*/
if (*(long **) free_item_ptr == NULL) {
new_ptr = (qnode_ptr) malloc (NITEMS * item_size);
if (new_ptr == NULL) {
fprintf (stderr, "%s: out of memory\n", name);
exit (1);
}
/* link all the free structs using the "next" pointer */
ptmp = new_ptr;
for (i = 0; i < NITEMS - 1; i++) {
ptmp->next = (qnode_ptr) ((char *) new_ptr +
((i + 1) * item_size));
ptmp = ptmp->next;
}
ptmp->next = NULL;
*(long **) free_item_ptr = (long *) new_ptr;
}
/* remove a free item from the front of the list */
new_ptr = *(qnode_ptr *) free_item_ptr;
*(long *) free_item_ptr = **(long **) free_item_ptr;
#endif
return new_ptr;
}
/* Define a macro for allocating a new structure, initialized to zero.
* Zero the item only when it is about to be used, not when allocating
* the array. This saves real memory when large items are allocated
* and not used, and saves time since the item must be zeroed anyway
* just before it is returned (since it may have been used and then freed).
*/
qnode_ptr
new_zitem (void *free_item_ptr, size_t item_size, char *name)
{
int i;
qnode_ptr new_ptr, ptmp;
#ifdef SLOW_MEMORY
new_ptr = (qnode_ptr) malloc (item_size);
#else
/* reduce calls to malloc and make more efficient use of space
* by allocating several structs at once
*/
if (*(long **) free_item_ptr == NULL) {
new_ptr = (qnode_ptr) malloc (NITEMS * item_size);
if (new_ptr == NULL) {
fprintf (stderr, "%s: out of memory\n", name);
exit (1);
}
/* link all the free structs using the "next" pointer */
ptmp = new_ptr;
for (i = 0; i < NITEMS - 1; i++) {
ptmp->next = (qnode_ptr) ((char *) new_ptr +
((i + 1) * item_size));
ptmp = ptmp->next;
}
ptmp->next = NULL;
*(long **) free_item_ptr = (long *) new_ptr;
}
/* remove a free item from the front of the list */
new_ptr = *(qnode_ptr *) free_item_ptr;
*(long *) free_item_ptr = **(long **) free_item_ptr;
memset (new_ptr, 0, item_size);
#endif
return new_ptr;
}
/* Free an item by putting it on the free list so it can be reused. */
void
free_item (void *free_item_ptr, void *item_ptr)
{
#ifdef SLOW_MEMORY
if (item_ptr != NULL)
free (item_ptr);
#else
/* put a free item on the front of the list */
*(long *) item_ptr = *(long *) free_item_ptr;
*(long **) free_item_ptr = (long *) item_ptr;
#endif
}