1 /*******************************************************************************
2 * Copyright (c) 2017, College of William & Mary
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the College of William & Mary nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COLLEGE OF WILLIAM & MARY BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * PRIMME: https://github.com/primme/primme
28 * Contact: Andreas Stathopoulos, a n d r e a s _at_ c s . w m . e d u
29 *******************************************************************************
30 * File: memman.c
31 *
32 * Purpose - Define functions to track allocated memory and free them in
33 * case of error.
34 *
35 ******************************************************************************/
36
37 #ifndef THIS_FILE
38 #define THIS_FILE "../linalg/memman.c"
39 #endif
40
41 #include <stdlib.h> /* free */
42 #include <assert.h>
43 #include <math.h>
44 #include "common.h"
45 #include "memman.h"
46
47
48 /*******************************************************************************
49 * Subroutine Mem_push_frame - Push a new frame in the context.
50 *
51 * INPUT PARAMETERS
52 * ----------------------------------
53 * ctx context
54 *
55 ******************************************************************************/
56
free_dummy(void * p,primme_context ctx)57 static int free_dummy(void *p, primme_context ctx) {
58 (void)ctx;
59 free(p);
60 return 0;
61 }
62
Mem_push_frame(primme_context * ctx)63 int Mem_push_frame(primme_context *ctx) {
64
65 /* Quick exit */
66
67 if (!ctx) return 0;
68
69 primme_frame *f = NULL;
70 primme_alloc *a = NULL;
71 if (MALLOC_PRIMME(1, &f) == 0 && MALLOC_PRIMME(1, &a) == 0) {
72 f->prev_alloc = a;
73 f->keep_frame = 0;
74 f->prev = ctx->mm;
75 a->p = f;
76 a->free_fn = free_dummy;
77 a->prev = NULL;
78 #ifndef NDEBUG
79 a->debug = NULL;
80 #endif
81 ctx->mm = f;
82 } else {
83 if (f) free(f);
84 if (a) free(a);
85 return -1;
86 }
87
88 return 0;
89 }
90
91 /*******************************************************************************
92 * Subroutine Mem_pop_frame - Remove the last frame pushed in the context.
93 *
94 * INPUT PARAMETERS
95 * ----------------------------------
96 * ctx context
97 *
98 ******************************************************************************/
99
Mem_pop_frame(primme_context * ctx)100 int Mem_pop_frame(primme_context *ctx) {
101
102 /* Quick exit */
103
104 if (!ctx || !ctx->mm) return 0;
105
106 /* Show message if there is no previous frame and they want to keep the
107 * frame. */
108
109 if (ctx->mm->keep_frame && !ctx->mm->prev && !ctx->mm->prev_alloc) {
110 PRINTFALLCTX(*ctx, 1, "Warning: no frame where to keep allocations");
111 return -1;
112 }
113
114 /* Store the reference to the previous frame. The current frame may be freed
115 * in the followings commands. */
116
117 primme_frame *mm_prev = ctx->mm->prev;
118
119 /* If it is asked to keep the frame, transfer all registers to the */
120 /* previous frame. */
121
122 if (ctx->mm->keep_frame && ctx->mm->prev) {
123 primme_alloc *a = ctx->mm->prev_alloc;
124 while (a) {
125 primme_alloc *a_prev = a->prev;
126 a->prev = ctx->mm->prev->prev_alloc;
127 ctx->mm->prev->prev_alloc = a;
128 a = a_prev;
129 }
130 }
131
132 /* If not, the function should have freed all allocations. */
133
134 else {
135
136 /* Warning about allocations not made by Mem_push_frame */
137
138 #ifndef NDEBUG
139 primme_alloc *a = ctx->mm->prev_alloc;
140 while (a) {
141 if (a->free_fn != free_dummy) {
142 PRINTFALLCTX(*ctx, 1,
143 "Warning: the allocation at %s has not been freed",
144 a->debug ? a->debug : "unknown");
145 assert(0);
146 }
147 a = a->prev;
148 }
149 #endif
150
151 /* If there are allocations, just free them */
152
153 Mem_pop_clean_frame(*ctx);
154 }
155
156 /* Set the current frame as the previous one */
157
158 ctx->mm = mm_prev;
159
160 return 0;
161 }
162
163 /*******************************************************************************
164 * Subroutine Mem_pop_clean_frame - Free all allocations registered already.
165 *
166 * INPUT PARAMETERS
167 * ----------------------------------
168 * ctx context
169 *
170 ******************************************************************************/
171
Mem_pop_clean_frame(primme_context ctx)172 int Mem_pop_clean_frame(primme_context ctx) {
173
174 primme_alloc *a = ctx.mm ? ctx.mm->prev_alloc : NULL;
175 if (ctx.mm) ctx.mm->prev_alloc = NULL;
176 while (a) {
177 primme_alloc *a_prev = a->prev;
178 if (a->p) a->free_fn(a->p, ctx);
179 free(a);
180 a = a_prev;
181 }
182
183 return 0;
184 }
185
186 /*******************************************************************************
187 * Subroutine Mem_keep_frame - Ask to not remove the last frame pushed.
188 *
189 * INPUT/OUTPUT PARAMETERS
190 * ----------------------------------
191 * ctx context
192 *
193 ******************************************************************************/
194
Mem_keep_frame(primme_context ctx)195 int Mem_keep_frame(primme_context ctx) {
196
197 assert(ctx.mm);
198 ctx.mm->keep_frame = 1;
199
200 return 0;
201 }
202
203 /*******************************************************************************
204 * Subroutine Mem_register_alloc - Register a pointer been allocated and the
205 * function to free the pointer.
206 *
207 * INPUT PARAMETERS
208 * ----------------------------------
209 * p Pointer been allocated
210 * free_fn Function to free the pointer
211 * ctx context
212 *
213 ******************************************************************************/
214
Mem_register_alloc(void * p,free_fn_type free_fn,primme_context ctx)215 int Mem_register_alloc(void *p, free_fn_type free_fn, primme_context ctx) {
216
217 assert(ctx.mm);
218
219 primme_alloc *prev_alloc = ctx.mm->prev_alloc, *a;
220 CHKERR(MALLOC_PRIMME(1, &a));
221 a->p = p;
222 a->free_fn = free_fn;
223 a->prev = prev_alloc;
224 #ifndef NDEBUG
225 a->debug = NULL;
226 #endif
227 ctx.mm->prev_alloc = a;
228
229 return 0;
230 }
231
232 /*******************************************************************************
233 * Subroutine Mem_deregister_alloc - Remove the pointer from the current frame
234 *
235 * INPUT PARAMETERS
236 * ----------------------------------
237 * p Pointer been removed
238 * ctx context
239 *
240 ******************************************************************************/
241
Mem_deregister_alloc(void * p,primme_context ctx)242 int Mem_deregister_alloc(void *p, primme_context ctx) {
243
244 if (!p) return 0;
245
246 assert(ctx.mm);
247
248 /* Find the register with the pointer p and who points out that register */
249
250 primme_frame *f = ctx.mm;
251 primme_alloc *a = NULL, **prev = NULL;
252 while (f) {
253 a = f->prev_alloc;
254 prev = &f->prev_alloc;
255 while (a && a->p != p) {
256 prev = &a->prev;
257 a = a->prev;
258 }
259 if (a) break;
260 f = f->prev;
261 }
262
263 /* Remove the register and link the list properly */
264
265 assert(a);
266 *prev = a->prev;
267 free(a);
268
269 return 0;
270 }
271
272 /*******************************************************************************
273 * Subroutine Mem_register_alloc - Register a pointer been allocated and the
274 * function to free the pointer.
275 *
276 * INPUT PARAMETERS
277 * ----------------------------------
278 * p Pointer been allocated
279 * free_fn Function to free the pointer
280 * ctx context
281 *
282 ******************************************************************************/
283
Mem_debug_frame(const char * debug,primme_context ctx)284 int Mem_debug_frame(const char *debug, primme_context ctx) {
285
286 /* Quick exit */
287
288 if (!ctx.mm) return 0;
289
290 #ifndef NDEBUG
291 primme_alloc *a = ctx.mm->prev_alloc;
292 while(a) {
293 if (!a->debug) a->debug = debug;
294 a = a->prev;
295 }
296 #endif
297
298 return 0;
299 }
300