| 1 | /* |
| 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
| 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 5 | * |
| 6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
| 7 | */ |
| 8 | |
| 9 | /* |
| 10 | * (author) M. L. Kersten |
| 11 | * @node Stack Management, The MAL Optimizer, Garbage Collection, The MAL Interpreter |
| 12 | * @+ MAL runtime stack |
| 13 | * The runtime context of a MAL procedure is allocated on the runtime stack |
| 14 | * of the corresponding interpreter. |
| 15 | * Access to the elements in the stack are through index offsets, |
| 16 | * determined during MAL procedure parsing. |
| 17 | * |
| 18 | * The scope administration for MAL procedures is |
| 19 | * decoupled from their actual runtime behavior. This means we are |
| 20 | * more relaxed on space allocation, because the size is determined |
| 21 | * by the number of MAL procedure definitions instead of the runtime |
| 22 | * calling behavior. (See mal_interpreter for details on value stack |
| 23 | * management) |
| 24 | * |
| 25 | * The variable names and types are kept in the stack to ease debugging. |
| 26 | * The underlying string value need not be garbage collected. |
| 27 | * Runtime storage for variables are allocated on the stack of the |
| 28 | * interpreter thread. The physical stack is often limited in size, |
| 29 | * which calls for safeguarding their value and garbage collection before returning. |
| 30 | * A malicious procedure or implementation will lead to memory leakage. |
| 31 | * |
| 32 | * A system command (linked C-routine) may be interested in extending the |
| 33 | * stack. This is precluded, because it could interfere with the recursive |
| 34 | * calling sequence of procedures. To accommodate the (rare) case, the routine |
| 35 | * should issue an exception to be handled by the interpreter before retrying. |
| 36 | * All other errors are turned into an exception, followed by continuing |
| 37 | * at the exception handling block of the MAL procedure. |
| 38 | * |
| 39 | * The interpreter should be protected against physical stack overflow. |
| 40 | * The solution chosen is to maintain an incremental depth size. |
| 41 | * Once it exceeds a threshold, we call upon the kernel to |
| 42 | * ensure we are still within safe bounds. |
| 43 | */ |
| 44 | /* |
| 45 | * The clearStack operation throws away any space occupied by variables |
| 46 | * Freeing the stack itself is automatic upon return from the interpreter |
| 47 | * context. Since the stack is allocated and zeroed on the calling stack, |
| 48 | * it may happen that entries are never set to a real value. |
| 49 | * This can be recognized by the vtype component |
| 50 | */ |
| 51 | #include "monetdb_config.h" |
| 52 | #include "mal_stack.h" |
| 53 | #include "mal_exception.h" |
| 54 | |
| 55 | /* #define DEBUG_MAL_STACK*/ |
| 56 | |
| 57 | MalStkPtr |
| 58 | newGlobalStack(int size) |
| 59 | { |
| 60 | MalStkPtr s; |
| 61 | |
| 62 | s = (MalStkPtr) GDKzalloc(stackSize(size) + offsetof(MalStack, stk)); |
| 63 | if (!s) |
| 64 | return NULL; |
| 65 | s->stksize = size; |
| 66 | return s; |
| 67 | } |
| 68 | |
| 69 | MalStkPtr |
| 70 | reallocGlobalStack(MalStkPtr old, int cnt) |
| 71 | { |
| 72 | int k; |
| 73 | MalStkPtr s; |
| 74 | |
| 75 | if (old->stksize > cnt) |
| 76 | return old; |
| 77 | k = ((cnt / STACKINCR) + 1) * STACKINCR; |
| 78 | s = newGlobalStack(k); |
| 79 | if (!s) { |
| 80 | return NULL; |
| 81 | } |
| 82 | memcpy(s, old, stackSize(old->stksize)); |
| 83 | s->stksize = k; |
| 84 | GDKfree(old); |
| 85 | return s; |
| 86 | } |
| 87 | |
| 88 | /* |
| 89 | * When you add a value to the stack, you should ensure that |
| 90 | * there is space left. It should only be used for global |
| 91 | * stack frames, because the others are allocated in the |
| 92 | * runtime stack. |
| 93 | */ |
| 94 | void |
| 95 | freeStack(MalStkPtr stk) |
| 96 | { |
| 97 | if (stk != NULL) { |
| 98 | clearStack(stk); |
| 99 | GDKfree(stk); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | void |
| 104 | clearStack(MalStkPtr s) |
| 105 | { |
| 106 | ValPtr v; |
| 107 | int i; |
| 108 | |
| 109 | if (!s) return; |
| 110 | |
| 111 | i = s->stktop; |
| 112 | |
| 113 | for (v = s->stk; i >= 0; i--, v++) |
| 114 | if (ATOMextern(v->vtype) && v->val.pval) { |
| 115 | GDKfree(v->val.pval); |
| 116 | v->vtype = 0; |
| 117 | v->val.pval = NULL; |
| 118 | } else if (BATatoms[v->vtype].atomUnfix) { |
| 119 | BATatoms[v->vtype].atomUnfix(VALget(v)); |
| 120 | v->vtype = 0; |
| 121 | v->val.pval = NULL; |
| 122 | } |
| 123 | s->stkbot = 0; |
| 124 | } |
| 125 | |
| 126 | |