📄 mal_factory.mx
字号:
@' The contents of this file are subject to the MonetDB Public License@' Version 1.1 (the "License"); you may not use this file except in@' compliance with the License. You may obtain a copy of the License at@' http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html@'@' Software distributed under the License is distributed on an "AS IS"@' basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the@' License for the specific language governing rights and limitations@' under the License.@'@' The Original Code is the MonetDB Database System.@'@' The Initial Developer of the Original Code is CWI.@' Portions created by CWI are Copyright (C) 1997-2007 CWI.@' All Rights Reserved.@a M. Kersten@v 0.0@+ Factories A convenient programming construct is the co-routine, whichis specified as an ordinary function, but maintains itsown state between calls, and permits re-entry other thanby the first statement. The random generator example is used to illustrate its definition and use.@examplefactory random(seed:int,limit:int):int; rnd:=seed; lim:= limit;barrier lim; leave lim:= lim-1; rnd:= rnd*125; yield rnd:= rnd % 32676; redo lim;exit lim;end random;@end exampleThe first time this factory is called, a factory plantis created in the local system to handle the requests.The plant carries the stack frame and synchronizes access.In this case it initializes the generator. The random number is generated and @sc{yield} asa result of the call. The factory plant is then put to sleep.The second call received by the factory wakes it up at thepoint where it went to sleep. In this case it willfind a @sc{redo} statement and produces the next random number.Note that also in this case a seed and limit value areexpected, but they are ignored in the body.This factory can be called upon to generate at most 'limit'random numbers using the 'seed' to initialize the generator.Thereafter it is being removed, i.e. reset to the original state.A cooperative group of factories can be readily constructed.For example, assume we would like therandom factories to respond to both @sc{random(seed,limit)} and @sc{random()}. This can be defined as follows:@examplefactory random(seed:int,limit:int):int; rnd:=seed; lim:= limit;barrier lim; leave lim:= lim-1; rnd:= rnd*125; yield rnd:= rnd % 32676; redo lim;exit lim;end random;factory random():int;barrier forever:=true; yield random(0,0); redo forever;exit forever;end random;@end example@- Factory OwnershipFor simple cases, e.g. implementation of a random function,it suffices to ensure that the state is secured between calls.But, in a database context there are multiple clientsactive. This means we have to be more precise on the relationshipbetween a co-routine and the client for which it works.The co-routine concept researched in Monet 5 is the notion of a 'factory',which consists of 'factory plants' at possibly different locations andwith different policies to handle client requests.Factory management is limited to its owner, which is derived from the modulein which it is placed. By default Admin is the owner of all modules.The factory produces elements for multiple clients.Sharing the factory state or even remote processing is up to the factory owner.They are set through properties for the factory plant.The default policy is to instantiate one sharedplant for each factory. If necessary, the factorycan keep track of a client list to differentiatethe states.A possible implementation would be:@examplefactory random(seed:int,clientid:int):int; clt:= bat.new(:int,:int); bat.insert(clt,clientid,seed);barrier always:=true; rnd:= algebra.find(clt,clientid);catch rnd; #failed to find client bat.insert(clt,clientid,seed); rnd:= algebra.find(clt,clientid);exit rnd; rnd:= rnd * 125; rnd:= rnd % 32676; algebra.replace(clt,clientid,rnd); yield rnd; redo always;exit always;end random;@end exampleThe operators to built client aware factories are,@sc{factory.getCaller()}, which returns a client index, @sc{factory.getModule()} and @sc{factory.getFunction()}, which returns the identity of scope enclosed.To illustrate, the client specific random generatorcan be shielded using the factory:@examplefactory random(seed:int):int;barrier always:=true; clientid:= factory.getCaller(); yield user.random(seed, clientid); redo always;exit always;end random;@end example@- Complex FactoriesOne interesting use of the factory scheme is to modela volcano-style query processor. Each node in the querytree is an iterator that calls upon the operands to producea chunk, which are combined into a new chunk for consumptionof the parent. The prototypical join(R,S) query illustrates it.The plan does not test for all boundary conditions, it merelyimplements a nested loop. The end of a sequence is identifiedby a NIL chunk.@examplefactory query(); Left:= sql.bind("relationA"); Right:= sql.bind("relationB"); rc:= sql.joinStep(Left,Right);barrier rc!= nil; io.print(rc); rc:= sql.joinStep(Left,Right); redo rc!= nil;exit rc;end query;#nested loop joinfactory sql.joinStep(Left:bat[:any,:any],Right:bat[:any,:any]):bat[:any,:any]; lc:= bat.chunkStep(Left);barrier outer:= lc != nil; rc:= bat.chunkStep(Right); barrier inner:= rc != nil; chunk:= algebra.join(lc,rc); yield chunk; rc:= bat.chunkStep(Right); redo inner:= rc != nil; exit inner; lc:= bat.chunkStep(Left); redo outer:= lc != nil;exit outer; # we have seen everything return nil;end joinStep;#factory for left branchfactory chunkStepL(L:bat[:any,:any]):bat[:any,:any]; i:= 0; j:= 20; cnt:= algebra.count(L);barrier outer:= j<cnt; chunk:= algebra.slice(L,i,j); i:= j; j:= i+ 20; yield chunk; redo loop:= j<cnt;exit outer; # send last portion chunk:= algebra.slice(L,i,cnt); yielD chunk; return nil;end chunkStep;#factory for right legfactory chunkStepR(L:bat[:any,:any]):bat[:any,:any];@end exampleSo far we haven;t re-used the pattern that both legs areidentical. This could be modeled by a generic chunk factory.Choosing a new factory for each query steps reduces the administrative overhead.@- Materialized ViewsAn area where factories might be useful are supportfor materialized views, i.e. the result of a queryis retained for ease of access.A simple strategy is to prepare the result onceand return it on each successive call. Provided the arguments have not been changed.For example:@examplefactory view1(l:int, h:int):bat[:oid,:str]; a:bat[:oid,:int]:= bbp.bind("emp","age"); b:bat[:oid,:str]:= bbp.bind("emp","name");barrier always := true; lOld := l; hOld := h; c := algebra.select(a,l,h); d := algebra.semijoin(b,c);barrier available := true; yield d; leave available := calc.!=(lOld,l); leave available := calc.!=(hOld,h); redo available := true;exit available; redo always;exit always;end view1;@end exampleThe code should be extended to also check validity of the BATs.It requires a check against the last transaction identifier known.The Factory concept is still rather experimental and manyquestions should be considered, e.g.What is the lifetime of a factory? Does it persists afterall clients has disappeared?What additional control do you need? Can you throw anexception to a Factory?@{@h#ifndef _MAL_FACTORY_H#define _MAL_FACTORY_H/* #define DEBUG_MAL_FACTORY */#include "mal.h"#include "mal_client.h"#include "mal_instruction.h"#include "mal_interpreter.h"#include "mal_function.h"#include "mal_exception.h"#include "mal_session.h"#include "mal_debugger.h"#define POLICYimmediate 1 /* let user wait for reply */#define POLICYprivate 2 /* each client its own plants */mal_export str runFactory(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr stk, InstrPtr pci);mal_export int yieldResult(MalBlkPtr mb, InstrPtr p, int pc);mal_export str yieldFactory(MalBlkPtr mb, InstrPtr p, int pc);mal_export str finishFactory(MalBlkPtr mb, InstrPtr pp, int pc);mal_export str shutdownFactory(MalBlkPtr mb);mal_export str shutdownFactoryByName(Module m,str nme);mal_export str callFactory(Client cntxt, MalBlkPtr mb, ValPtr argv[],char flag);mal_export int factoryHasFreeSpace(void);#endif /* _MAL_FACTORY_H */@-The initial implementation is geared at a centralfactory plant manager, which is called to forwardany factory call to their proper destination.The factory plants are collected in a global,limited table for now.@c#include "mal_config.h"#include "mal_factory.h"typedef struct { int id; /* unique plant number */ MalBlkPtr factory; MalStkPtr stk; /* private state */ int pc; /* where we are */ int inuse; /* able to handle it */ int next; /* next plant of same factory */ int policy; /* flags to control behavior */ str location; /* where the factoryMgr resides */ Client client; /* who called it */ MalBlkPtr caller; /* from routine */ MalStkPtr env; /* with the stack */ InstrPtr pci; /* with the instruction */} PlantRecord, *Plant;#define MAXPLANTS 256static PlantRecord plants[MAXPLANTS];static int lastPlant;static int plantId = 1;mal_export Plant newPlant(MalBlkPtr mb);intfactoryHasFreeSpace(){ return lastPlant <255;}intfindPlant(MalBlkPtr mb){ int i; for(i=0; i<lastPlant; i++) if( plants[i].factory == mb) return i; return -1;}strrunFactory(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr stk, InstrPtr pci){ Plant pl=0; int firstcall= TRUE, i, k;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -