📄 mal_box.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.L. Kersten@+ Boxed VariablesClients sessions often come with a global scope of variable settings.Access to these global variables should be easy,but they should also provide protection against concurrent updatewhen the client wishes to perform parallel processing.Likewise, databases, query languages, etc. may define constants and variablesaccessible, e.g. relational schemas, to a selected user group.The approach taken is to rely on persistent object spacesas pioniered in Lynda and -later- JavaSpaces.They are called boxes in MonetDB and act as managed containers for persistent variables.Before a client program can interact with a box, itshould open it, passing qualifying authorizationinformation and parameters to instruct the box-managerof the intended use. A built-in box is implicitlyopened when you request for its service.At the end of a session, the box should be closed. Some box-managersmay implement a lease-scheme to automatically close interactionwith a client when the lease runs out. Likewise, the box canbe notified when the last reference to a leased objectceases to exist.A box can be extended with a new object using the functiondeposit(name) with name a local variable.The default implementation silently accepts any new definition of the box.If the variable was known already in the box, its value is overwritten.A local copy of an object can be obtained using thepattern 'take(name,[param])', where name denotes the variableof interest. The type of the receiving variable shouldmatch the one known for the object. Whether an actual copy is produced or a reference to a shared objectis returned is defined by the box manager.The object is given back to the box manager calling 'release(name)'.It may update the content of the repository accordingly, release locks,and move the value to persistent store. Whatever the semanticsof the box requires. [The default implementation is a no-op]Finally, the object manager can be requested to 'discard(name)'a variable completely. The default implementation is to reclaimthe space in the box.Concurrency control, replication services, as well as accessto remote stores may be delegated to a box manager.Depending on the intended semantics, the box managermay keep track of the clients holding links to this members, provide a traditional 2-phaselocking scheme, optimistic control, or check-out/check-in scheme.In all cases, these management issues are transparant to themain thread (=client) of control, which operates on a temporarysnapshot. For the time being we realize the managers as criticalcode sections, i.e. one client is permitted access to the box spaceat a time.Example: consider the client function:@examplefunction myfcn():void; b:bat[:oid,:int] := bbp.take("mytable"); c:bat[:int,:str] := sql.take("person","age"); d:= intersect(b,c); io.print(d); u:str:= client.take(user); io.print(u); client.release(user);end function;@end exampleThe function binds to a copy from the local persistent BAT space,much like bat-names are resolved in earlier MonetDB versions. The secondstatement uses an implementation of take that searches a variableof interest using two string properties. It illustrates thata box manager is free to extend/overload the predefined scheme, which is geared towards storing MAL variables.The result bat 'c' is temporary and disappears upon garbagecollection. The variable 'u' is looked up as the string object user.Note that BATs b and c need be released at some point. In generalthis point in time does not coincide with a computational boundarylike a function return. During a session, several bats may be takenout of the box, being processed, and only at the end of a session being released. In this example, it means that the reference tob and c is lost at the end of the function (due to garbarge collection)and that subsequent use requires another take() call.The box manager bbp is notified of the implicit release andcan take garbage collection actions.The box may be inspected at several times during a scenario run.The first time is when the MAL program is type-checked for thebox operations. Typechecking a take() function is tricky.If the argument is a string literal, the box can be querieddirectly for the objects' type.If found, its type is matched against the lhs variable. This strategy fails in the situation when atruntime the object is subsequently replaced by another typed-instance in the box. We assume this not to happen andthe exceptions it raises a valuable advice to reconsiderthe programming style.The type indicator for the destination variable should beprovided to proceed with proper type checking. It can resolve overloaded function selection. Inspection of the Box can be encoded using an iterator at the MALlayer and relying on the functionality of the box.However, to improve introspection, we assume that all box implementations provide a few rudimentary functions, called objects(arglist)and dir(arglist). The function objects() produces a BAT withthe object names, possibly limited to those identified bythe arglist.The world of boxes has not been explored deeply yet.It is envisioned that it could play a role to import/exportdifferent objects, e.g.introduce xml.take() which converts an XML document to a BAT,jpeg.take() similer for an image.Nesting boxes (like Russian dolls) is possible. It provides a simplecontainment scheme between boxes, but in general will interfere withthe semantics of each box. Each box has [should] have an access control list, which namesthe users having permission to read/write its content.The first one to create the box becomes the owner. He may grant/revokeaccess to the box to users on a selective basis.@- Session BoxAside from box associated with the modules, a session box is createddynamically on behalf of each client. Such boxes are considered privateand require access by the user name (and password). At the end of a session they are closed, which means that they aresaved in persistent store until the next session starts.For example:@examplefunction m():void; box.open("client_name"); box.deposit("client_name","pi",3.417:flt); f:flt := box.take("client_name","pi"); io.print(t); box.close("client_name");end function;@end example@-In the namespace it is placed subordinate to any space introduced by thesystem administrator. It will contain global client data, e.g.user, language, database, port, and any other session parameter.The boxes are all collected in the context of the database directory,i.e. the directory <dbfarm>/box@- Garbage CollectionThe key objects managed by MonetDB are the persistent BATs, whichcall for an efficient scheme to make them accessible for manipulationin the MAL procedures taking into account a possibly hostileparallel access.Most kernel routines produce BATs as a result, which will be referenced from the runtime stack. They should be garbage collected as soon asdeemed possible to free-up space. By default, temporary results aregarbage collected before returning from a MAL function.@- Globale EnvironmentThe top level interaction keeps a 'box' with global variables,i.e. each MAL statement is interpreted in an already initializedstack frame.This causes the following problems: 1) how to get rid of global variablesand 2) how to deal with variables that can take 'any' type.It is illustrated as follows:@examplef:= const.take("dbname");io.print(f);@end exampleWhen executed in the context of a function, the answer will besimple [ nil ]. The reason is that the expecteed type is not knownat compilation time. The correct definition would have been@examplef:str:= const.take("dbname");io.print(f);@end example@{@h#ifndef _MAL_BOX_H#define _MAL_BOX_H#include "mal_stack.h"#include "mal_instruction.h"/*#define DEBUG_MAL_BOX */typedef struct BOX { MT_Lock lock; /* provide exclusive access */ str name; MalBlkPtr sym; MalStkPtr val; int dirty; /* don't save if it hasn't been changed */} *Box, BoxRecord;mal_export Box newBox(str name);mal_export Box findBox(str name);mal_export Box openBox(str name);mal_export int closeBox(str name, int flag);mal_export void destroyBox(str name);mal_export str boxFileName(Box box, str extension);mal_export int saveBox(Box box, int flag);mal_export void loadBox(str nme);mal_export int releaseAllBox(Box box);mal_export int depositBox(Box box, str name, ValPtr val);mal_export void insertToBox(Box box, str name, str val);mal_export int takeBox(Box box, str name, ValPtr val, int tpe);mal_export int bindBAT(Box box, str name, str location);mal_export int releaseBox(Box box, str name);mal_export int discardBox(Box box, str name);mal_export str getBoxName(Box box, lng i);mal_export str getBoxNames(int *bid);mal_export str toString(Box box, lng i);mal_export int nextBoxElement(Box box, lng *cursor, ValPtr v);mal_export stream *prepareSaveBox(Box box, str *boxfile, str *boxfilebak);mal_export void printSomeBox(stream *fd, int k);mal_export void printBox(stream *fd, Box obj);mal_export void freeBoxes(void);#endif /* _MAL_BOX_H */@-The hierarchy of object spaces ends at the root of the tree.This is a dummy element and should contain system-wide objectsonly.@= newBOX obj= (Box) GDKzalloc(sizeof(BoxRecord)); obj->name= GDKstrdup(name); obj->sym= newMalBlk(MAXVARS,STMT_INCREMENT); obj->val = newGlobalStack(MAXVARS); MT_lock_init(&obj->lock);@-@c#include "mal_config.h"#include "mal_box.h"#include "mal_interpreter.h" /* for garbageCollector() & garbageElement() */#include "mal_client.h" /* for MCgetClient() */#include "mal_debugger.h" /* for printStack() */#include "mal_import.h" /* for malInclude() */#define MAXSPACES 64 /* >MAXCLIENTS+ max modules !! */Box box[MAXSPACES];BoxnewBox(str name){ Box obj = 0; int i; mal_set_lock(mal_contextLock, "newBox");#ifdef DEBUG_MAL_BOX stream_printf(GDKout, "create new box '%s'\n", name);#endif for (i = 0; i < MAXSPACES; i++) if (box[i] != NULL && idcmp(name, box[i]->name) == 0) { mal_unset_lock(mal_contextLock, "newBox"); GDKwarning("newBox:duplicate box definition\n"); return box[i]; } for (i = 0; i < MAXSPACES; i++) if (box[i] == NULL) { @:newBOX@ box[i] = obj; break; } mal_unset_lock(mal_contextLock, "newBox"); if (i == MAXSPACES) showException(MAL,"box.new", "too many boxes");#ifdef DEBUG_MAL_BOX stream_printf(GDKout, "succeeded at %d\n", i);#endif return obj;}voidfreeBox(int i){ if (box[i]) { garbageCollector(box[i]->sym, box[i]->val,TRUE); GDKfree(box[i]->name); freeMalBlk(box[i]->sym); GDKfree(box[i]->val); GDKfree(box[i]); box[i] = 0; }}voidfreeBoxes(){ int i; for (i = 0; i < MAXSPACES; i++) freeBox(i);}BoxfindBox(str name){ int i; mal_set_lock(mal_contextLock, "findBox"); for (i = 0; i < MAXSPACES; i++) if (box[i] != NULL && idcmp(name, box[i]->name) == 0) {#ifdef DEBUG_MAL_BOX stream_printf(GDKout, "found the box '%s' %d\n", name, i);#endif mal_unset_lock(mal_contextLock, "findBox"); return box[i]; } mal_unset_lock(mal_contextLock, "findBox");#ifdef DEBUG_MAL_BOX stream_printf(GDKout, "could not find the box '%s' \n", name);#endif return 0;}BoxopenBox(str name){ Box box = findBox(name); if (box) return box; box = newBox(name); loadBox(name); box->dirty= FALSE; return box;}intcloseBox(str name, int flag){ Box box; if ((box = findBox(name))) { saveBox(box, flag); MT_destroy_lock(box->lock); return 0; } return -1;}voiddestroyBox(str name){ int i, j; str boxfile; mal_set_lock(mal_contextLock, "destroyBox"); for (i = j = 0; i < MAXSPACES; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -