1 /*
2  * falloc.h
3  * nyquist memory allocation data structures and macros
4  *
5  * there is an falloc and ffree for each major type of data structure
6  * there is an falloc and ffree for generic (not so common) structures
7  * there is an frelease for some structures.  this reduces the
8  *      reference count for the particular structure by 1; it
9  *      does not continue recursively.
10  */
11 
12 /* Debugging support:
13  * When DEBUG_MEM is set, each piece of allocated storage will contain
14  * a pointer to a string naming the caller or other allocation info,
15  * and a sequence number.  (8 extra bytes are allocated for this info).
16  *
17  * When storage is freed, the ID is set to NULL, and the routine
18  * dbg_mem_check(ptr) will abort if ID is NULL.  Call this routine to
19  * avoid following a pointer to data that was previously freed.
20  *
21  * The goal of this support is to allow you to "go back" to the point
22  * where memory is corrupted; specifically where a memory block is freed
23  * too early.
24  *
25  * When a memory-related bug is crashing the system:
26  * (1) Recompile with DEBUG_MEM on.
27  * (2) Develop some Nyquist code that will predictably crash the system.
28  * (3) When Nyquist crashes, use a debugger to find where the bad
29  *   pointer came from.  See if the source of the pointer was freed.
30  * (4) If the source of the pointer was freed, then notice the sequence
31  *   number.
32  * (5) Rerun with dbg_mem_seq_num set to the number noted in (4).
33  * (6) Nyquist will print when the storage in question was allocated and
34  *   freed.  Use the debugger to find out why the storage is
35  *   freed too early and who did it.
36  * (7) If the source of the pointer in (3) was not freed, you're on your
37  *   own.
38  *
39  * The DEBUG_MEM related routines are:
40  *    dbg_mem_allocated: called when memory is allocated
41  *    dbg_mem_freed: called when memory is freed
42  *    dbg_mem_released: called when memory is released
43  *    dbg_mem_check: called to check memory
44  *
45  * see also xldmem.c:
46  * by setting xldmem_trace to a pointer, you can trace when the
47  * pointer is referenced by anything in the heap
48  */
49 
50 
51 /* to get size_t on pmax: */
52 #ifdef pmax
53 #include "sys/types.h"
54 #endif
55 
56 #include "cque.h"
57 #include "debug.h"
58 
59 #define DEBUG_MEM 0
60 #define DEBUG_MEM_INFO_SIZE (sizeof(long) + sizeof(char *))
61 
62 /* special free lists */
63 extern CQUE *sample_block_free; /* really a sample_block_type */
64 
65 /* special counts */
66 extern int sample_block_total;
67 extern int sample_block_used;
68 extern int snd_list_used;
69 extern int sound_used;
70 extern long table_memory;
71 
72 /* generic free lists */
73 #define MAXLISTS 128
74 extern CQUE *generic_free[MAXLISTS];
75 
76 /* general memory pool */
77 #define MAXPOOLSIZE 1000000
78 extern char *poolp;
79 extern char *poolend;
80 
81 /* sample block memory pool */
82 #define MAXSPOOLSIZE (256 * round_size(sizeof(sample_block_node)))
83 extern char *spoolp;
84 extern char *spoolend;
85 
86 extern int npools;
87 extern int sample_blocks_since_gc;
88 
89 #if !defined(TRACK_POOLS)
90 #define TRACK_POOLS 1
91 #endif
92 
93 #if defined(TRACK_POOLS) && TRACK_POOLS
94 // extern CQUE *pools;
95 void falloc_gc(void);
96 #endif
97 
98 void falloc_init(void);
99 void new_pool(void);
100 void new_spool(void);
101 void find_sample_block(sample_block_type *sp);
102 
103 char *get_from_pool(size_t siz);
104 
105 #define round_size(n)  (((n) + 7) & ~7)
106 
107 /* check_pool -- returns true if enough bytes are available */
108 #if DEBUG_MEM
109 #define check_pool(size) (poolp + (size) + DEBUG_MEM_INFO_SIZE <= poolend)
110 #define check_spool(size) (spoolp + (size) + DEBUG_MEM_INFO_SIZE <= spoolend)
111 #define DBG_MEM_ALLOCATED(p, who) dbg_mem_allocated(p, who)
112 #define DBG_MEM_FREED(p, who) dbg_mem_freed(p, who)
113 #define DBG_MEM_RELEASED(p, who) dbg_mem_released(p, who)
114 #else
115 #define check_pool(size) (poolp + (size) <= poolend)
116 #define check_spool(size) (spoolp + (size) <= spoolend)
117 #define DBG_MEM_ALLOCATED(p, who)
118 #define DBG_MEM_FREED(p, who)
119 #define DBG_MEM_RELEASED(p, who)
120 #endif
121 
122 #define BLOCKS_PER_GC 100
123 
124 /* There used to be a lot of code in this macro. I moved it to
125  * find_sample_block, but kept the macro mainly in order to pass sp
126  * by reference.
127  */
128 #define falloc_sample_block(sp, who) { \
129     find_sample_block(&sp); \
130     DBG_MEM_ALLOCATED(sp, who); }
131 
132 
133 #define ffree_sample_block(sp, who) { \
134     /* printf("freeing sample_block@%x\n", sp); */ \
135     DBG_MEM_FREED(sp, who); \
136     Qenter(sample_block_free, sp); \
137     sample_block_used--; \
138 }
139 
140 #define frelease_sample_block(sp, who) { \
141     sp->refcnt--; \
142     DBG_MEM_RELEASED(sp, who); \
143     if (sp->refcnt <= 0) { \
144         ffree_sample_block(sp); \
145     } \
146 }
147 
148 
149 /* NOTE: This must not cause garbage collection.
150  * LVAL parameters to snd_make_? functions are not
151  * protected and falloc_sound is invoked there.
152  */
153 #define snd_list_free (generic_free[round_size(sizeof(snd_list_node)) >> 3])
154 
155 #define falloc_snd_list(sp, who) {  \
156     if (!Qempty(snd_list_free)) \
157         Qget(snd_list_free, snd_list_type, sp) \
158     else \
159         sp = (snd_list_type)get_from_pool(round_size(sizeof(snd_list_node)));\
160     snd_list_used++; \
161     DBG_MEM_ALLOCATED(sp, who); \
162 }
163 
164 
165 #define ffree_snd_list(sp, who) { \
166     DBG_MEM_FREED(sp, who); \
167     Qenter(snd_list_free, sp); \
168     snd_list_used--; \
169 }
170 
171 
172 #define frelease_snd_list(sp, who) { \
173     sp->refcnt--; \
174     DBG_MEM_RELEASED(sp, who); \
175     if (sp->refcnt <= 0) { \
176         ffree_snd_list(sp, who); \
177     } \
178 }
179 
180 
181 #define sound_free (generic_free[round_size(sizeof(sound_node)) >> 3])
182 
183 #define NORMALSOUNDALLOC
184 #ifdef NORMALSOUNDALLOC
185 #define falloc_sound(sp, who) {  \
186     if (!Qempty(sound_free)) { \
187         Qget(sound_free, sound_type, sp); \
188     } else { \
189         sp = (sound_type) get_from_pool(round_size(sizeof(sound_node))); \
190     } \
191     sound_used++; \
192     DBG_MEM_ALLOCATED(sp, who); \
193 }
194 #else
195 #define falloc_sound(sp) \
196     sp =(sound_type) \
197         get_from_pool(round_size(sizeof(sound_node)))
198 #endif
199 
200 /* note: usually you call sound_unref, not this macro */
201 #define ffree_sound(sp, who) { \
202 /*    sound_already_free_test(); */ \
203     DBG_MEM_FREED(sp, who); \
204     Qenter(sound_free, sp); \
205     sound_used--; \
206 }
207 
208 
209 /* falloc_generic -- sp gets new node of type sptype */
210 /**/
211 #define falloc_generic(sp, sptype, who) { \
212     int size = round_size(sizeof(sptype)); \
213     falloc_generic_bytes(sp, sptype, size, who) }
214 
215 /* falloc_generic_n -- sp gets new array of n sptype's */
216 /**/
217 #define falloc_generic_n(sp, sptype, n, who) { \
218     int min_size = sizeof(sptype) * (n); \
219     int size = round_size(min_size); \
220     falloc_generic_bytes(sp, sptype, size, who) }
221 
222 #define falloc_generic_bytes(sp, sptype, size, who) \
223     if ((size >> 3) >= MAXLISTS) { \
224         stdputstr("falloc_generic problem\n"); \
225         sp = (sptype *) malloc(size); \
226     } else if (!Qempty(generic_free[size >> 3])) { \
227         Qget(generic_free[size >> 3], sptype *, sp); \
228     } else { \
229         sp = (sptype *) get_from_pool(size); \
230     } \
231     DBG_MEM_ALLOCATED(sp, who); \
232 /*    printf("GENERIC ALLOC %x\n", sp);  */
233 
234 
235 /* ffree_generic puts an item back on proper freelist */
236 /* NOTE: sIzE is capitalized funny so that it will not
237  * match an actual parameter, e.g. if the caller writes
238  * ffree_generic(ptr, size), we don't want the expanded
239  * code to include: "int size = round_size(size) >> 3"!
240  */
241 #define ffree_generic(sp, nn, who) { \
242     int sIzE = round_size(nn) >> 3; \
243     DBG_MEM_FREED(sp, who); \
244     /* printf("GENERIC FREE %x SIZE %d\n", sp, nnn); */ \
245     if ((sIzE) >= MAXLISTS) { \
246         free(sp); \
247     } else { \
248         Qenter(generic_free[sIzE], sp); \
249     } \
250 }
251