| 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 | * For documentation see website |
| 12 | */ |
| 13 | #include "monetdb_config.h" |
| 14 | #include "mal_factory.h" |
| 15 | #include "mal_instruction.h" |
| 16 | #include "mal_interpreter.h" |
| 17 | #include "mal_function.h" |
| 18 | #include "mal_exception.h" |
| 19 | #include "mal_session.h" |
| 20 | #include "mal_namespace.h" |
| 21 | #include "mal_private.h" |
| 22 | |
| 23 | typedef struct { |
| 24 | int id; /* unique plant number */ |
| 25 | MalBlkPtr factory; |
| 26 | MalStkPtr stk; /* private state */ |
| 27 | int pc; /* where we are */ |
| 28 | int inuse; /* able to handle it */ |
| 29 | int next; /* next plant of same factory */ |
| 30 | int policy; /* flags to control behavior */ |
| 31 | |
| 32 | Client client; /* who called it */ |
| 33 | MalBlkPtr caller; /* from routine */ |
| 34 | MalStkPtr env; /* with the stack */ |
| 35 | InstrPtr pci; /* with the instruction */ |
| 36 | } PlantRecord, *Plant; |
| 37 | |
| 38 | #define MAXPLANTS 256 |
| 39 | static PlantRecord plants[MAXPLANTS]; |
| 40 | static int lastPlant= 0; |
| 41 | static int plantId = 1; |
| 42 | |
| 43 | mal_export Plant newPlant(MalBlkPtr mb); |
| 44 | |
| 45 | |
| 46 | static int |
| 47 | findPlant(MalBlkPtr mb){ |
| 48 | int i; |
| 49 | for(i=0; i<lastPlant; i++) |
| 50 | if( plants[i].factory == mb) |
| 51 | return i; |
| 52 | return -1; |
| 53 | } |
| 54 | |
| 55 | str |
| 56 | runFactory(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr stk, InstrPtr pci) |
| 57 | { |
| 58 | Plant pl=0; |
| 59 | int firstcall= TRUE, i, k; |
| 60 | InstrPtr psig = getInstrPtr(mb, 0); |
| 61 | ValPtr lhs, rhs; |
| 62 | char cmd; |
| 63 | str msg; |
| 64 | |
| 65 | #ifdef DEBUG_MAL_FACTORY |
| 66 | fprintf(stderr, "#factoryMgr called\n" ); |
| 67 | #endif |
| 68 | /* the lookup can be largely avoided by handing out the index |
| 69 | upon factory definition. todo |
| 70 | Alternative is to move them to the front |
| 71 | */ |
| 72 | for(i=0; i< lastPlant; i++) |
| 73 | if( plants[i].factory == mb){ |
| 74 | if(i > 0 && i< lastPlant ){ |
| 75 | PlantRecord prec= plants[i-1]; |
| 76 | plants[i-1] = plants[i]; |
| 77 | plants[i]= prec; |
| 78 | i--; |
| 79 | } |
| 80 | pl= plants+i; |
| 81 | firstcall= FALSE; |
| 82 | break; |
| 83 | } |
| 84 | if (pl == 0) { |
| 85 | /* compress the plant table*/ |
| 86 | for(k=i=0;i<=lastPlant; i++) |
| 87 | if( plants[i].inuse) |
| 88 | plants[k++]= plants[i]; |
| 89 | lastPlant = k; |
| 90 | /* initialize a new plant using the owner policy */ |
| 91 | pl = newPlant(mb); |
| 92 | if (pl == NULL) |
| 93 | throw(MAL, "factory.new" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 94 | } |
| 95 | /* |
| 96 | * We have found a factory to process the request. |
| 97 | * Let's call it as a synchronous action, without concern on parallelism. |
| 98 | */ |
| 99 | /* remember context */ |
| 100 | pl->client = cntxt; |
| 101 | pl->caller = mbcaller; |
| 102 | pl->env = stk; |
| 103 | pl->pci = pci; |
| 104 | pl->inuse = 1; |
| 105 | /* inherit debugging */ |
| 106 | cmd = stk->cmd; |
| 107 | if ( pl->stk == NULL) |
| 108 | throw(MAL, "factory.new" , "internal error, stack frame missing" ); |
| 109 | |
| 110 | /* copy the calling arguments onto the stack |
| 111 | of the factory */ |
| 112 | i = psig->retc; |
| 113 | for (k = pci->retc; i < pci->argc; i++, k++) { |
| 114 | lhs = &pl->stk->stk[psig->argv[k]]; |
| 115 | /* variable arguments ? */ |
| 116 | if (k == psig->argc - 1) |
| 117 | k--; |
| 118 | |
| 119 | rhs = &pl->env->stk[getArg(pci, i)]; |
| 120 | if (VALcopy(lhs, rhs) == NULL) |
| 121 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 122 | if( lhs->vtype == TYPE_bat ) |
| 123 | BBPretain(lhs->val.bval); |
| 124 | } |
| 125 | if (mb->errors) |
| 126 | throw(MAL, "factory.call" , PROGRAM_GENERAL); |
| 127 | if (firstcall ){ |
| 128 | /* initialize the stack */ |
| 129 | for(i= psig->argc; i< mb->vtop; i++) { |
| 130 | lhs = &pl->stk->stk[i]; |
| 131 | if( isVarConstant(mb,i) > 0 ){ |
| 132 | if( !isVarDisabled(mb,i)){ |
| 133 | rhs = &getVarConstant(mb,i); |
| 134 | if (VALcopy(lhs,rhs) == NULL) |
| 135 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 136 | } |
| 137 | } else{ |
| 138 | lhs->vtype = getVarGDKType(mb,i); |
| 139 | lhs->val.pval = 0; |
| 140 | lhs->len = 0; |
| 141 | } |
| 142 | } |
| 143 | pl->stk->stkbot= mb->vtop; /* stack already initialized */ |
| 144 | msg = runMAL(cntxt, mb, 0, pl->stk); |
| 145 | } else { |
| 146 | msg = reenterMAL(cntxt, mb, pl->pc, -1, pl->stk); |
| 147 | } |
| 148 | /* propagate change in debugging status */ |
| 149 | if (cmd && pl->stk && pl->stk->cmd != cmd && cmd != 'x') |
| 150 | for (; stk; stk = stk->up) |
| 151 | stk->cmd = pl->stk->cmd; |
| 152 | return msg; |
| 153 | } |
| 154 | /* |
| 155 | * The shortcut operator for factory calls assumes that the user is |
| 156 | * not interested in the results produced. |
| 157 | */ |
| 158 | str |
| 159 | callFactory(Client cntxt, MalBlkPtr mb, ValPtr argv[], char flag){ |
| 160 | Plant pl; |
| 161 | InstrPtr psig = getInstrPtr(mb, 0); |
| 162 | int i; |
| 163 | ValPtr lhs,rhs; |
| 164 | MalStkPtr stk; |
| 165 | str ret; |
| 166 | |
| 167 | i= findPlant(mb); |
| 168 | if( i< 0) { |
| 169 | /* first call? prepare the factory */ |
| 170 | pl = newPlant(mb); |
| 171 | if (pl == NULL) |
| 172 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 173 | /* remember context, which does not exist. */ |
| 174 | pl->client = cntxt; |
| 175 | pl->caller = 0; |
| 176 | pl->env = 0; |
| 177 | pl->pci = 0; |
| 178 | pl->inuse = 1; |
| 179 | stk = pl->stk; |
| 180 | /* initialize the stack */ |
| 181 | stk->stktop= mb->vtop; |
| 182 | stk->stksize= mb->vsize; |
| 183 | stk->blk= mb; |
| 184 | stk->up = 0; |
| 185 | stk->cmd= flag; |
| 186 | /* initialize the stack */ |
| 187 | for(i= psig->argc; i< mb->vtop; i++) |
| 188 | if( isVarConstant(mb,i) > 0 ){ |
| 189 | lhs = &stk->stk[i]; |
| 190 | rhs = &getVarConstant(mb,i); |
| 191 | if (VALcopy(lhs,rhs) == NULL) |
| 192 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 193 | } else { |
| 194 | lhs = &stk->stk[i]; |
| 195 | lhs->vtype = getVarGDKType(mb,i); |
| 196 | } |
| 197 | pl->stk= stk; |
| 198 | } else { |
| 199 | pl= plants+i; |
| 200 | /* |
| 201 | * When you re-enter the factory the old arguments should be |
| 202 | * released to make room for the new ones. |
| 203 | */ |
| 204 | for (i = psig->retc; i < psig->argc; i++) { |
| 205 | lhs = &pl->stk->stk[psig->argv[i]]; |
| 206 | if( lhs->vtype == TYPE_bat ) |
| 207 | BBPrelease(lhs->val.bval); |
| 208 | } |
| 209 | } |
| 210 | /* copy the calling arguments onto the stack of the factory */ |
| 211 | i = psig->retc; |
| 212 | for (i = psig->retc; i < psig->argc; i++) { |
| 213 | lhs = &pl->stk->stk[psig->argv[i]]; |
| 214 | if (VALcopy(lhs, argv[i]) == NULL) |
| 215 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 216 | if( lhs->vtype == TYPE_bat ) |
| 217 | BBPretain(lhs->val.bval); |
| 218 | } |
| 219 | ret= reenterMAL(cntxt, mb, pl->pc, -1, pl->stk); |
| 220 | /* garbage collect the string arguments, these positions |
| 221 | will simply be overwritten the next time. |
| 222 | for (i = psig->retc; i < psig->argc; i++) |
| 223 | garbageElement(lhs = &pl->stk->stk[psig->argv[i]]); |
| 224 | */ |
| 225 | return ret; |
| 226 | } |
| 227 | /* |
| 228 | * A new plant is constructed. The properties of the factory |
| 229 | * should be known upon compile time. They are retrieved from |
| 230 | * the signature of the factory definition. |
| 231 | */ |
| 232 | Plant |
| 233 | newPlant(MalBlkPtr mb) |
| 234 | { |
| 235 | Plant p, plim; |
| 236 | MalStkPtr stk; |
| 237 | |
| 238 | plim = plants + lastPlant; |
| 239 | for (p = plants; p < plim && p->factory; p++) |
| 240 | ; |
| 241 | stk = newGlobalStack(mb->vsize); |
| 242 | if (lastPlant == MAXPLANTS || stk == NULL){ |
| 243 | if( stk) GDKfree(stk); |
| 244 | return 0; |
| 245 | } |
| 246 | if (p == plim) |
| 247 | lastPlant++; |
| 248 | p->factory = mb; |
| 249 | p->id = plantId++; |
| 250 | |
| 251 | p->pc = 1; /* where we start */ |
| 252 | p->stk = stk; |
| 253 | p->stk->blk = mb; |
| 254 | p->stk->keepAlive = TRUE; |
| 255 | return p; |
| 256 | } |
| 257 | |
| 258 | /* |
| 259 | * Upon reaching the yield operator, the factory is |
| 260 | * suspended until the next request arrives. |
| 261 | * The information in the target list should be delivered |
| 262 | * to the caller stack frame. |
| 263 | */ |
| 264 | int |
| 265 | yieldResult(MalBlkPtr mb, InstrPtr p, int pc) |
| 266 | { |
| 267 | Plant pl, plim = plants + lastPlant; |
| 268 | ValPtr lhs, rhs; |
| 269 | int i; |
| 270 | |
| 271 | (void) p; |
| 272 | (void) pc; |
| 273 | for (pl = plants; pl < plim; pl++) |
| 274 | if (pl->factory == mb ) { |
| 275 | if( pl->env == NULL) |
| 276 | return(int) (pl-plants); |
| 277 | for (i = 0; i < p->retc; i++) { |
| 278 | #ifdef DEBUG_MAL_FACTORY |
| 279 | fprintf(stderr,"#lhs %d rhs %d\n" , getArg(pl->pci, i), getArg(p, i)); |
| 280 | #endif |
| 281 | rhs = &pl->stk->stk[getArg(p, i)]; |
| 282 | lhs = &pl->env->stk[getArg(pl->pci, i)]; |
| 283 | if (VALcopy(lhs, rhs) == NULL) |
| 284 | return -1; |
| 285 | } |
| 286 | return (int) (pl-plants); |
| 287 | } |
| 288 | return -1; |
| 289 | } |
| 290 | |
| 291 | str |
| 292 | yieldFactory(MalBlkPtr mb, InstrPtr p, int pc) |
| 293 | { |
| 294 | Plant pl; |
| 295 | int i; |
| 296 | |
| 297 | i = yieldResult(mb, p, pc); |
| 298 | |
| 299 | if (i>=0) { |
| 300 | pl = plants+i; |
| 301 | pl->pc = pc + 1; |
| 302 | pl->client = NULL; |
| 303 | pl->caller = NULL; |
| 304 | pl->pci = NULL; |
| 305 | pl->env = NULL; |
| 306 | return MAL_SUCCEED; |
| 307 | } |
| 308 | throw(MAL, "factory.yield" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
| 309 | } |
| 310 | |
| 311 | /* |
| 312 | * A return from a factory body implies removal of |
| 313 | * all state information. |
| 314 | * This code should also prepare for handling factories |
| 315 | * that are still running threads in parallel. |
| 316 | */ |
| 317 | |
| 318 | str |
| 319 | shutdownFactory(Client cntxt, MalBlkPtr mb) |
| 320 | { |
| 321 | Plant pl, plim; |
| 322 | |
| 323 | plim = plants + lastPlant; |
| 324 | for (pl = plants; pl < plim; pl++) |
| 325 | if (pl->factory == mb) { |
| 326 | /* MSresetVariables(mb, pl->stk, 0);*/ |
| 327 | /* freeStack(pl->stk); there may be a reference?*/ |
| 328 | /* we are inside the body of the factory and about to return */ |
| 329 | pl->factory = 0; |
| 330 | if (pl->stk) |
| 331 | pl->stk->keepAlive = FALSE; |
| 332 | if ( pl->stk) { |
| 333 | garbageCollector(cntxt, mb, pl->stk,TRUE); |
| 334 | GDKfree(pl->stk); |
| 335 | } |
| 336 | pl->stk=0; |
| 337 | pl->pc = 0; |
| 338 | pl->inuse = 0; |
| 339 | pl->client = NULL; |
| 340 | pl->caller = NULL; |
| 341 | pl->pci = NULL; |
| 342 | pl->env = NULL; |
| 343 | pl->client = NULL; |
| 344 | pl->caller = NULL; |
| 345 | pl->env= NULL; |
| 346 | pl->pci = NULL; |
| 347 | } |
| 348 | return MAL_SUCCEED; |
| 349 | } |
| 350 | |
| 351 | str |
| 352 | shutdownFactoryByName(Client cntxt, Module m, str nme){ |
| 353 | Plant pl, plim; |
| 354 | InstrPtr p; |
| 355 | Symbol s; |
| 356 | |
| 357 | plim = plants + lastPlant; |
| 358 | for (pl = plants; pl < plim; pl++) |
| 359 | if (pl->factory ) { |
| 360 | MalStkPtr stk; |
| 361 | |
| 362 | p= getInstrPtr(pl->factory,0); |
| 363 | if( strcmp(nme, getFunctionId(p)) != 0) continue; |
| 364 | s = findSymbolInModule(m, nme ); |
| 365 | if (s == NULL){ |
| 366 | throw(MAL, "factory.remove" , |
| 367 | OPERATION_FAILED " SQL entry '%s' not found" , |
| 368 | putName(nme)); |
| 369 | } |
| 370 | stk = pl->stk; |
| 371 | MSresetVariables(cntxt, pl->factory, stk, 0); |
| 372 | shutdownFactory(cntxt, pl->factory); |
| 373 | freeStack(stk); |
| 374 | deleteSymbol(m,s); |
| 375 | return MAL_SUCCEED; |
| 376 | } |
| 377 | return MAL_SUCCEED; |
| 378 | } |
| 379 | |
| 380 | void mal_factory_reset(void) |
| 381 | { |
| 382 | Plant pl, plim; |
| 383 | |
| 384 | plim = plants + lastPlant; |
| 385 | for (pl = plants; pl < plim; pl++){ |
| 386 | /* MSresetVariables(mb, pl->stk, 0);*/ |
| 387 | /* freeStack(pl->stk); there may be a reference?*/ |
| 388 | /* we are inside the body of the factory and about to return */ |
| 389 | if (pl->stk) { |
| 390 | pl->stk->keepAlive = FALSE; |
| 391 | garbageCollector(NULL, pl->factory, pl->stk, TRUE); |
| 392 | GDKfree(pl->stk); |
| 393 | } |
| 394 | pl->factory = 0; |
| 395 | pl->stk=0; |
| 396 | pl->pc = 0; |
| 397 | pl->inuse = 0; |
| 398 | pl->client = NULL; |
| 399 | pl->caller = NULL; |
| 400 | pl->pci = NULL; |
| 401 | pl->env = NULL; |
| 402 | pl->client = NULL; |
| 403 | pl->caller = NULL; |
| 404 | pl->env= NULL; |
| 405 | pl->pci = NULL; |
| 406 | } |
| 407 | plantId = 1; |
| 408 | lastPlant = 0; |
| 409 | } |
| 410 | |