1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
|
(*
Copyright (c) 2000
Cambridge University Technical Services Limited
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*)
structure LowerLevel =
struct
open Volatile;
open Volatile.BehaviourRefs;
open ForeignException;
(**********************************************************************
An 'a promise represents a suspended computation of type 'a. A promise
is created by passing an 'a thunk (a function from unit to 'a) to
makePromise.
A promise is activated using forcePromise. When a promise is forced,
the result is remembered for subsequent force. This ensures that the
thunk passed to makePromise will be evaluated at most once.
For example, evaluation of the following would cause the message,
"computing..." to be echoed just once:
fun echo s = output (std_out,s);
let val P = makePromise (fn () => (echo "computing...\n"; 2 + 2))
in forcePromise P + forcePromise P
end;
**********************************************************************)
datatype ('a,'b) either = This of 'a | That of 'b
datatype 'a promise = Promise of (unit -> 'a, 'a) either ref
fun makePromise thunk = Promise (ref (This thunk));
fun forcePromise (Promise thingref) =
case (!thingref) of
This thunk => let val res = thunk()
in (thingref := That res; res)
end
| That res => res;
(**********************************************************************
An 'a inert represents a re-creatable value of type 'a. Recreation is
performed when a given exception is raised during the subsequent use
of the inert value. This is useful for objects which do not survive
from one PolyML session to the next, e.g. values of type "vol".
> makeInert CATCH BUILD
Create an inert value by forcing the thunk BUILD. Use CATCH as a
predicate on exception values. It must return true for any exception
which indicates recreation of this inert value is necessary.
Recreation will be achieved by re-forcing BUILD.
> withInert INERT F
Apply the function F to the value encapsulated within INERT.
If during the course of this application recreation is necessary
then perform that recreation.
**********************************************************************)
datatype 'a inert =
Inert of 'a ref * (unit -> 'a) * (exn -> bool)
fun makeInert catch build =
Inert(ref(build()),
build,
catch);
fun withInert (Inert(value,build,catch)) f =
f (!value)
handle e => if catch e then (value := build(); f (!value))
else raise e;
(**********************************************************************
* Debugging...
**********************************************************************)
fun inform str =
(******
* Output a message if "inform_about_symbol_reload" ref is set.
******)
if !inform_about_symbol_reload then TextIO.print str else ();
(**********************************************************************
* Combination of Inert & Promises...
**********************************************************************)
fun makeIP catch build = makePromise (fn () => makeInert catch build);
fun withIP ip f = withInert (forcePromise ip) f
(**********************************************************************
* Implementation of dylib & sym.
**********************************************************************)
datatype dylib = Dylib of string ref * vol inert promise;
datatype sym = Sym of vol inert promise;
fun withDylib (Dylib(_,ip)) f = withIP ip f;
fun withSym (Sym ip) f = withIP ip f;
fun get_libPath (Dylib(pathRef,_)) = !pathRef;
fun set_libPath (Dylib(pathRef,_)) path = pathRef := path;
val catchIV =
(******
* Returns "true" when given an `invalid volatile' exception.
* Note: this string must *exactly* match the corresponding
* string found in the runtime system file: .../Driver/foreign.c
******)
fn Foreign "Invalid volatile" => true | _ => false;
fun load_lib path =
(******
* Load a dynamic library.
* This loading is done lazily, i.e. not until the library is needed.
* The library will automatically reload in subsequent PolyML sessions
* using the latest pathname set by set_libPath above.
******)
let val pathRef = ref path
in Dylib(pathRef,
makeIP catchIV (fn () =>
(inform ("Loading library: "^(!pathRef)^"\n");
Volatile.load_lib (!pathRef))))
end;
fun load_sym dylib symName =
(******
* Load a symbol from a dynamic library.
* This loading is done lazily. Neither the library or the symbol
* will be loaded until the symbol is actually needed.
* The symbol will be reloaded in subsequent PolyML sessions.
******)
Sym(makeIP catchIV (fn () =>
withDylib dylib (fn dylibVol =>
(inform ("Loading symbol: "^symName^"\n");
Volatile.load_sym dylibVol symName))));
fun call_sym_and_convert sym args retType =
(******
* Call a symbol as a C-function.
* May load/reload either the symbol, or both the symbol and the library.
******)
withSym sym (fn symVol =>
Volatile.call_sym_and_convert symVol args retType);
(* Set a finalizer for a vol. *)
fun setFinal sym vol =
withSym sym (fn symVol => Volatile.setFinal symVol vol)
fun volOfSym sym =
(******
* Extract a *valid* volatile from a symbol
* May load/reload either the symbol, or both the symbol and the library.
*
* The reload is caused because we attempt to take the address
* of the vol which will raise an exception if the vol is invalid.
******)
withSym sym (fn symVol => (Volatile.address symVol;
symVol));
fun mapSym f sym =
(******
* Change order of params for export...
******)
withSym sym f;
fun get_sym libPath (*symName*) =
(******
* Common combination of load_lib & load_sym.
* Partial application will cause the library to be shared, and
* hence only loaded/reloaded once per session.
******)
load_sym (load_lib libPath) (*symName*) ;
open Union;
(* now imported from Union ...
exception Never of string;
... *)
fun never string = raise Never string;
(* Define call_sym in terms of call_sym_and_convert *)
fun call_sym sym args retCtype =
let
val result =
call_sym_and_convert
sym
(map (fn x => In (Vol x)) args)
(chooseVol retCtype)
in
case result of
(v,[]) => if isVol v then deVol v else never "call_sym"
| _ => never "call_sym"
end
local
fun prettyDylib _ _ (_: dylib) = PolyML.PrettyString "?"
fun prettySym _ _ (_: sym) = PolyML.PrettyString "?"
in
val () = PolyML.addPrettyPrinter prettyDylib
and () = PolyML.addPrettyPrinter prettySym
end
end;
|