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. Kersten
11 * The MAL Interpreter
12 */
13#include "monetdb_config.h"
14#include "mal_runtime.h"
15#include "mal_interpreter.h"
16#include "mal_resource.h"
17#include "mal_listing.h"
18#include "mal_debugger.h" /* for mdbStep() */
19#include "mal_type.h"
20#include "mal_private.h"
21
22static lng qptimeout = 0; /* how often we print still running queries (usec) */
23
24void
25setqptimeout(lng usecs)
26{
27 qptimeout = usecs;
28}
29
30inline
31ptr getArgReference(MalStkPtr stk, InstrPtr pci, int k)
32{
33 /* the C standard says: "A pointer to a union object, suitably
34 * converted, points to each of its members (or if a member is a
35 * bit-field, then to the unit in which it resides), and vice
36 * versa." */
37 return (ptr) &stk->stk[pci->argv[k]].val;
38}
39
40str malCommandCall(MalStkPtr stk, InstrPtr pci)
41{
42 str ret= MAL_SUCCEED;
43
44 switch (pci->argc) {
45 case 0: ret = (*pci->fcn)();
46 break;
47 case 1: ret = (*pci->fcn)(
48 getArgReference(stk, pci, 0));
49 break;
50 case 2: ret = (*pci->fcn)(
51 getArgReference(stk, pci, 0),
52 getArgReference(stk, pci, 1));
53 break;
54 case 3: ret = (*pci->fcn)(
55 getArgReference(stk, pci, 0),
56 getArgReference(stk, pci, 1),
57 getArgReference(stk, pci, 2));
58 break;
59 case 4: ret = (*pci->fcn)(
60 getArgReference(stk, pci, 0),
61 getArgReference(stk, pci, 1),
62 getArgReference(stk, pci, 2),
63 getArgReference(stk, pci, 3));
64 break;
65 case 5: ret = (*pci->fcn)(
66 getArgReference(stk, pci, 0),
67 getArgReference(stk, pci, 1),
68 getArgReference(stk, pci, 2),
69 getArgReference(stk, pci, 3),
70 getArgReference(stk, pci, 4));
71 break;
72 case 6: ret = (*pci->fcn)(
73 getArgReference(stk, pci, 0),
74 getArgReference(stk, pci, 1),
75 getArgReference(stk, pci, 2),
76 getArgReference(stk, pci, 3),
77 getArgReference(stk, pci, 4),
78 getArgReference(stk, pci, 5));
79 break;
80 case 7: ret = (*pci->fcn)(
81 getArgReference(stk, pci, 0),
82 getArgReference(stk, pci, 1),
83 getArgReference(stk, pci, 2),
84 getArgReference(stk, pci, 3),
85 getArgReference(stk, pci, 4),
86 getArgReference(stk, pci, 5),
87 getArgReference(stk, pci, 6));
88 break;
89 case 8: ret = (*pci->fcn)(
90 getArgReference(stk, pci, 0),
91 getArgReference(stk, pci, 1),
92 getArgReference(stk, pci, 2),
93 getArgReference(stk, pci, 3),
94 getArgReference(stk, pci, 4),
95 getArgReference(stk, pci, 5),
96 getArgReference(stk, pci, 6),
97 getArgReference(stk, pci, 7));
98 break;
99 case 9: ret = (*pci->fcn)(
100 getArgReference(stk, pci, 0),
101 getArgReference(stk, pci, 1),
102 getArgReference(stk, pci, 2),
103 getArgReference(stk, pci, 3),
104 getArgReference(stk, pci, 4),
105 getArgReference(stk, pci, 5),
106 getArgReference(stk, pci, 6),
107 getArgReference(stk, pci, 7),
108 getArgReference(stk, pci, 8));
109 break;
110 case 10: ret = (*pci->fcn)(
111 getArgReference(stk, pci, 0),
112 getArgReference(stk, pci, 1),
113 getArgReference(stk, pci, 2),
114 getArgReference(stk, pci, 3),
115 getArgReference(stk, pci, 4),
116 getArgReference(stk, pci, 5),
117 getArgReference(stk, pci, 6),
118 getArgReference(stk, pci, 7),
119 getArgReference(stk, pci, 8),
120 getArgReference(stk, pci, 9));
121 break;
122 case 11: ret = (*pci->fcn)(
123 getArgReference(stk, pci, 0),
124 getArgReference(stk, pci, 1),
125 getArgReference(stk, pci, 2),
126 getArgReference(stk, pci, 3),
127 getArgReference(stk, pci, 4),
128 getArgReference(stk, pci, 5),
129 getArgReference(stk, pci, 6),
130 getArgReference(stk, pci, 7),
131 getArgReference(stk, pci, 8),
132 getArgReference(stk, pci, 9),
133 getArgReference(stk, pci, 10));
134 break;
135 case 12: ret = (*pci->fcn)(
136 getArgReference(stk, pci, 0),
137 getArgReference(stk, pci, 1),
138 getArgReference(stk, pci, 2),
139 getArgReference(stk, pci, 3),
140 getArgReference(stk, pci, 4),
141 getArgReference(stk, pci, 5),
142 getArgReference(stk, pci, 6),
143 getArgReference(stk, pci, 7),
144 getArgReference(stk, pci, 8),
145 getArgReference(stk, pci, 9),
146 getArgReference(stk, pci, 10),
147 getArgReference(stk, pci, 11));
148 break;
149 case 13: ret = (*pci->fcn)(
150 getArgReference(stk, pci, 0),
151 getArgReference(stk, pci, 1),
152 getArgReference(stk, pci, 2),
153 getArgReference(stk, pci, 3),
154 getArgReference(stk, pci, 4),
155 getArgReference(stk, pci, 5),
156 getArgReference(stk, pci, 6),
157 getArgReference(stk, pci, 7),
158 getArgReference(stk, pci, 8),
159 getArgReference(stk, pci, 9),
160 getArgReference(stk, pci, 10),
161 getArgReference(stk, pci, 11),
162 getArgReference(stk, pci, 12));
163 break;
164 case 14: ret = (*pci->fcn)(
165 getArgReference(stk, pci, 0),
166 getArgReference(stk, pci, 1),
167 getArgReference(stk, pci, 2),
168 getArgReference(stk, pci, 3),
169 getArgReference(stk, pci, 4),
170 getArgReference(stk, pci, 5),
171 getArgReference(stk, pci, 6),
172 getArgReference(stk, pci, 7),
173 getArgReference(stk, pci, 8),
174 getArgReference(stk, pci, 9),
175 getArgReference(stk, pci, 10),
176 getArgReference(stk, pci, 11),
177 getArgReference(stk, pci, 12),
178 getArgReference(stk, pci, 13));
179 break;
180 case 15: ret = (*pci->fcn)(
181 getArgReference(stk, pci, 0),
182 getArgReference(stk, pci, 1),
183 getArgReference(stk, pci, 2),
184 getArgReference(stk, pci, 3),
185 getArgReference(stk, pci, 4),
186 getArgReference(stk, pci, 5),
187 getArgReference(stk, pci, 6),
188 getArgReference(stk, pci, 7),
189 getArgReference(stk, pci, 8),
190 getArgReference(stk, pci, 9),
191 getArgReference(stk, pci, 10),
192 getArgReference(stk, pci, 11),
193 getArgReference(stk, pci, 12),
194 getArgReference(stk, pci, 13),
195 getArgReference(stk, pci, 14));
196 break;
197 case 16: ret = (*pci->fcn)(
198 getArgReference(stk, pci, 0),
199 getArgReference(stk, pci, 1),
200 getArgReference(stk, pci, 2),
201 getArgReference(stk, pci, 3),
202 getArgReference(stk, pci, 4),
203 getArgReference(stk, pci, 5),
204 getArgReference(stk, pci, 6),
205 getArgReference(stk, pci, 7),
206 getArgReference(stk, pci, 8),
207 getArgReference(stk, pci, 9),
208 getArgReference(stk, pci, 10),
209 getArgReference(stk, pci, 11),
210 getArgReference(stk, pci, 12),
211 getArgReference(stk, pci, 13),
212 getArgReference(stk, pci, 14),
213 getArgReference(stk, pci, 15));
214 break;
215 default:
216 throw(MAL, "mal.interpreter", "too many arguments for command call");
217 }
218 return ret;
219}
220
221/*
222 * Copy the constant values onto the stack frame
223 * Also we cannot overwrite values on the stack as this maybe part of a
224 * sequence of factory calls.
225 */
226#define initStack(S, R)\
227 for (i = S; i < mb->vtop; i++) {\
228 lhs = &stk->stk[i];\
229 if (isVarConstant(mb, i) > 0) {\
230 if (!isVarDisabled(mb, i)) {\
231 rhs = &getVarConstant(mb, i);\
232 if(VALcopy(lhs, rhs) == NULL) \
233 R = 0; \
234 }\
235 } else {\
236 lhs->vtype = getVarGDKType(mb, i);\
237 lhs->val.pval = 0;\
238 lhs->len = 0;\
239 }\
240 }
241
242int
243isNotUsedIn(InstrPtr p, int start, int a)
244{
245 int k;
246 for (k = start; k < p->argc; k++)
247 if (getArg(p, k) == a)
248 return 0;
249 return 1;
250}
251
252MalStkPtr
253prepareMALstack(MalBlkPtr mb, int size)
254{
255 MalStkPtr stk = NULL;
256 int i, res = 1;
257 ValPtr lhs, rhs;
258
259 stk = newGlobalStack(size);
260 if (!stk) {
261 return NULL;
262 }
263 //memset((char *)stk, 0, stackSize(size)); already set
264 //stk->stksize = size;
265 stk->stktop = mb->vtop;
266 stk->blk = mb;
267 stk->workers = 0;
268 stk->memory = 0;
269 initStack(0, res);
270 if(!res) {
271 freeStack(stk);
272 return NULL;
273 }
274 return stk;
275}
276
277str runMAL(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr env)
278{
279 MalStkPtr stk = NULL;
280 int i;
281 ValPtr lhs, rhs;
282 str ret;
283 (void) mbcaller;
284
285 /* Prepare a new interpreter call. This involves two steps, (1)
286 * allocate the minimum amount of stack space needed, some slack
287 * resources are included to permit code optimizers to add a few
288 * variables at run time, (2) copying the arguments into the new
289 * stack frame.
290 *
291 * The env stackframe is set when a MAL function is called
292 * recursively. Alternatively, there is no caller but a stk to be
293 * re-used for interpretation. We assume here that it aligns with
294 * the variable table of the routine being called.
295 *
296 * allocate space for value stack the global stack should be large
297 * enough
298 */
299 cntxt->lastcmd= time(0);
300 ATOMIC_SET(&cntxt->lastprint, GDKusec());
301 if (env != NULL) {
302 int res = 1;
303 stk = env;
304 if (mb != stk->blk)
305 throw(MAL, "mal.interpreter","misalignment of symbols");
306 if (mb->vtop > stk->stksize)
307 throw(MAL, "mal.interpreter","stack too small");
308 initStack(env->stkbot, res);
309 if(!res)
310 throw(MAL, "mal.interpreter", MAL_MALLOC_FAIL);
311 } else {
312 stk = prepareMALstack(mb, mb->vsize);
313 if (stk == 0)
314 throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
315 stk->blk = mb;
316 stk->cmd = cntxt->itrace; /* set debug mode */
317 /*safeguardStack*/
318 if( env){
319 stk->stkdepth = stk->stksize + env->stkdepth;
320 stk->calldepth = env->calldepth + 1;
321 stk->up = env;
322 if (stk->calldepth > 256)
323 throw(MAL, "mal.interpreter", MAL_CALLDEPTH_FAIL);
324 }
325 /*
326 * An optimization is to copy all constant variables used in
327 * functions immediately onto the value stack. Then we do not
328 * have to check for their location later on any more. At some
329 * point, the effect is optimal, if at least several constants
330 * are referenced in a function (a gain on tst400a of 20% has
331 * been observed due the small size of the function).
332 */
333 }
334 if (stk->cmd && env && stk->cmd != 'f')
335 stk->cmd = env->cmd;
336 ret = runMALsequence(cntxt, mb, 1, 0, stk, env, 0);
337
338 /* pass the new debug mode to the caller */
339 if (stk->cmd && env && stk->cmd != 'f')
340 env->cmd = stk->cmd;
341 if (!stk->keepAlive && garbageControl(getInstrPtr(mb, 0)))
342 garbageCollector(cntxt, mb, stk, env != stk);
343 if (stk && stk != env)
344 freeStack(stk);
345 if (ret == MAL_SUCCEED && cntxt->querytimeout && mb->starttime && GDKusec()- mb->starttime > cntxt->querytimeout)
346 throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
347 return ret;
348}
349
350/* Single instruction
351 * It is possible to re-enter the interpreter at a specific place.
352 * This is used in the area where we need to support co-routines.
353 *
354 * A special case for MAL interpretation is to execute just one instruction.
355 * This is typically used by optimizers and schedulers that need part of the
356 * answer to direct their actions. Or, a dataflow scheduler could step in
357 * to enforce a completely different execution order.
358 */
359str reenterMAL(Client cntxt, MalBlkPtr mb, int startpc, int stoppc, MalStkPtr stk)
360{
361 str ret;
362 int keepAlive;
363
364 if (stk == NULL)
365 throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
366 keepAlive = stk->keepAlive;
367 ret = runMALsequence(cntxt, mb, startpc, stoppc, stk, 0, 0);
368
369 /* pass the new debug mode to the caller */
370 if (keepAlive == 0 && garbageControl(getInstrPtr(mb, 0)))
371 garbageCollector(cntxt, mb, stk, stk != 0);
372 return ret;
373}
374
375/*
376 * Front ends may benefit from a more direct call to any of the MAL
377 * procedural abstractions. The argument list points to the arguments
378 * for the block to be executed. An old stack frame may be re-used,
379 * but it is then up to the caller to ensure it is properly
380 * initialized.
381 * The call does not return values, they are ignored.
382 */
383str
384callMAL(Client cntxt, MalBlkPtr mb, MalStkPtr *env, ValPtr argv[], char debug)
385{
386 MalStkPtr stk = NULL;
387 str ret = MAL_SUCCEED;
388 int i;
389 ValPtr lhs;
390 InstrPtr pci = getInstrPtr(mb, 0);
391
392 cntxt->lastcmd= time(0);
393#ifdef DEBUG_CALLMAL
394 fprintf(stderr, "callMAL\n");
395 fprintInstruction(stderr, mb, 0, pci, LIST_MAL_ALL);
396#endif
397 switch (pci->token) {
398 case FUNCTIONsymbol:
399 case FCNcall:
400 /*
401 * Prepare the stack frame for this operation. Copy all the arguments
402 * in place. We assume that the caller has supplied pointers for
403 * all arguments and return values.
404 */
405 if (*env == NULL) {
406 stk = prepareMALstack(mb, mb->vsize);
407 if (stk == NULL)
408 throw(MAL, "mal.interpreter", SQLSTATE(HY001) MAL_MALLOC_FAIL);
409 stk->up = 0;
410 *env = stk;
411 } else {
412 ValPtr lhs, rhs;
413 int res = 1;
414
415 stk = *env;
416 initStack(0, res);
417 if(!res)
418 throw(MAL, "mal.interpreter", SQLSTATE(HY001) MAL_MALLOC_FAIL);
419 }
420 assert(stk);
421 for (i = pci->retc; i < pci->argc; i++) {
422 lhs = &stk->stk[pci->argv[i]];
423 if (VALcopy(lhs, argv[i]) == NULL)
424 throw(MAL, "mal.interpreter", SQLSTATE(HY001) MAL_MALLOC_FAIL);
425 if (lhs->vtype == TYPE_bat)
426 BBPretain(lhs->val.bval);
427 }
428 stk->cmd = debug;
429 ret = runMALsequence(cntxt, mb, 1, 0, stk, 0, 0);
430 break;
431 case FACTORYsymbol:
432 case FACcall:
433 ret = callFactory(cntxt, mb, argv, debug);
434 break;
435 case PATcall:
436 case CMDcall:
437 default:
438 throw(MAL, "mal.interpreter", RUNTIME_UNKNOWN_INSTRUCTION);
439 }
440 if (stk)
441 garbageCollector(cntxt, mb, stk, TRUE);
442 if ( ret == MAL_SUCCEED && cntxt->querytimeout && mb->starttime && GDKusec()- mb->starttime > cntxt->querytimeout)
443 throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
444 return ret;
445}
446
447/*
448 * The core of the interpreter is presented next. It takes the context
449 * information and starts the interpretation at the designated
450 * instruction. Note that the stack frame is aligned and initialized
451 * in the enclosing routine. When we start executing the first
452 * instruction, we take the wall-clock time for resource management.
453 */
454str runMALsequence(Client cntxt, MalBlkPtr mb, int startpc,
455 int stoppc, MalStkPtr stk, MalStkPtr env, InstrPtr pcicaller)
456{
457 ValPtr lhs, rhs, v;
458 int i, k;
459 InstrPtr pci = 0;
460 int exceptionVar;
461 str ret = MAL_SUCCEED, localGDKerrbuf= GDKerrbuf;
462 ValRecord backups[16];
463 ValPtr backup;
464 int garbages[16], *garbage;
465 int stkpc = 0;
466 RuntimeProfileRecord runtimeProfile, runtimeProfileFunction;
467 lng lastcheck = 0;
468 int startedProfileQueue = 0;
469#define CHECKINTERVAL 1000 /* how often do we check for client disconnect */
470 runtimeProfile.ticks = runtimeProfileFunction.ticks = 0;
471
472 if (stk == NULL)
473 throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
474
475 /* prepare extended backup and garbage structures */
476 if (startpc+1 == stoppc) {
477 pci = getInstrPtr(mb, startpc);
478 if (pci->argc > 16) {
479 backup = GDKmalloc(pci->argc * sizeof(ValRecord));
480 garbage = (int*)GDKzalloc(pci->argc * sizeof(int));
481 if( backup == NULL || garbage == NULL) {
482 GDKfree(backup);
483 GDKfree(garbage);
484 throw(MAL, "mal.interpreter", SQLSTATE(HY001) MAL_MALLOC_FAIL);
485 }
486 } else {
487 backup = backups;
488 garbage = garbages;
489 memset(garbages, 0, sizeof(garbages));
490 }
491 } else if ( mb->maxarg > 16 ){
492 backup = GDKmalloc(mb->maxarg * sizeof(ValRecord));
493 garbage = (int*)GDKzalloc(mb->maxarg * sizeof(int));
494 if( backup == NULL || garbage == NULL) {
495 GDKfree(backup);
496 GDKfree(garbage);
497 throw(MAL, "mal.interpreter", SQLSTATE(HY001) MAL_MALLOC_FAIL);
498 }
499 } else {
500 backup = backups;
501 garbage = garbages;
502 memset(garbages, 0, sizeof(garbages));
503 }
504
505 /* also produce event record for start of function */
506 if ( startpc == 1 && startpc < mb->stop ){
507 startedProfileQueue = 1;
508 runtimeProfileInit(cntxt, mb, stk);
509 runtimeProfileBegin(cntxt, mb, stk, getInstrPtr(mb,0), &runtimeProfileFunction);
510 mb->starttime = GDKusec();
511 if (cntxt->sessiontimeout && mb->starttime - cntxt->session > cntxt->sessiontimeout) {
512 if ( backup != backups) GDKfree(backup);
513 if ( garbage != garbages) GDKfree(garbage);
514 throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_SESSION_TIMEOUT);
515 }
516 }
517 stkpc = startpc;
518 exceptionVar = -1;
519
520 while (stkpc < mb->stop && stkpc != stoppc) {
521 // incomplete block being executed, requires at least signature and end statement
522 pci = getInstrPtr(mb, stkpc);
523 if (cntxt->mode == FINISHCLIENT){
524 stkpc = stoppc;
525 if (ret == MAL_SUCCEED)
526 ret= createException(MAL, "mal.interpreter", "prematurely stopped client");
527 break;
528 }
529#ifndef NDEBUG
530 if (cntxt->itrace || stk->status) {
531 if (stk->status == 'p'){
532 // execution is paused
533 while ( stk->status == 'p')
534 MT_sleep_ms(50);
535 continue;
536 }
537 if ( stk->status == 'q')
538 stk->cmd = 'x';
539
540 if (stk->cmd == 0)
541 stk->cmd = cntxt->itrace;
542 mdbStep(cntxt, mb, stk, stkpc);
543 if (stk->cmd == 'x' ) {
544 stk->cmd = 0;
545 stkpc = mb->stop;
546 ret= createException(MAL, "mal.interpreter", "prematurely stopped client");
547 break;
548 }
549 }
550#endif
551
552 //Ensure we spread system resources over multiple users as well.
553 runtimeProfileBegin(cntxt, mb, stk, pci, &runtimeProfile);
554 if (runtimeProfile.ticks > lastcheck + CHECKINTERVAL) {
555 if (cntxt->fdin && !mnstr_isalive(cntxt->fdin->s)) {
556 cntxt->mode = FINISHCLIENT;
557 stkpc = stoppc;
558 ret= createException(MAL, "mal.interpreter", "prematurely stopped client");
559 break;
560 }
561 lastcheck = runtimeProfile.ticks;
562 }
563
564 if (qptimeout > 0) {
565 lng t = GDKusec();
566 ATOMIC_BASE_TYPE lp = ATOMIC_GET(&cntxt->lastprint);
567 if ((lng) lp + qptimeout < t) {
568 /* if still the same, replace lastprint with current
569 * time and print the query */
570 if (ATOMIC_CAS(&cntxt->lastprint, &lp, t)) {
571 const char *q = cntxt->getquery ? cntxt->getquery(cntxt) : NULL;
572 fprintf(stderr, "#%s: query already running "LLFMT"s: %.200s\n",
573 cntxt->mythread->name,
574 (lng) (time(0) - cntxt->lastcmd),
575 q ? q : "");
576 }
577 }
578 }
579
580 /* The interpreter loop
581 * The interpreter is geared towards execution a MAL
582 * procedure together with all its descendant
583 * invocations. As such, it provides the MAL abtract
584 * machine processor.
585 *
586 * The value-stack frame of the surrounding scope is
587 * needed to resolve binding values. Getting (putting) a
588 * value from (into) a surrounding scope should be guarded
589 * with the exclusive access lock. This situation is
590 * encapsulated by a bind() function call, whose
591 * parameters contain the access mode required.
592 *
593 * The formal procedure arguments are assumed to always
594 * occupy the first elements in the value stack.
595 *
596 * Before we execute an instruction the variables to be
597 * garbage collected are identified. In the post-execution
598 * phase they are removed.
599 */
600 for (i = 0; i < pci->retc; i++)
601 backup[i] = stk->stk[getArg(pci, i)];
602
603 if (garbageControl(pci)) {
604 for (i = 0; i < pci->argc; i++) {
605 int a = getArg(pci, i);
606
607 if (stk->stk[a].vtype == TYPE_bat && getEndScope(mb, a) == stkpc && isNotUsedIn(pci, i + 1, a))
608 garbage[i] = a;
609 else
610 garbage[i] = -1;
611 }
612 }
613
614 freeException(ret);
615 ret = MAL_SUCCEED;
616 switch (pci->token) {
617 case ASSIGNsymbol:
618 /* Assignment command
619 * The assignment statement copies values around on
620 * the stack frame, including multiple assignments.
621 *
622 * Pushing constants/initial values onto the stack is
623 * a separate operation. It takes the constant value
624 * discovered at compile time and stored in the symbol
625 * table and moves it to the stackframe location. This
626 * activity is made part of the start-up procedure.
627 *
628 * The before after calls should be reconsidered here,
629 * because their. They seem superflous and the way
630 * they are used will cause errors in multi-assignment
631 * statements.
632 */
633 for (k = 0, i = pci->retc; k < pci->retc && i < pci->argc; i++, k++) {
634 lhs = &stk->stk[pci->argv[k]];
635 rhs = &stk->stk[pci->argv[i]];
636 if(VALcopy(lhs, rhs) == NULL) {
637 ret = createException(MAL, "mal.interpreter", MAL_MALLOC_FAIL);
638 break;
639 } else if (lhs->vtype == TYPE_bat && !is_bat_nil(lhs->val.bval))
640 BBPretain(lhs->val.bval);
641 }
642 break;
643 case PATcall:
644 if (pci->fcn == NULL) {
645 ret = createException(MAL,"mal.interpreter", "address of pattern %s.%s missing", pci->modname, pci->fcnname);
646 } else {
647 ret = (*pci->fcn)(cntxt, mb, stk, pci);
648#ifndef NDEBUG
649 if (ret == MAL_SUCCEED) {
650 /* check that the types of actual results match
651 * expected results */
652 for (i = 0; i < pci->retc; i++) {
653 int a = getArg(pci, i);
654 int t = getArgType(mb, pci, i);
655
656 if (isaBatType(t)) {
657 bat bid = stk->stk[a].val.bval;
658 BAT *_b = BATdescriptor(bid);
659 t = getBatType(t);
660 assert(stk->stk[a].vtype == TYPE_bat);
661 assert(is_bat_nil(bid) ||
662 t == TYPE_any ||
663 ATOMtype(_b->ttype) == ATOMtype(t));
664 if(_b) BBPunfix(bid);
665 } else {
666 assert(t == stk->stk[a].vtype);
667 }
668 }
669 }
670#endif
671 }
672 break;
673 case CMDcall:
674 ret = malCommandCall(stk, pci);
675#ifndef NDEBUG
676 if (ret == MAL_SUCCEED) {
677 /* check that the types of actual results match
678 * expected results */
679 for (i = 0; i < pci->retc; i++) {
680 int a = getArg(pci, i);
681 int t = getArgType(mb, pci, i);
682
683 if (isaBatType(t)) {
684 bat bid = stk->stk[a].val.bval;
685 t = getBatType(t);
686 assert(stk->stk[a].vtype == TYPE_bat);
687 assert(is_bat_nil(bid) ||
688 t == TYPE_any ||
689 ATOMtype(BBP_desc(bid)->ttype) == ATOMtype(t));
690 } else {
691 assert(t == stk->stk[a].vtype);
692 }
693 }
694 }
695#endif
696 break;
697 case FACcall:
698 /*
699 * Factory calls are more involved. At this stage it
700 * is a synchrononous call to the factory manager.
701 * Factory calls should deal with the reference
702 * counting.
703 */
704 if (pci->blk == NULL)
705 ret = createException(MAL,"mal.interpreter", "%s.%s[%d] reference to MAL function missing", getModuleId(pci), getFunctionId(pci), pci->pc);
706 else {
707 /* show call before entering the factory */
708#ifndef NDEBUG
709 if (cntxt->itrace) {
710 if (stk->cmd == 0)
711 stk->cmd = cntxt->itrace;
712 mdbStep(cntxt, pci->blk, stk, 0);
713 if (stk->cmd == 'x') {
714 stk->cmd = 0;
715 stkpc = mb->stop;
716 }
717 }
718#endif
719 ret = runFactory(cntxt, pci->blk, mb, stk, pci);
720 }
721 break;
722 case FCNcall:
723 /*
724 * MAL function calls are relatively expensive,
725 * because they have to assemble a new stack frame and
726 * do housekeeping, such as garbagecollection of all
727 * non-returned values.
728 */
729 { MalStkPtr nstk;
730 InstrPtr q;
731 int ii, arg;
732
733 stk->pcup = stkpc;
734 nstk = prepareMALstack(pci->blk, pci->blk->vsize);
735 if (nstk == 0){
736 ret= createException(MAL,"mal.interpreter",MAL_STACK_FAIL);
737 break;
738 }
739
740 /*safeguardStack*/
741 nstk->stkdepth = nstk->stksize + stk->stkdepth;
742 nstk->calldepth = stk->calldepth + 1;
743 nstk->up = stk;
744 if (nstk->calldepth > 256) {
745 ret= createException(MAL, "mal.interpreter", MAL_CALLDEPTH_FAIL);
746 GDKfree(nstk);
747 break;
748 }
749 if ((unsigned)nstk->stkdepth > THREAD_STACK_SIZE / sizeof(mb->var[0]) / 4 && THRhighwater()){
750 /* we are running low on stack space */
751 ret= createException(MAL, "mal.interpreter", MAL_STACK_FAIL);
752 GDKfree(nstk);
753 break;
754 }
755
756 /* copy arguments onto destination stack */
757 q= getInstrPtr(pci->blk,0);
758 arg = q->retc;
759 for (ii = pci->retc; ii < pci->argc; ii++,arg++) {
760 lhs = &nstk->stk[q->argv[arg]];
761 rhs = &stk->stk[pci->argv[ii]];
762 if(VALcopy(lhs, rhs) == NULL) {
763 GDKfree(nstk);
764 ret = createException(MAL, "mal.interpreter", MAL_MALLOC_FAIL);
765 break;
766 } else if (lhs->vtype == TYPE_bat)
767 BBPretain(lhs->val.bval);
768 }
769 if(ret == MAL_SUCCEED) {
770 ret = runMALsequence(cntxt, pci->blk, 1, pci->blk->stop, nstk, stk, pci);
771 for (ii = 0; ii < nstk->stktop; ii++)
772 if (ATOMextern(nstk->stk[ii].vtype))
773 GDKfree(nstk->stk[ii].val.pval);
774 GDKfree(nstk);
775 }
776 }
777 break;
778 case NOOPsymbol:
779 case REMsymbol:
780 break;
781 case ENDsymbol:
782 if (getInstrPtr(mb, 0)->token == FACTORYsymbol)
783 ret = shutdownFactory(cntxt, mb);
784 runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
785 runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb,0), &runtimeProfileFunction);
786 if (pcicaller && garbageControl(getInstrPtr(mb, 0)))
787 garbageCollector(cntxt, mb, stk, TRUE);
788 if (cntxt->querytimeout && mb->starttime && GDKusec()- mb->starttime > cntxt->querytimeout){
789 freeException(ret); /* overrule exception */
790 ret= createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
791 break;
792 }
793 stkpc = mb->stop; // force end of loop
794 continue;
795 default: {
796 str w;
797 if (pci->token < 0) {
798 /* temporary NOOP instruction */
799 break;
800 }
801 w= instruction2str(mb, 0, pci, FALSE);
802 if(w) {
803 ret = createException(MAL,"interpreter", "unkown operation:%s", w);
804 GDKfree(w);
805 } else {
806 ret = createException(MAL,"interpreter", "failed instruction2str");
807 }
808 // runtimeProfileBegin already sets the time in the instruction
809 if (cntxt->querytimeout && mb->starttime && GDKusec()- mb->starttime > cntxt->querytimeout){
810 freeException(ret); /* in case it's set */
811 ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
812 break;
813 }
814
815 stkpc= mb->stop;
816 continue;
817 } }
818
819 /* monitoring information should reflect the input arguments,
820 which may be removed by garbage collection */
821 /* BEWARE, the SQL engine or MAL function could zap the block, leaving garbage behind in pci */
822 /* this hack means we loose a closing event */
823 if( mb->stop <= 1)
824 continue;
825 runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
826 /* check for strong debugging after each MAL statement */
827 /* when we find a timeout situation, then the result is already known
828 * and assigned, the backup version is not removed*/
829 if ( pci->token != FACcall && ret== MAL_SUCCEED) {
830 for (i = 0; i < pci->retc; i++) {
831 lhs = &backup[i];
832 if (BATatoms[lhs->vtype].atomUnfix)
833 (*BATatoms[lhs->vtype].atomUnfix)(VALget(lhs));
834 if (ATOMextern(lhs->vtype) &&
835 lhs->val.pval &&
836 lhs->val.pval != ATOMnilptr(lhs->vtype) &&
837 lhs->val.pval != stk->stk[getArg(pci, i)].val.pval)
838 GDKfree(lhs->val.pval);
839 }
840 if (GDKdebug & (CHECKMASK|PROPMASK) && exceptionVar < 0) {
841 BAT *b;
842
843 for (i = 0; i < pci->retc; i++) {
844 if (garbage[i] == -1 && stk->stk[getArg(pci, i)].vtype == TYPE_bat &&
845 !is_bat_nil(stk->stk[getArg(pci, i)].val.bval)) {
846 assert(stk->stk[getArg(pci, i)].val.bval > 0);
847 b = BBPquickdesc(stk->stk[getArg(pci, i)].val.bval, false);
848 if (b == NULL) {
849 if (ret == MAL_SUCCEED)
850 ret = createException(MAL, "mal.propertyCheck", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
851 continue;
852 }
853 b = BATdescriptor(stk->stk[getArg(pci, i)].val.bval);
854 if (b) {
855 BATassertProps(b);
856 BBPunfix(b->batCacheid);
857 }
858 }
859 }
860 }
861
862 /* general garbage collection */
863 if (ret == MAL_SUCCEED && garbageControl(pci)) {
864 for (i = 0; i < pci->argc; i++) {
865 int a = getArg(pci, i);
866
867 if (isaBatType(getArgType(mb, pci, i))) {
868 bat bid = stk->stk[a].val.bval;
869
870 if (garbage[i] >= 0) {
871 PARDEBUG fprintf(stderr, "#GC pc=%d bid=%d %s done\n", stkpc, bid, getVarName(mb, garbage[i]));
872 bid = stk->stk[garbage[i]].val.bval;
873 stk->stk[garbage[i]].val.bval = bat_nil;
874 BBPrelease(bid);
875 }
876 }
877 }
878 }
879 }
880
881 /* Exception handling */
882 if (localGDKerrbuf && localGDKerrbuf[0]) {
883 if( ret == MAL_SUCCEED)
884 ret = createException(MAL,"mal.interpreter",GDK_EXCEPTION);
885 // TODO take properly care of the GDK exception
886 localGDKerrbuf[0]=0;
887 }
888
889 if (ret != MAL_SUCCEED) {
890 str msg = 0;
891
892#ifndef NDEBUG
893 if (stk->cmd) {
894 mnstr_printf(cntxt->fdout, "!ERROR: %s\n", ret);
895 stk->cmd = '\n'; /* in debugging go to step mode */
896 mdbStep(cntxt, mb, stk, stkpc);
897 if (stk->cmd == 'x' || stk->cmd == 'q' ) {
898 stkpc = mb->stop;
899 continue;
900 }
901 if (stk->cmd == 'r') {
902 stk->cmd = 'n';
903 stkpc = startpc;
904 exceptionVar = -1;
905 continue;
906 }
907 }
908#endif
909 /* Detect any exception received from the implementation. */
910 /* The first identifier is an optional exception name */
911 if (strstr(ret, "!skip-to-end")) {
912 freeException(ret);
913 ret = MAL_SUCCEED;
914 stkpc = mb->stop;
915 continue;
916 }
917 /*
918 * Exceptions are caught based on their name, which is part of the
919 * exception message. The ANYexception variable catches all.
920 */
921 exceptionVar = -1;
922 msg = strchr(ret, ':');
923 if (msg) {
924 exceptionVar = findVariableLength(mb, ret, (int)(msg - ret));
925 }
926 if (exceptionVar == -1)
927 exceptionVar = findVariable(mb, "ANYexception");
928
929 /* unknown exceptions lead to propagation */
930 if (exceptionVar == -1) {
931 if (cntxt->querytimeout && mb->starttime && GDKusec()- mb->starttime > cntxt->querytimeout)
932 ret= createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
933 stkpc = mb->stop;
934 continue;
935 }
936 /* assure correct variable type */
937 if (getVarType(mb, exceptionVar) == TYPE_str) {
938 /* watch out for concurrent access */
939 MT_lock_set(&mal_contextLock);
940 v = &stk->stk[exceptionVar];
941 if (v->val.sval)
942 freeException(v->val.sval); /* old exception*/
943 v->vtype = TYPE_str;
944 v->val.sval = ret;
945 v->len = strlen(v->val.sval);
946 ret = MAL_SUCCEED;
947 MT_lock_unset(&mal_contextLock);
948 } else {
949 mnstr_printf(cntxt->fdout, "%s", ret);
950 freeException(ret);
951 ret = MAL_SUCCEED;
952 }
953 /* position yourself at the catch instruction for further decisions */
954 /* skipToCatch(exceptionVar,@2,@3) */
955#ifndef NDEBUG
956 if (stk->cmd == 'C') {
957 stk->cmd = 'n';
958 mdbStep(cntxt, mb, stk, stkpc);
959 if (stk->cmd == 'x' ) {
960 stkpc = mb->stop;
961 continue;
962 }
963 }
964#endif
965 /* skip to catch block or end */
966 for (; stkpc < mb->stop; stkpc++) {
967 InstrPtr l = getInstrPtr(mb, stkpc);
968 if (l->barrier == CATCHsymbol) {
969 int j;
970 for (j = 0; j < l->retc; j++)
971 if (getArg(l, j) == exceptionVar)
972 break;
973 else if (strcmp(getArgName(mb, l, j), "ANYexception") == 0)
974 break;
975 if (j < l->retc)
976 break;
977 }
978 }
979 if (stkpc == mb->stop) {
980 if (cntxt->querytimeout && mb->starttime && GDKusec()- mb->starttime > cntxt->querytimeout){
981 ret= createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
982 stkpc = mb->stop;
983 }
984 continue;
985 }
986 pci = getInstrPtr(mb, stkpc);
987 }
988
989 /*
990 * After the expression has been evaluated we should check for
991 * a possible change in the control flow.
992 */
993 switch (pci->barrier) {
994 case BARRIERsymbol:
995 v = &stk->stk[getDestVar(pci)];
996 /* skip to end of barrier, depends on the type */
997 switch (v->vtype) {
998 case TYPE_bit:
999 if (v->val.btval == FALSE || is_bit_nil(v->val.btval))
1000 stkpc = pci->jump;
1001 break;
1002 case TYPE_bte:
1003 if (is_bte_nil(v->val.btval))
1004 stkpc = pci->jump;
1005 break;
1006 case TYPE_oid:
1007 if (is_oid_nil(v->val.oval))
1008 stkpc = pci->jump;
1009 break;
1010 case TYPE_sht:
1011 if (is_sht_nil(v->val.shval))
1012 stkpc = pci->jump;
1013 break;
1014 case TYPE_int:
1015 if (is_int_nil(v->val.ival))
1016 stkpc = pci->jump;
1017 break;
1018 case TYPE_lng:
1019 if (is_lng_nil(v->val.lval))
1020 stkpc = pci->jump;
1021 break;
1022#ifdef HAVE_HGE
1023 case TYPE_hge:
1024 if (is_hge_nil(v->val.hval))
1025 stkpc = pci->jump;
1026 break;
1027#endif
1028 case TYPE_flt:
1029 if (is_flt_nil(v->val.fval))
1030 stkpc = pci->jump;
1031 break;
1032 case TYPE_dbl:
1033 if (is_dbl_nil(v->val.dval))
1034 stkpc = pci->jump;
1035 break;
1036 case TYPE_str:
1037 if (GDK_STRNIL(v->val.sval))
1038 stkpc = pci->jump;
1039 break;
1040 default:
1041 ret = createException(MAL,"mal.interpreter", "%s: Unknown barrier type", getVarName(mb, getDestVar(pci)));
1042 }
1043 stkpc++;
1044 break;
1045 case LEAVEsymbol:
1046 case REDOsymbol:
1047 v = &stk->stk[getDestVar(pci)];
1048 /* skip to end of barrier, depending on the type */
1049 switch (v->vtype) {
1050 case TYPE_bit:
1051 if (v->val.btval == TRUE )
1052 stkpc = pci->jump;
1053 else
1054 stkpc++;
1055 break;
1056 case TYPE_str:
1057 if (!GDK_STRNIL(v->val.sval))
1058 stkpc = pci->jump;
1059 else
1060 stkpc++;
1061 break;
1062 case TYPE_oid:
1063 if (!is_oid_nil(v->val.oval))
1064 stkpc = pci->jump;
1065 else
1066 stkpc++;
1067 break;
1068 case TYPE_sht:
1069 if (!is_sht_nil(v->val.shval))
1070 stkpc = pci->jump;
1071 else
1072 stkpc++;
1073 break;
1074 case TYPE_int:
1075 if (!is_int_nil(v->val.ival))
1076 stkpc = pci->jump;
1077 else
1078 stkpc++;
1079 break;
1080 case TYPE_bte:
1081 if (!is_bte_nil(v->val.btval))
1082 stkpc = pci->jump;
1083 else
1084 stkpc++;
1085 break;
1086 case TYPE_lng:
1087 if (!is_lng_nil(v->val.lval))
1088 stkpc = pci->jump;
1089 else
1090 stkpc++;
1091 break;
1092#ifdef HAVE_HGE
1093 case TYPE_hge:
1094 if (!is_hge_nil(v->val.hval))
1095 stkpc = pci->jump;
1096 else
1097 stkpc++;
1098 break;
1099#endif
1100 case TYPE_flt:
1101 if (!is_flt_nil(v->val.fval))
1102 stkpc = pci->jump;
1103 else
1104 stkpc++;
1105 break;
1106 case TYPE_dbl:
1107 if (!is_dbl_nil(v->val.dval))
1108 stkpc = pci->jump;
1109 else
1110 stkpc++;
1111 break;
1112 default:
1113 break;
1114 }
1115 break;
1116 case CATCHsymbol:
1117 /* catch blocks are skipped unless
1118 searched for explicitly*/
1119 if (exceptionVar < 0) {
1120 stkpc = pci->jump;
1121 break;
1122 }
1123 exceptionVar = -1;
1124 stkpc++;
1125 break;
1126 case EXITsymbol:
1127 if (getDestVar(pci) == exceptionVar)
1128 exceptionVar = -1;
1129 stkpc++;
1130 break;
1131 case RAISEsymbol:
1132 exceptionVar = getDestVar(pci);
1133 //freeException(ret);
1134 ret = MAL_SUCCEED;
1135 if (getVarType(mb, getDestVar(pci)) == TYPE_str) {
1136 char nme[256];
1137 snprintf(nme,256,"%s.%s[%d]", getModuleId(getInstrPtr(mb,0)), getFunctionId(getInstrPtr(mb,0)), stkpc);
1138 ret = createException(MAL, nme, "%s", stk->stk[getDestVar(pci)].val.sval);
1139 }
1140 /* skipToCatch(exceptionVar, @2, stk) */
1141#ifndef NDEBUG
1142 if (stk->cmd == 'C') {
1143 stk->cmd = 'n';
1144 mdbStep(cntxt, mb, stk, stkpc);
1145 if (stk->cmd == 'x' ) {
1146 stkpc = mb->stop;
1147 continue;
1148 }
1149 }
1150#endif
1151 /* skip to catch block or end */
1152 for (; stkpc < mb->stop; stkpc++) {
1153 InstrPtr l = getInstrPtr(mb, stkpc);
1154 if (l->barrier == CATCHsymbol) {
1155 int j;
1156 for (j = 0; j < l->retc; j++)
1157 if (getArg(l, j) == exceptionVar)
1158 break;
1159 else if (strcmp(getArgName(mb, l, j), "ANYexception") == 0)
1160 break;
1161 if (j < l->retc)
1162 break;
1163 }
1164 }
1165 if (stkpc == mb->stop) {
1166 runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
1167 runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb,0), &runtimeProfileFunction);
1168 break;
1169 }
1170 if (stkpc == mb->stop)
1171 ret = mb->errors = createMalException(mb, stkpc, TYPE,
1172 "Exception raised\n");
1173 break;
1174 case YIELDsymbol: /* to be defined */
1175 if( startedProfileQueue)
1176 runtimeProfileFinish(cntxt, mb, stk);
1177 if ( backup != backups) GDKfree(backup);
1178 if ( garbage != garbages) GDKfree(garbage);
1179 return yieldFactory(mb, pci, stkpc);
1180 case RETURNsymbol:
1181 /* Return from factory involves cleanup */
1182
1183 if (getInstrPtr(mb, 0)->token == FACTORYsymbol) {
1184 yieldResult(mb, pci, stkpc);
1185 shutdownFactory(cntxt, mb);
1186 } else {
1187 /* a fake multi-assignment */
1188 if (env != NULL && pcicaller != NULL) {
1189 InstrPtr pp = pci;
1190 pci = pcicaller;
1191 for (i = 0; i < pci->retc; i++) {
1192 rhs = &stk->stk[pp->argv[i]];
1193 lhs = &env->stk[pci->argv[i]];
1194 if(VALcopy(lhs, rhs) == NULL) {
1195 ret = createException(MAL, "mal.interpreter", MAL_MALLOC_FAIL);
1196 break;
1197 } else if (lhs->vtype == TYPE_bat)
1198 BBPretain(lhs->val.bval);
1199 }
1200 if (garbageControl(getInstrPtr(mb, 0)))
1201 garbageCollector(cntxt, mb, stk, TRUE);
1202 /* reset the clock */
1203 runtimeProfileExit(cntxt, mb, stk, pp, &runtimeProfile);
1204 runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb,0), &runtimeProfileFunction);
1205 }
1206 }
1207 stkpc = mb->stop;
1208 continue;
1209 default:
1210 stkpc++;
1211 }
1212 if (cntxt->querytimeout && mb->starttime && GDKusec()- mb->starttime > cntxt->querytimeout){
1213 if (ret == MAL_SUCCEED)
1214 ret= createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
1215 stkpc= mb->stop;
1216 }
1217 }
1218
1219 /* if we could not find the exception variable, cascade a new one */
1220 if (exceptionVar >= 0) {
1221 char nme[256];
1222 snprintf(nme,256,"%s.%s[%d]", getModuleId(getInstrPtr(mb,0)), getFunctionId(getInstrPtr(mb,0)), stkpc);
1223 if (ret != MAL_SUCCEED) {
1224 str new, n;
1225 n = createException(MAL,nme,"exception not caught");
1226 if (n) {
1227 new = GDKzalloc(strlen(ret) + strlen(n) +16);
1228 if (new){
1229 strcpy(new, ret);
1230 if( new[strlen(new)-1] != '\n')
1231 strcat(new,"\n");
1232 strcat(new,"!");
1233 strcat(new,n);
1234 freeException(n);
1235 freeException(ret);
1236 ret = new;
1237 }
1238 }
1239 } else {
1240 ret = createException(MAL, nme, "Exception not caught");
1241 }
1242 }
1243 if( startedProfileQueue)
1244 runtimeProfileFinish(cntxt, mb, stk);
1245 if ( backup != backups) GDKfree(backup);
1246 if ( garbage != garbages) GDKfree(garbage);
1247 return ret;
1248}
1249
1250
1251/*
1252 * MAL API
1253 * The linkage between MAL interpreter and compiled C-routines
1254 * is kept as simple as possible.
1255 * Basically we distinguish four kinds of calling conventions:
1256 * CMDcall, FCNcall, FACcall, and PATcall.
1257 * The FCNcall indicates calling a MAL procedure, which leads
1258 * to a recursive call to the interpreter.
1259 *
1260 * CMDcall initiates calling a linked function, passing pointers
1261 * to the parameters and result variable, i.e. f(ptr a0,..., ptr aN)
1262 * The function returns a MAL-SUCCEED upon success and a pointer
1263 * to an exception string upon failure.
1264 * Failure leads to raise-ing an exception in the interpreter loop,
1265 * by either looking up the relevant exception message in the module
1266 * administration or construction of a standard string.
1267 *
1268 * The PATcall initiates a call which contains the MAL context,
1269 * i.e. f(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1270 * The mb provides access to the code definitions. It is primarilly
1271 * used by routines intended to manipulate the code base itself, such
1272 * as the optimizers. The Mal stack frame pointer provides access
1273 * to the values maintained. The arguments passed are offsets
1274 * into the stack frame rather than pointers to the actual value.
1275 *
1276 * BAT parameters require some care. Ideally, a BAT should not be kept
1277 * around long. This would mean that each time we access a BAT it has to be
1278 * pinned in memory and upon leaving the function, it is unpinned.
1279 * This degrades performance significantly.
1280 * After the parameters are fixed, we can safely free the destination
1281 * variable and re-initialize it to nil.
1282 *
1283 */
1284
1285/*
1286 * The type dispatching table in getArgReference can be removed if we
1287 * determine at compile time the address offset within a ValRecord.
1288 * We leave this optimization for the future, it leads to about 10%
1289 * improvement (100ms for 1M calls).
1290 *
1291 * Flow of control statements
1292 * Each assignment (function call) may be part of the initialization
1293 * of a barrier- block. In that case we have to test the
1294 * outcome of the operation and possibly skip the block altogether.
1295 * The latter is implemented as a linear scan for the corresponding
1296 * labeled statemtent. This might be optimized later.
1297 *
1298 * You can skip to a catch block by searching for the corresponding 'lab'
1299 * The return value should be set to pass the error automatically upon
1300 * reaching end of function block.
1301 */
1302
1303/*
1304 * Each time we enter a barrier block, we could keep its position in the
1305 * interpreter stack frame. It forms the starting point to issue a redo.
1306 * Unfortunately, this does not easily work in the presence of optimizers, which
1307 * may change the order/block structure. Therefore, we simple have to search
1308 * the beginning or ensure that during chkProgram the barrier/redo/leave/catch
1309 * jumps are re-established.
1310 *
1311 * Exception handling
1312 * Calling a built-in or user-defined routine may lead to an error or a
1313 * cached status message to be dealt with in MAL.
1314 * To improve error handling in MAL, an exception handling
1315 * scheme based on @sc{catch}-@sc{exit} blocks. The @sc{catch}
1316 * statement identifies a (string-valued) variable, which carries the
1317 * exception message from
1318 * the originally failed routine or @sc{raise} exception assignment.
1319 * During normal processing @sc{catch}-@sc{exit} blocks are simply skipped.
1320 * Upon receiving an exception status from a function call, we set the
1321 * exception variable and skip to the first associated @sc{catch}-@sc{exit}
1322 * block.
1323 * MAL interpretation then continues until it reaches the end of the block.
1324 * If no exception variable was defined, we should abandon the function
1325 * alltogether searching for a catch block at a higher layer.
1326 *
1327 * For the time being we have ignored cascaded/stacked exceptions.
1328 * The policy is to pass the first recognized exception to a context
1329 * in which it can be handled.
1330 *
1331 * Exceptions raised within a linked-in function requires some care.
1332 * First, the called procedure does not know anything about the MAL
1333 * interpreter context. Thus, we need to return all relevant information
1334 * upon leaving the linked library routine.
1335 *
1336 * Second, exceptional cases can be handled deeply in the recursion, where they
1337 * may also be handled, i.e. by issueing an GDKerror message. The upper layers
1338 * merely receive a negative integer value to indicate occurrence of an
1339 * error somewhere in the calling sequence.
1340 * We then have to also look into GDKerrbuf to see if there was
1341 * an error raised deeply inside the system.
1342 *
1343 * The policy is to require all C-functions to return a string-pointer.
1344 * Upon a successfull call, it is a NULL string. Otherwise it contains an
1345 * encoding of the exceptional state encountered. This message
1346 * starts with the exception identifer, followed by contextual details.
1347 */
1348
1349/*
1350 * Garbage collection
1351 * Garbage collection is relatively straightforward, because most values are
1352 * retained on the stackframe of an interpreter call. However, two storage
1353 * types and possibly user-defined type garbage collector definitions
1354 * require attention: BATs and strings.
1355 *
1356 * A key issue is to deal with temporary BATs in an efficient way.
1357 * References to bats in the buffer pool may cause dangling references
1358 * at the language level. This appears as soons as your share
1359 * a reference and delete the BAT from one angle. If not carefull, the
1360 * dangling pointer may subsequently be associated with another BAT
1361 *
1362 * All string values are private to the VALrecord, which means they
1363 * have to be freed explicitly before a MAL function returns.
1364 * The first step is to always safe the destination variable
1365 * before a function call is made.
1366 */
1367void garbageElement(Client cntxt, ValPtr v)
1368{
1369 (void) cntxt;
1370 if (v->vtype == TYPE_str) {
1371 if (v->val.sval) {
1372 GDKfree(v->val.sval);
1373 v->val.sval = NULL;
1374 }
1375 v->len = 0;
1376 } else if (v->vtype == TYPE_bat) {
1377 /*
1378 * All operations are responsible to properly set the
1379 * reference count of the BATs being produced or destroyed.
1380 * The libraries should not leave the
1381 * physical reference count being set. This is only
1382 * allowed during the execution of a GDK operation.
1383 * All references should be logical.
1384 */
1385 bat bid = v->val.bval;
1386 /* printf("garbage collecting: %d lrefs=%d refs=%d\n",
1387 bid, BBP_lrefs(bid),BBP_refs(bid));*/
1388 v->val.bval = bat_nil;
1389 if (is_bat_nil(bid))
1390 return;
1391 if (!BBP_lrefs(bid))
1392 return;
1393 BBPrelease(bid);
1394 } else if (0 < v->vtype && v->vtype < MAXATOMS && ATOMextern(v->vtype)) {
1395 if (v->val.pval)
1396 GDKfree(v->val.pval);
1397 v->val.pval = 0;
1398 v->len = 0;
1399 }
1400}
1401
1402/*
1403 * Before we return from the interpreter, we should free all
1404 * dynamically allocated objects and adjust the BAT reference counts.
1405 * Early experience shows that for small stack frames the overhead
1406 * is about 200 ms for a 1M function call loop (tst400e). This means that
1407 * for the time being we do not introduce more complex garbage
1408 * administration code.
1409 *
1410 * Also note that for top-level stack frames (no environment available),
1411 * we should retain the value stack because it acts as a global variables.
1412 * This situation is indicated by the 'global' in the stack frame.
1413 * Upon termination of the session, the stack should be cleared.
1414 * Beware that variables may be know polymorphic, their actual
1415 * type should be saved for variables that recide on a global
1416 * stack frame.
1417 */
1418void garbageCollector(Client cntxt, MalBlkPtr mb, MalStkPtr stk, int flag)
1419{
1420 int k;
1421 ValPtr v;
1422
1423#ifdef STACKTRACE
1424 if (cntxt) {
1425 mnstr_printf(cntxt->fdout, "#--->stack before garbage collector\n");
1426 printStack(cntxt->fdout, mb, stk, 0);
1427 }
1428#endif
1429 assert(mb->vtop <= mb->vsize);
1430 (void) flag;
1431 for (k = 0; k < mb->vtop; k++) {
1432 // if (isVarCleanup(mb, k) ){
1433 garbageElement(cntxt, v = &stk->stk[k]);
1434 v->vtype = TYPE_int;
1435 v->val.ival = int_nil;
1436 // }
1437 }
1438#ifdef STACKTRACE
1439 if (cntxt) {
1440 mnstr_printf(cntxt->fdout, "#-->stack after garbage collector\n");
1441 printStack(cntxt->fdout, mb, stk, 0);
1442 }
1443#else
1444 (void)cntxt;
1445#endif
1446}
1447