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