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 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
|
(*
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
*)
(***
Implementaion of a wrapper for the raw C programming primitives,
which provides the means for holding onto ML objects to avoid GC.
***)
(*
I think the idea here is to allow C data structures to be built up.
If we allocate a C object using the low-level "alloc" function it may be
freed by the GC if the corresponding ML object is no longer referenced.
We don't want that to happen if we have stored a pointer to this C object
in another C object. Only if the root of the whole structure is no longer
reachable do we want the objects within the structure to be freed.
DCJM 30/4/01.
I've modified this to make it more efficient, particularly for large
arrays. DCJM 27/6/01.
*)
(**********************************************************************
* Functor Definition
**********************************************************************)
functor VOLS_THAT_HOLD_REFS
(structure Underlying : VolatileSig
structure ForeignException : ForeignExceptionSig)
:> VolatileSig =
struct
structure Ctype = Underlying.Ctype
structure BehaviourRefs = Underlying.BehaviourRefs
structure Union = Underlying.Union
val sizeof = Ctype.sizeof;
open ForeignException
open Union
(*.....
datatype owner = Owner of rawvol * dependancy ref list
and vol = Vol of rawvol * dependancy
where type dependancy = (int * owner) option
.....*)
(* The int records information for offset *)
datatype owner = Owner of Underlying.vol * ownerOpt Array.array ref
and ownerOpt = None | Some of int * owner (* This is more storage efficient than using "option". *)
datatype vol = Vol of {thevol: Underlying.vol, depends: ownerOpt}
fun vol(thevol, depends) = Vol{thevol = thevol, depends = depends}
fun thevol(Vol{thevol, ...}) = thevol
and depends(Vol{depends, ...}) = depends
val PointerSize = sizeof (Ctype.Cpointer Ctype.Cvoid)
val MaxVectorSize = 20 (* Maximum initial size of the dependency array. *)
fun selfOwner v deps =
(******
* Create a vol that `owns' itself, with dependencies "deps".
******)
vol (v,Some(0,Owner(v, ref deps)))
(***********************************************************************
a = alloc(n)
***********************************************************************)
(* "alloc" allocates a new region of memory. The dependencies are
initialised to None. If the region is large we don't allocate
a dependency array that big at this stage. Instead we grow the
array later if necessary. Typically large arrays are byte arrays
and they don't actually have dependencies. *)
fun alloc n ctype =
let val m = n * sizeof ctype
val v = Underlying.alloc n ctype
in
selfOwner v (Array.array(Int.min(m div PointerSize, MaxVectorSize), None))
end
(***********************************************************************
a = &b
***********************************************************************)
(* "address" creates a new vol containing the address of an existing one.
We create a single element array to hold the dependencies. *)
fun address (Vol{thevol, depends}) =
let val v = Underlying.address thevol
in selfOwner v (Array.array (1, depends))
end
(***********************************************************************
a = *b
***********************************************************************)
(* "deref" returns the value at the appropriate offset. The dependency
is the dependency at the corresponding (word) offset. (We need to do
this if, for example, we have created a list in the C space. If
we extract the tail of a cell we need to hang on to the rest of
the list.) *)
fun deref(Vol{thevol, depends}) =
vol (Underlying.deref thevol,
case depends of
None => None
| Some(i, Owner(_, ref refs)) =>
if i mod PointerSize <> 0
then None
else
let
val j = i div PointerSize
in
if j < 0 orelse j >= Array.length refs
then None
else Array.sub(refs, j)
end)
(***********************************************************************
a = b.X (offset n objects of type ctype) [*a = ( char* )&b + n]
***********************************************************************)
(* "offset" gives us a new address based on the old one. We shift the
origin of the dependency index by the appropriate amount. Because
offset can be applied to this vol to give a new address we can't
select the dependency at this offset yet.
This is where the self-owner comes in. Although we're creating a
new vol here we need to retain the original vol in the event of a GC. *)
fun offset n ctype (Vol{thevol, depends}) =
let val m = n * sizeof ctype
in
vol (Underlying.offset n ctype thevol,
case depends of
None => None
| Some (i,x) => Some (i+m, x))
end
(***********************************************************************
a := b (n bytes)
***********************************************************************)
(* When we make an assignment of h into g we update the dependencies of
g to point to the dependencies of h. We may have to grow the
destination array. *)
fun assign ctype g h =
let val n = sizeof ctype
in
case (depends g, depends h) of
(Some(i, Owner(_, drefs as ref destRefs)), Some(j, Owner(_, ref sourceRefs))) =>
(* We only copy the references if we're moving at least a word and the
source and destinations are word aligned. We also skip this if the
offsets are negative. It's just possible that there might be a C
function which would return an area of memory with a pointer offset
within it. In that case we don't want to raise a Subscript exception
but I don't know how to deal with the dependencies. *)
if n < PointerSize orelse i mod PointerSize <> 0 orelse j mod PointerSize <> 0
orelse i < 0 orelse j < 0
then ()
else
let
(* Neither the source array nor the destination array may be big enough. *)
val di = i div PointerSize (* Starting offset in dest. *)
val si = j div PointerSize (* Starting offset in source. *)
val maxLen = n div PointerSize (* Words to copy. *)
val sLen = Array.length sourceRefs
val toCopy = if si + maxLen <= sLen then maxLen else sLen - si
in
if toCopy <= 0
then () (* Nothing in the source to copy. *)
else
let
val diMax = di + toCopy (* Maximum offset. *)
val dLen = Array.length destRefs
val dVec =
if dLen < diMax
then (* Need to grow array. *)
let
val newSize = Int.max(diMax, dLen + dLen div 2)
val newArray = Array.array(newSize, None)
in
(* Copy the old dependencies *)
Array.copy{dst = newArray, src = destRefs, di = 0};
drefs := newArray; (* Remember this new array. *)
newArray
end
else destRefs
in
(* Copy the dependencies being updated by the assignment. *)
ArraySlice.copy{dst = dVec, src = ArraySlice.slice(sourceRefs, si, SOME toCopy), di = di}
end
end
| _ => ();
Underlying.assign ctype (thevol g) (thevol h)
end
(**********************************************************************
From / To C values
**********************************************************************)
fun makeVol v = selfOwner v (Array.array(1, None))
fun load_lib s =
makeVol (Underlying.load_lib s)
fun load_sym (Vol{thevol, ...}) s =
makeVol (Underlying.load_sym thevol s)
fun ID x = x;
fun call_sym_and_convert g args rt =
let val (u,us) =
Underlying.call_sym_and_convert
(thevol g)
(map (mapDirectedArg ID thevol) args)
rt
in
(mapUnion makeVol u, map (mapUnion makeVol) us)
end;
val toCchar = makeVol o Underlying.toCchar
val toCdouble = makeVol o Underlying.toCdouble
val toCfloat = makeVol o Underlying.toCfloat
val toCint = makeVol o Underlying.toCint
val toClong = makeVol o Underlying.toClong
val toCshort = makeVol o Underlying.toCshort
val toCstring = makeVol o Underlying.toCstring
val toCuint = makeVol o Underlying.toCuint
val toCbytes = makeVol o Underlying.toCbytes
val fromCchar = Underlying.fromCchar o thevol
val fromCdouble = Underlying.fromCdouble o thevol
val fromCfloat = Underlying.fromCfloat o thevol
val fromCint = Underlying.fromCint o thevol
val fromClong = Underlying.fromClong o thevol
val fromCshort = Underlying.fromCshort o thevol
val fromCstring = Underlying.fromCstring o thevol
val fromCuint = Underlying.fromCuint o thevol
val fillCstring = Underlying.fillCstring o thevol
fun fromCbytes(v, i) = Underlying.fromCbytes(thevol v, i)
(*
DCJM 7/4/04. I've added these although I suspect that they need to do more than
they're currently doing.
*)
fun toCfunction argType resType (f: vol list -> vol) =
let
fun appF (args: Underlying.vol list) : Underlying.vol =
let
in
thevol (f (map makeVol args))
end
in
makeVol (Underlying.toCfunction argType resType appF)
end
fun toPascalfunction argType resType (f: vol list -> vol) =
let
fun appF (args: Underlying.vol list) : Underlying.vol =
let
in
thevol (f (map makeVol args))
end
in
makeVol (Underlying.toPascalfunction argType resType appF)
end
fun setFinal f v = Underlying.setFinal (thevol f) (thevol v)
local
fun prettyVol _ _ (_: vol) = PolyML.PrettyString "?"
in
val () = PolyML.addPrettyPrinter prettyVol
end
val null = vol (Underlying.null, None)
end (* struct *)
|