File: VOLS_THAT_HOLD_REFS.ML

package info (click to toggle)
polyml 5.7.1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 40,616 kB
  • sloc: cpp: 44,142; ansic: 26,963; sh: 22,002; asm: 13,486; makefile: 602; exp: 525; python: 253; awk: 91
file content (310 lines) | stat: -rw-r--r-- 11,420 bytes parent folder | download | duplicates (4)
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 *)