1
2;; Externally, a cpointer can be #f or a byte string, in
3;; addition to a cpointer record
4(define (cpointer? v)
5  (or (authentic-cpointer? v)
6      (not v)
7      (bytes? v)
8      (has-cpointer-property? v)))
9
10;; A cpointer record's `memory` is either a raw foreign address (i.e., a
11;; number), bytevector, or flvector. A reference bytevector is used for
12;; non-atomic memory.
13(define-record-type (cpointer make-cpointer authentic-cpointer?)
14  (fields memory (mutable tags)))
15(define-record-type cpointer+offset
16  (parent cpointer)
17  (fields (mutable offset)))
18
19(define-values (prop:cpointer has-cpointer-property? cpointer-property-ref)
20  (make-struct-type-property 'cpointer
21                             (lambda (v info)
22                               (cond
23                                [(exact-nonnegative-integer? v)
24                                 (unless (< v (list-ref info 1))
25                                   (raise-arguments-error 'prop:cpointer
26                                                          "index is out of range"
27                                                          "index" v))
28                                 (unless (#%memv v (list-ref info 5))
29                                   (raise-arguments-error 'prop:cpointer
30                                                          "index does not refer to an immutable field"
31                                                          "index" v))
32                                 (+ v (let ([p (list-ref info 6)])
33                                        (if p
34                                            (struct-type-total*-field-count p)
35                                            0)))]
36                                [(and (procedure? v)
37                                      (procedure-arity-includes? v 1))
38                                 v]
39                                [(cpointer? v) v]
40                                [else
41                                 (raise-argument-error 'prop:cpointer
42                                                       (string-append
43                                                        "(or/c exact-nonnegative-integer?\n"
44                                                        "      (procedure-arity-includes/c 1)\n"
45                                                        "      cpointer?)")
46                                                       v)]))))
47
48;; Gets a primitive cpointer type by following a `prop:evt` property
49;; as needed. Call with function *before* disabling GC interrupts.
50(define (unwrap-cpointer who p)
51  (cond
52   [(authentic-cpointer? p) p]
53   [(not p) p]
54   [(bytes? p) p]
55   [(ffi-callback? p) p]
56   [else (let ([v (cpointer-property-ref p)])
57           (cond
58            [(exact-nonnegative-integer? v)
59             (let ([v (unsafe-struct-ref p v)])
60               (if (cpointer? v)
61                   (unwrap-cpointer who v)
62                   #f))]
63            [(procedure? v)
64             (let ([p2 (v p)])
65               (unless (cpointer? p2)
66                 (raise-result-error 'prop:cpointer-accessor
67                                     "cpointer?"
68                                     p2))
69               (unwrap-cpointer who p2))]
70            [else
71             (unwrap-cpointer who v)]))]))
72
73;; Like `unwrap-cpointer*`, but also allows an integer as a raw
74;; foreign address:
75(define (unwrap-cpointer* who p)
76  (if (integer? p)
77      p
78      (unwrap-cpointer who p)))
79
80(define (offset-ptr? p)
81  (unless (cpointer? p)
82    (raise-argument-error 'offset-ptr? "cpointer?" p))
83  (cpointer+offset? p))
84
85(define/who (set-cpointer-tag! p t)
86  (if (authentic-cpointer? p)
87      (cpointer-tags-set! p t)
88      (if (cpointer? p)
89          (let ([q (unwrap-cpointer who p)])
90            (if (authentic-cpointer? q)
91                (set-cpointer-tag! q t)
92                (raise-arguments-error who
93                                       "cannot set tag on given cpointer"
94                                       "given" p
95                                       "tag" t)))
96          (raise-argument-error who "cpointer?" p))))
97
98(define/who (cpointer-tag p)
99  (if (authentic-cpointer? p)
100      (cpointer-tags p)
101      (if (cpointer? p)
102          (let ([q (unwrap-cpointer who p)])
103            (if (authentic-cpointer? q)
104                (cpointer-tag q)
105                #f))
106          (raise-argument-error who "cpointer?" p))))
107
108;; Convert a `memory` --- typically a raw foreign address, but possibly
109;; a bytevector or flvector --- to a cpointer, using #f for a NULL
110;; address:
111(define (memory->cpointer x)
112  (cond
113   [(or (not x) (authentic-cpointer? x))
114    ;; This happens when a pointer is converted without going through
115    ;; `cpointer-address` such as a `ptr-ref` on a struct or array type
116    x]
117   [(eqv? x 0) #f]
118   [else (make-cpointer x #f)]))
119
120;; Works on unwrapped cpointers:
121(define (cpointer-nonatomic? p)
122  (and (authentic-cpointer? p)
123       (reference-bytevector? (cpointer-memory p))))
124
125;; Works on unwrapped cpointers:
126(define (cpointer->name proc-p)
127  (and (ffi-obj? proc-p)
128       (string->symbol (utf8->string (cpointer/ffi-obj-name proc-p)))))
129
130;; ----------------------------------------
131
132;; Convert a raw foreign address to a Scheme value on the
133;; assumption that the address is the payload of a byte
134;; string:
135(define (addr->gcpointer-memory v) ; call with GC disabled
136  (reference-address->object v))
137
138;; Converts a primitive cpointer (normally the result of
139;; `unwrap-cpointer`) to a memory plus offset
140(define (cpointer-address+offset p)
141  (cond
142   [(not p) (values 0 0)]
143   [(or (bytevector? p) (flvector? p)) (values p 0)]
144   [(cpointer+offset? p)
145    (values (cpointer-memory p) (cpointer+offset-offset p))]
146   [(authentic-cpointer? p)
147    (values (cpointer-memory p) 0)]
148   [(ffi-callback? p)
149    (values (foreign-callable-entry-point (callback-code p)) 0)]
150   [else
151    (raise-arguments-error 'internal-error "bad case extracting a cpointer address"
152                           "value" p)]))
153
154;; Convert a `memory` (as in a cpointer) to a raw foreign address.
155(define (memory-address memory) ; call with GC disabled
156  (cond
157   [(integer? memory) memory]
158   [else (object->reference-address memory)]))
159
160;; Converts a primitive cpointer (normally the result of
161;; `unwrap-cpointer`) to a raw foreign address. The
162;; GC must be disabled while extracting an address,
163;; which might be the address of a byte string that
164;; could otherwise change due to a GC.
165(define (cpointer-address p) ; call with GC disabled
166  (let-values ([(memory offset) (cpointer-address+offset p)])
167    (+ (memory-address memory) offset)))
168
169(define (cpointer-needs-lock? p)
170  (cond
171   [(bytes? p) #t]
172   [(authentic-cpointer? p) (not (integer? (cpointer-memory p)))]
173   [else #f]))
174
175;; Like `cpointer-address`, but allows a raw foreign
176;; address to pass through:
177(define (cpointer*-address p) ; call with GC disabled
178  (if (number? p)
179      p
180      (cpointer-address p)))
181
182;; ----------------------------------------
183
184(define/who (ptr-equal? p1 p2)
185  (let ([p1 (unwrap-cpointer who p1)]
186        [p2 (unwrap-cpointer who p2)])
187    (with-interrupts-disabled* ; disable GC while extracting addresses
188     (= (cpointer-address p1) (cpointer-address p2)))))
189
190(define/who (ptr-offset p)
191  (let ([p (unwrap-cpointer who p)])
192    (ptr-offset* p)))
193
194(define (ptr-offset* p)
195  (if (cpointer+offset? p)
196      (cpointer+offset-offset p)
197      0))
198
199(define (set-ptr-offset! p n)
200  (unless (cpointer+offset? p)
201    (raise-argument-error 'ptr-offset "(and/c cpointer? ptr-offset?)" p))
202  (unless (exact-integer? n)
203    (raise-argument-error 'ptr-offset "exact-integer?" n))
204  (cpointer+offset-offset-set! p n))
205
206(define ptr-add
207  (case-lambda
208   [(p n type)
209    (unless (cpointer? p)
210      (raise-argument-error 'ptr-add "cpointer?" p))
211    (unless (exact-integer? n)
212      (raise-argument-error 'ptr-add "exact-integer?" n))
213    (unless (ctype? type)
214      (raise-argument-error 'ptr-add "ctype?" type))
215    (do-ptr-add p (* n (ctype-sizeof type)) #t)]
216   [(p n)
217    (unless (cpointer? p)
218      (raise-argument-error 'ptr-add "cpointer?" p))
219    (unless (exact-integer? n)
220      (raise-argument-error 'ptr-add "exact-integer?" n))
221    (do-ptr-add p n #t)]))
222
223(define (do-ptr-add p n save-tags?)
224  (cond
225   [(authentic-cpointer? p)
226    (make-cpointer+offset (cpointer-memory p)
227                          (and save-tags? (cpointer-tag p))
228                          (+ n (ptr-offset* p)))]
229   [(has-cpointer-property? p)
230    (do-ptr-add (unwrap-cpointer 'do-ptr-add p) n save-tags?)]
231   [else
232    (make-cpointer+offset (or p 0) #f n)]))
233
234(define ptr-add!
235  (case-lambda
236   [(p n type)
237    (unless (cpointer+offset? p)
238      (raise-argument-error 'ptr-add! "(and/c cpointer? offset-ptr?)" p))
239    (unless (exact-integer? n)
240      (raise-argument-error 'ptr-add! "exact-integer?" n))
241    (unless (ctype? type)
242      (raise-argument-error 'ptr-add! "ctype?" type))
243    (do-ptr-add! p (* n (ctype-sizeof type)))]
244   [(p n)
245    (unless (cpointer+offset? p)
246      (raise-argument-error 'ptr-add! "(and/c cpointer? offset-ptr?)" p))
247    (unless (exact-integer? n)
248      (raise-argument-error 'ptr-add! "exact-integer?" n))
249    (do-ptr-add! p n)]))
250
251(define (do-ptr-add! p n)
252  (unless (cpointer+offset? p)
253    (raise-arguments-error 'ptr-add!
254                           "given cpointer does not have an offset"
255                           "given" p))
256  (cpointer+offset-offset-set! p (+ n (cpointer+offset-offset p))))
257
258;; ----------------------------------------
259
260;; In ctype-host-rep, we use 'uptr and 'void* (which are aliases for foreign-ref)
261;; to reflect intent with respect to foreign versus Scheme addresses when reading:
262;;  - 'uptr  => inferred as Scheme or foreign, read as memory instead of address
263;;  - 'void* => ctype-out-rep ('pointer versus 'gcpointer) implies Scheme or foreign,
264;;;             and ctype-out-rep is assumed to be correct in that regard
265
266(define-record-type (ctype create-ctype ctype?)
267  (fields host-rep    ; host-Scheme representation description, 'struct, 'union, or 'array
268          our-rep     ; Racket representation description
269          basetype    ; parent ctype or the same as `our-rep`
270          scheme->c   ; converter of values to `basetype`
271          c->scheme)) ; converter of values from `basetype`
272
273;; A `compound-ctype` is used for structs, unions, and arrays
274(define-record-type (compound-ctype create-compound-ctype compound-ctype?)
275  (parent ctype)
276  (fields get-decls
277          size
278          alignment
279          malloc-mode))
280
281(define/who (make-ctype type racket-to-c c-to-racket)
282  (check who ctype? type)
283  (check who (procedure-arity-includes/c 1) :or-false racket-to-c)
284  (check who (procedure-arity-includes/c 1) :or-false c-to-racket)
285  (cond
286   [(compound-ctype? type)
287    (create-compound-ctype (ctype-host-rep type)
288                           (ctype-our-rep type)
289                           type
290                           (protect-racket-to-c racket-to-c)
291                           c-to-racket
292                           (compound-ctype-get-decls type)
293                           (compound-ctype-size type)
294                           (compound-ctype-alignment type)
295                           (compound-ctype-malloc-mode type))]
296   [else
297    (create-ctype (ctype-host-rep type)
298                  (ctype-our-rep type)
299                  type
300                  (protect-racket-to-c racket-to-c)
301                  c-to-racket)]))
302
303(define (protect-racket-to-c racket-to-c)
304  ;; Make sure `racket-to-c` is not confused for an internal
305  ;; variant that accepts a `who` argument:
306  (if (and (#%procedure? racket-to-c)
307           (chez:procedure-arity-includes? racket-to-c 2))
308      (lambda (v) (racket-to-c v))
309      racket-to-c))
310
311;; Apply all the conversion wrappers of `type` to the Scheme value `v`
312(define (s->c who type v)
313  (let* ([racket-to-c (ctype-scheme->c type)]
314         [v (if racket-to-c
315                (if (and (#%procedure? racket-to-c)
316                         (chez:procedure-arity-includes? racket-to-c 2))
317                    (racket-to-c who v)
318                    (|#%app| racket-to-c v))
319                v)]
320         [next (ctype-basetype type)])
321    (if (ctype? next)
322        (s->c who next v)
323        v)))
324
325;; Apply all the conversion wrapper of `type` to the C value `v`
326(define (c->s type v)
327  (let* ([next (ctype-basetype type)]
328         [v (if (ctype? next)
329                (c->s next v)
330                v)]
331         [c-to-racket (ctype-c->scheme type)])
332    (if c-to-racket
333        (|#%app| c-to-racket v)
334        v)))
335
336;; ----------------------------------------
337
338(define-syntax define-ctype
339  (syntax-rules ()
340    [(_ id host-rep basetype)
341     (define/who id (create-ctype host-rep basetype basetype #f #f))]
342    [(_ id host-rep basetype s->c)
343     (define/who id (create-ctype host-rep basetype basetype s->c #f))]
344    [(_ id host-rep basetype s->c c->s)
345     (define/who id (create-ctype host-rep basetype basetype s->c c->s))]))
346
347;; We need `s->c` checks, even if they seem redundant, to make sure
348;; that the checks happen early enough --- outside of atomic and
349;; foreign-thread regions. Also, the integer checks built into Chez
350;; Scheme are more permissive than Racket's.
351
352(define-syntax-rule (checker who ?) (lambda (for-whom x) (if (? x) x (bad-ctype-value for-whom who x))))
353(define-syntax integer-checker
354  (syntax-rules (signed unsigned)
355    [(_ who signed n int?) (checker who (lambda (x) (and (int? x) (<= (- (expt 2 (- n 1))) x  (- (expt 2 (- n 1)) 1)))))]
356    [(_ who unsigned n int?) (checker who (lambda (x) (and (int? x) (<= 0 x  (- (expt 2 n) 1)))))]))
357
358(define-ctype _bool 'boolean 'bool)
359(define-ctype _double 'double 'double (checker who flonum?))
360(define-ctype _fixnum 'fixnum 'fixnum (checker who fixnum?))
361(define-ctype _float 'float 'float (checker who flonum?))
362(define-ctype _int8 'integer-8 'int8 (integer-checker who signed 8 fixnum?))
363(define-ctype _int16 'integer-16 'int16 (integer-checker who signed 16 fixnum?))
364(define-ctype _int32 'integer-32 'int32 (integer-checker who signed 32 exact-integer?))
365(define-ctype _int64 'integer-64 'int64 (integer-checker who signed 64 exact-integer?))
366(define-ctype _uint8 'unsigned-8 'uint8 (integer-checker who unsigned 8 fixnum?))
367(define-ctype _uint16 'unsigned-16 'uint16 (integer-checker who unsigned 16 fixnum?))
368(define-ctype _uint32 'unsigned-32 'uint32 (integer-checker who unsigned 32 exact-integer?))
369(define-ctype _uint64 'unsigned-64 'uint64 (integer-checker who unsigned 64 exact-integer?))
370(define-ctype _scheme 'scheme-object 'scheme)
371(define-ctype _void 'void 'void (checker who void))
372
373(define (bad-ctype-value who type-name v)
374  (raise-arguments-error who
375                         "given value does not fit primitive C type"
376                         "C type" (make-unquoted-printing-string (symbol->string type-name))
377                         "value" v))
378
379;; Unlike traditional Racket, copies when converting from C:
380(define-ctype _bytes 'uptr 'bytes
381  (checker who (lambda (x) (or (not x) (bytes? x))))
382  (lambda (x)
383    (cond
384     [(not x) ; happens with non-atomic memory reference
385      x]
386     [(bytes? x) ; happens with non-atomic memory reference
387      ;; For consistency, truncate byte string at any NUL byte
388      (let ([len (bytes-length x)])
389        (let loop ([i 0])
390          (cond
391           [(fx= i len) x]
392           [(fx= 0 (bytes-ref x i))
393            (subbytes x 0 i)]
394           [else (loop (fx+ i 1))])))]
395     [(eqv? x 0) #f]
396     [else
397      (let loop ([i 0])
398        (if (fx= 0 (foreign-ref 'unsigned-8 x i))
399            (let ([bstr (make-bytes i)])
400              (memcpy* bstr 0 x 0 i #f)
401              bstr)
402            (loop (fx+ i 1))))])))
403
404(define (uptr->bytevector/two-nuls x)
405  (cond
406    [(not x) #f]
407    [else
408     (let loop ([i 0])
409       (if (fx= 0 (if (bytevector? x)
410                      (bytevector-u16-native-ref x i)
411                      (foreign-ref 'unsigned-16 x i)))
412           (let ([bstr (make-bytes i)])
413             (memcpy* bstr 0 x 0 i #f)
414             bstr)
415           (loop (+ i 2))))]))
416
417(define-ctype _short_bytes 'uptr 'bytes
418  (lambda (form-whom x) x)
419  (lambda (x) (uptr->bytevector/two-nuls x)))
420
421(define-ctype _string/utf-16 'uptr 'string/utf-16
422  (lambda (for-whom x)
423    (cond
424      [(not x) #f]
425      [(string? x) (string->utf16 (string-append x "\x0;") (if (system-big-endian?) 'big 'little))]
426      [else (bad-ctype-value who for-whom x)]))
427  (lambda (x) (and x
428                   (not (eq? x 0))
429                   (utf16->string (uptr->bytevector/two-nuls x)
430                                  (if (system-big-endian?) 'big 'little)))))
431
432(define (uptr->bytevector/four-nuls x)
433  (cond
434    [(not x) #f]
435    [else
436     (let loop ([i 0])
437       (if (eqv? 0 (if (bytevector? x)
438                       (bytevector-u32-native-ref x i)
439                       (foreign-ref 'unsigned-32 x i)))
440           (let ([bstr (make-bytes i)])
441             (memcpy* bstr 0 x 0 i #f)
442             bstr)
443           (loop (+ i 4))))]))
444
445(define-ctype _string/ucs-4 'uptr 'string/ucs-4
446  (lambda (for-whom x)
447    (cond
448      [(not x) #f]
449      [(string? x) (string->utf32 (string-append x "\x0;") (if (system-big-endian?) 'big 'little))]
450      [else (bad-ctype-value who for-whom x)]))
451  (lambda (x) (and x
452                   (not (eq? x 0))
453                   (utf32->string (uptr->bytevector/four-nuls x)
454                                  (if (system-big-endian?) 'big 'little)))))
455
456(define-ctype _double* 'double 'double
457  (lambda (for-whom x) (if (real? x)
458                           (exact->inexact x)
459                           (bad-ctype-value for-whom who x))))
460
461(define-ctype _ufixnum 'fixnum 'fixnum (checker who fixnum?)) ; historically, no sign check
462(define-ctype _fixint 'integer-32 'fixint (checker who fixnum?))
463(define-ctype _ufixint 'unsigned-32 'ufixint (checker who fixnum?)) ; historically, no sign check
464
465(define-ctype _symbol 'string 'string
466  (lambda (for-whom x) (if (symbol? x)
467                           (symbol->string x)
468                           (bad-ctype-value for-whom who x)))
469  (lambda (s) (string->symbol s)))
470
471(define-ctype _longdouble 'double 'double
472  (lambda (for-whom x) (bad-ctype-value for-whom who x)))
473
474(define-ctype _pointer 'void* 'pointer
475  (lambda (for-whom v) (unwrap-cpointer for-whom v)) ; resolved to an address later (with the GC disabled)
476  (lambda (x) (memory->cpointer x)))
477
478;; Treated specially by `ptr-ref`
479(define-ctype _fpointer 'void* 'fpointer
480  (lambda (for-whom v) (unwrap-cpointer for-whom v)) ; resolved to an address later (with the GC disabled)
481  (lambda (x)
482    (if (ffi-obj? x) ; check for `ptr-ref` special case on `ffi-obj`s
483        x
484        (memory->cpointer x))))
485
486(define-ctype _gcpointer 'void* 'gcpointer
487  (lambda (for-whom v) (unwrap-cpointer for-whom v)) ; like `_pointer`: resolved later
488  (lambda (x)
489    ;; `x` must have been converted to a bytevector or vector before
490    ;; the GC was re-enabled
491    (memory->cpointer x)))
492
493;; One-byte stdbool is correct on all currently supported platforms, at least:
494(define-ctype _stdbool 'integer-8 'stdbool
495  (lambda (for-whom x) (if x 1 0))
496  (lambda (v) (not (zero? v))))
497
498(define make-cstruct-type
499  (case-lambda
500   [(types) (make-cstruct-type types #f #f 'atomic)]
501   [(types abi) (make-cstruct-type types abi #f 'atomic)]
502   [(types abi alignment) (make-cstruct-type types abi alignment 'atomic)]
503   [(types abi alignment malloc-mode)
504    (let ([make-decls
505           (lambda (id next!-id)
506             (let-values ([(reps decls) (types->reps types next!-id)])
507               (append decls
508                       `((define-ftype ,id
509                           (struct ,@(map (lambda (rep)
510                                            `[,(next!-id) ,rep])
511                                          reps)))))))])
512      (let-values ([(size alignment) (ctypes-sizeof+alignof types alignment)])
513        (create-compound-ctype 'struct
514                               'struct
515                               types
516                               (lambda (s) (unwrap-cpointer '_struct s)) ; like `_pointer`: resolved later
517                               (lambda (c) (memory->cpointer c))
518                               make-decls
519                               size
520                               alignment
521                               malloc-mode)))]))
522
523(define/who (make-union-type . types)
524  (for-each (lambda (type) (check who ctype? type))
525            types)
526  (let ([make-decls
527         (lambda (id next!-id)
528           (let-values ([(reps decls) (types->reps types next!-id)])
529             (append decls
530                     `((define-ftype ,id
531                         (union ,@(map (lambda (rep)
532                                         `[,(next!-id) ,rep])
533                                       reps)))))))]
534        [size (apply max (map ctype-sizeof types))]
535        [alignment (apply max (map ctype-alignof types))])
536    (create-compound-ctype 'union
537                           'union
538                           types
539                           (lambda (s) (unwrap-cpointer '_union s)) ; like `_pointer`: resolved later
540                           (lambda (c) (memory->cpointer c))
541                           make-decls
542                           size
543                           alignment
544                           'atomic)))
545
546(define/who (make-array-type type count)
547  (check who ctype? type)
548  (check who exact-nonnegative-integer? count)
549  (let ([make-decls
550         (lambda (id next!-id)
551           (let-values ([(reps decls) (types->reps (list type) next!-id)])
552             (append decls
553                     `((define-ftype ,id
554                         (array ,count ,(car reps)))))))]
555        [size (* count (ctype-sizeof type))]
556        [alignment (ctype-alignof type)])
557    (unless (fixnum? size)
558      (raise-arguments-error who "arithmetic overflow for overlarge array type"
559                             "size" size))
560    (create-compound-ctype 'array
561                           'array
562                           (vector type count)
563                           (lambda (s) (unwrap-cpointer '_array s)) ; like `_pointer`: resolved later
564                           (lambda (c) (memory->cpointer c))
565                           make-decls
566                           size
567                           alignment
568                           #f)))
569
570(define (compiler-sizeof sl)
571  (let ([rest (lambda (sl) (if (pair? sl) (cdr sl) '()))])
572    (unless (or (symbol? sl)
573                (list? sl))
574      (raise-argument-error 'compiler-sizeof
575                            "(or/c ctype-symbol? (listof ctype-symbol?))"
576                            sl))
577    (let loop ([sl sl] [base-type #f] [star? #f] [size #f])
578      (cond
579       [(null? sl)
580        (cond
581         [(eq? base-type 'void)
582          (when size
583            (raise-arguments-error 'compiler-sizeof "cannot qualify 'void"))
584          (if star?
585              (foreign-sizeof 'void*)
586              (raise-arguments-error 'compiler-sizeof "cannot use 'void without a '*"))]
587         [(or (not size)
588              (eq? base-type 'int)
589              (not base-type))
590          (if star?
591              (foreign-sizeof 'void*)
592              (foreign-sizeof (or size base-type 'int)))]
593         [(eq? base-type 'double)
594          (case size
595            [(long)
596             (if star?
597                 (foreign-sizeof 'void*)
598                 ;; FIXME:
599                 (foreign-sizeof 'double))]
600            [(#f)
601             (if star?
602                 (foreign-sizeof 'void*)
603                 (foreign-sizeof 'double))]
604            [else
605             (raise-arguments-error 'compiler-sizeof "bad qualifiers for 'double")])]
606         [(eq? base-type 'float)
607          (case size
608            [(#f)
609             (if star?
610                 (foreign-sizeof 'void*)
611                 (foreign-sizeof 'float))]
612            [else
613             (raise-arguments-error 'compiler-sizeof "bad qualifiers for 'float")])]
614         [size
615          (raise-arguments-error 'compiler-sizeof (format "cannot qualify '~a" base-type))])]
616       [else
617        (let ([s (if (pair? sl) (car sl) sl)])
618          (case s
619            [(int char wchar float double void)
620             (cond
621              [base-type
622               (raise-arguments-error 'compiler-sizeof
623                                      (format "extraneous type: '~a" s))]
624              [else
625               (loop (rest sl) s star? size)])]
626            [(short)
627             (case size
628               [(short)
629                (raise-arguments-error 'compiler-sizeof
630                                       "cannot handle more than one 'short")]
631               [(long)
632                (raise-arguments-error 'compiler-sizeof
633                                       "cannot use both 'short and 'long")]
634               [(#f) (loop (rest sl) base-type star? 'short)])]
635            [(long)
636             (case size
637               [(short)
638                (raise-arguments-error 'compiler-sizeof
639                                       "cannot use both 'short and 'long")]
640               [(long-long)
641                (raise-arguments-error 'compiler-sizeof
642                                       "cannot handle more than two 'long")]
643               [(long)
644                (loop (rest sl) base-type star? 'long-long)]
645               [(#f)
646                (loop (rest sl) base-type star? 'long)])]
647            [(*)
648             (if star?
649                 (raise-arguments-error 'compiler-sizeof
650                                        "cannot handle more than one '*")
651                 (loop (rest sl) base-type #t size))]
652            [else
653             (raise-argument-error 'compiler-sizeof
654                                   "(or/c ctype-symbol? (listof ctype-symbol?))"
655                                   sl)]))]))))
656
657(define (ctype-malloc-mode c)
658  (let ([t (ctype-our-rep c)])
659    (if (or (eq? t 'gcpointer)
660            (eq? t 'bytes)
661            (eq? t 'scheme)
662            (eq? t 'string)
663            (eq? t 'string/ucs-4)
664            (eq? t 'string/utf-16))
665        'nonatomic
666        'atomic)))
667
668(define/who (ctype-sizeof c)
669  (check who ctype? c)
670  (case (ctype-host-rep c)
671    [(void) 0]
672    [(boolean int) 4]
673    [(double) 8]
674    [(float) 4]
675    [(integer-8 unsigned-8) 1]
676    [(integer-16 unsigned-16) 2]
677    [(integer-32 unsigned-32) 4]
678    [(integer-64 unsigned-64) 8]
679    [else
680     (if (compound-ctype? c)
681         (compound-ctype-size c)
682         ;; Everything else is pointer-sized:
683         (foreign-sizeof 'void*))]))
684
685(define (ctypes-sizeof+alignof base alignment)
686  (let ([align (lambda (size algn)
687                 (let ([amt (modulo size (or alignment algn))])
688                   (if (zero? amt)
689                       size
690                       (+ size (- algn amt)))))])
691    (let loop ([types base] [size 0] [max-align 1])
692      (cond
693       [(null? types) (values (align size max-align)
694                              max-align)]
695       [else (let ([sz (ctype-sizeof (car types))]
696                   [algn (ctype-alignof (car types))])
697               (loop (cdr types)
698                     (+ (align size algn)
699                        sz)
700                     (max algn max-align)))]))))
701
702(define/who (ctype-alignof c)
703  (check who ctype? c)
704  (cond
705   [(compound-ctype? c)
706    (compound-ctype-alignment c)]
707   [else
708    (case (ctype-host-rep c)
709      [(boolean int) (foreign-alignof 'int)]
710      [(double) (foreign-alignof 'double)]
711      [(float) (foreign-alignof 'float)]
712      [(integer-8 unsigned-8) (foreign-alignof 'integer-8)]
713      [(integer-16 unsigned-16) (foreign-alignof 'integer-16)]
714      [(integer-32 unsigned-32) (foreign-alignof 'integer-32)]
715      [(integer-64 unsigned-64) (foreign-alignof 'integer-64)]
716      [else
717       ;; Everything else is pointer-sized:
718       (foreign-alignof 'void*)])]))
719
720(define/who (cpointer-gcable? p)
721  (let ([p (unwrap-cpointer who p)])
722    (or (bytes? p)
723        (and (authentic-cpointer? p)
724             (let ([memory (cpointer-memory p)])
725               (or (bytes? memory)
726                   (flvector? memory)))))))
727
728;; ----------------------------------------
729
730(define-record-type (ffi-lib make-ffi-lib ffi-lib?)
731  (fields handle name))
732
733(define ffi-lib*
734  (case-lambda
735   [(name) (ffi-lib* name #f #f)]
736   [(name fail-as-false?) (ffi-lib* name fail-as-false? #f)]
737   [(name fail-as-false? as-global?)
738    (let ([name (if (string? name)
739                    (string->immutable-string name)
740                    name)])
741      (ffi-get-lib 'ffi-lib
742                   name
743                   as-global?
744                   fail-as-false?
745                   (lambda (h)
746                     (make-ffi-lib h name))))]))
747
748(define/who (ffi-lib-unload lib)
749  (check who ffi-lib? lib)
750  (ffi-unload-lib (ffi-lib-handle lib)))
751
752(define-record-type (cpointer/ffi-obj make-ffi-obj ffi-obj?)
753  (parent cpointer)
754  (fields lib name))
755
756(define/who (ffi-obj name lib)
757  (check who bytes? name)
758  (check who ffi-lib? lib)
759  (let ([name (bytes->immutable-bytes name)])
760    (ffi-get-obj who
761                 (ffi-lib-handle lib)
762                 (ffi-lib-name lib)
763                 name
764                 (lambda (ptr)
765                   (make-ffi-obj (ffi-ptr->address ptr) #f lib name)))))
766
767(define (ffi-obj-name obj)
768  (cpointer/ffi-obj-name obj))
769
770(define (ffi-obj-lib obj)
771  (cpointer/ffi-obj-lib obj))
772
773(define ffi-get-lib
774  ;; Placeholder implementation that either fails
775  ;; or returns a dummy value:
776  (lambda (who name as-global? fail-as-false? success-k)
777    (if fail-as-false?
778        #f
779        (success-k #f))))
780
781(define ffi-unload-lib
782  ;; Placeholder implementation that does nothing:
783  (lambda (lib)
784    (void)))
785
786(define ffi-get-obj
787  ;; Placeholder implementation that always fails:
788  (lambda (who lib lib-name name success-k)
789    (raise
790     (|#%app|
791      exn:fail:filesystem
792      (format "~a: not yet ready\n  name: ~a" who name)
793      (current-continuation-marks)))))
794
795(define ffi-ptr->address
796  ;; Placeholder implementation
797  (lambda (p) p))
798
799(define (set-ffi-get-lib-and-obj! do-ffi-get-lib do-ffi-get-obj do-ffi-unload-lib do-ffi-ptr->address)
800  (set! ffi-get-lib do-ffi-get-lib)
801  (set! ffi-get-obj do-ffi-get-obj)
802  (set! ffi-unload-lib do-ffi-unload-lib)
803  (set! ffi-ptr->address do-ffi-ptr->address))
804
805;; ----------------------------------------
806
807(define/who ptr-ref
808  (case-lambda
809   [(p type)
810    (check who cpointer? p)
811    (check who ctype? type)
812    (c->s type (foreign-ref* type p 0))]
813   [(p type offset)
814    (check who cpointer? p)
815    (check who ctype? type)
816    (check who exact-integer? offset)
817    (c->s type (foreign-ref* type
818                             p
819                             (* (ctype-sizeof type) offset)))]
820   [(p type abs-tag offset)
821    (check who cpointer? p)
822    (check who ctype? type)
823    (check who (lambda (p) (eq? p 'abs)) :contract "'abs" abs-tag)
824    (check who exact-integer? offset)
825    (c->s type (foreign-ref* type p offset))]))
826
827(define (foreign-ref* type orig-p offset)
828  (cond
829   [(and (ffi-obj? orig-p)
830         (eq? 'fpointer (ctype-our-rep type)))
831    ;; Special case for `ptr-ref` on a function-type ffi-object:
832    ;; cancel a level of indirection and preserve `ffi-obj`ness
833    ;; to keep its name
834    orig-p]
835   [else
836    (cond
837     [(compound-ctype? type)
838      ;; Instead of copying, get a pointer within `p`:
839      (do-ptr-add orig-p offset #f)]
840     [else
841      (let ([p (unwrap-cpointer 'foreign-ref* orig-p)]
842            [host-rep (ctype-host-rep type)])
843        (let-values ([(memory mem-offset) (cpointer-address+offset p)])
844          (cond
845            [(and (eq? 'scheme-object host-rep)
846                  (reference-bytevector? memory))
847             (bytevector-reference-ref memory (+ offset mem-offset))]
848            [(and (eq? 'uptr host-rep)
849                  (reference-bytevector? memory))
850             ;; used for string conversions; allow Scheme or foreign pointer
851             (bytevector-reference*-ref memory (+ offset mem-offset))]
852            [(and (eq? 'void* host-rep)
853                  (reference-bytevector? memory))
854             ;; used for _pointer and _gcpointer
855             (case (ctype-our-rep type)
856               [(gcpointer)
857                (bytevector-reference-ref memory (+ offset mem-offset))]
858               [else
859                ;; Although `bytevector-reference*-ref` would be sensible
860                ;; here, since a non-GCable pointer that overlaps with the
861                ;; GC pages is likely to go wrong with a GC, we return a
862                ;; non-GC-pointer representation and don't automatically
863                ;; fix up a GCable-pointer reference (if for no other reason
864                ;; then consistency with BC)
865                (if (fx= 8 (foreign-sizeof 'ptr))
866                    (bytevector-u64-native-ref memory (+ mem-offset offset))
867                    (bytevector-u32-native-ref memory (+ mem-offset offset)))])]
868            [else
869             ;; Disable interrupts to avoid a GC:
870             (with-interrupts-disabled*
871              ;; Special treatment is needed for 'scheme-object, since the
872              ;; host Scheme rejects the use of 'scheme-object with
873              ;; `foreign-ref`
874              (let ([v (foreign-ref (if (eq? host-rep 'scheme-object)
875                                        'uptr
876                                        host-rep)
877                                    (+ (memory-address memory) mem-offset)
878                                    offset)])
879                (case host-rep
880                  [(scheme-object) (reference-address->object v)]
881                  [else
882                   (case (ctype-our-rep type)
883                     [(gcpointer) (addr->gcpointer-memory v)]
884                     [else v])])))])))])]))
885
886(define/who ptr-set!
887  (case-lambda
888   [(p type v)
889    (check who cpointer? p)
890    (check who ctype? type)
891    (foreign-set!* who
892                   type
893                   p
894                   0
895                   v)]
896   [(p type offset v)
897    (check who cpointer? p)
898    (check who ctype? type)
899    (check who exact-integer? offset)
900    (foreign-set!* who
901                   type
902                   p
903                   (* (ctype-sizeof type) offset)
904                   v)]
905   [(p type abs-tag offset v)
906    (check who cpointer? p)
907    (check who ctype? type)
908    (check who (lambda (p) (eq? p 'abs)) :contract "'abs" abs-tag)
909    (check who exact-integer? offset)
910    (foreign-set!* who
911                   type
912                   p
913                   offset
914                   v)]))
915
916(define-syntax-rule (define-fast-ptr-ops ref set _type ok-v? bytes-ref bytes-set foreign-type type-bits)
917  (begin
918    (define (ref p offset abs?)
919      (let ([simple-p (if (bytevector? p)
920                          p
921                          (and (authentic-cpointer? p)
922                               (let ([m (cpointer-memory p)])
923                                 (and (or (bytevector? m)
924                                          (exact-integer? m))
925                                      m))))])
926        (cond
927         [(and simple-p
928               (fixnum? offset)
929               (or (not abs?) (fx= 0 (fxand offset (fx- (fxsll 1 type-bits) 1)))))
930          (let ([offset (let ([offset (if abs? offset (fxsll offset type-bits))])
931                          (if (cpointer+offset? p)
932                              (+ offset (cpointer+offset-offset p))
933                              offset))])
934            (if (bytevector? simple-p)
935                (bytes-ref simple-p offset)
936                (foreign-ref 'foreign-type simple-p offset)))]
937         [else
938          (if abs?
939              (ptr-ref p _type 'abs offset)
940              (ptr-ref p _type offset))])))
941    (define (set p offset v abs?)
942      (let ([simple-p (if (bytevector? p)
943                          p
944                          (and (authentic-cpointer? p)
945                               (let ([m (cpointer-memory p)])
946                                 (and (or (bytevector? m)
947                                          (exact-integer? m))
948                                      m))))])
949        (cond
950         [(and simple-p
951               (fixnum? offset)
952               (or (not abs?) (fx= 0 (fxand offset (fx- (fxsll 1 type-bits) 1))))
953               (ok-v? v))
954          (let ([offset (let ([offset (if abs? offset (fxsll offset type-bits))])
955                          (if (cpointer+offset? p)
956                              (+ offset (cpointer+offset-offset p))
957                              offset))])
958            (if (bytevector? simple-p)
959                (bytes-set simple-p offset v)
960                (foreign-set! 'foreign-type simple-p offset v)))]
961         [else
962          (if abs?
963              (ptr-set! p _type 'abs offset v)
964              (ptr-set! p _type offset v))])))))
965
966(define (fixnum-in-range? lo hi) (lambda (v) (and (fixnum? v) (fx>= v lo) (fx>= v hi))))
967(define (in-range? lo hi) (lambda (v) (and (exact-integer? v) (>= v lo) (>= v hi))))
968
969;; Schemify optimizes `(ptr-ref p _uint16 offset v)` to `(ptr-set!/uint16 p (fxlshift offset 1) v #f)`, etc.
970(define-fast-ptr-ops ptr-ref/int8 ptr-set!/int8 _int8 (fixnum-in-range? -128 127) bytevector-s8-ref bytevector-s8-set! integer-8 0)
971(define-fast-ptr-ops ptr-ref/uint8 ptr-set!/uint8 _uint8 byte? bytevector-u8-ref bytevector-u8-set! unsigned-8 0)
972(define-fast-ptr-ops ptr-ref/int16 ptr-set!/int16 _int16 (fixnum-in-range? -32768 32767) bytevector-s16-native-ref bytevector-s16-native-set! integer-16 1)
973(define-fast-ptr-ops ptr-ref/uint16 ptr-set!/uint16 _uint16 (fixnum-in-range? 0 65535) bytevector-u16-native-ref bytevector-u16-native-set! unsigned-16 1)
974(define-fast-ptr-ops ptr-ref/int32 ptr-set!/int32 _int32 (in-range? -2147483648 2147483647) bytevector-s32-native-ref bytevector-s32-native-set! integer-32 2)
975(define-fast-ptr-ops ptr-ref/uint32 ptr-set!/uint32 _uint32 (in-range? 0 4294967296) bytevector-u32-native-ref bytevector-u32-native-set! unsigned-32 2)
976(define-fast-ptr-ops ptr-ref/int64 ptr-set!/int64 _int64 (in-range? -9223372036854775808 9223372036854775807) bytevector-s64-native-ref bytevector-s64-native-set! integer-64 3)
977(define-fast-ptr-ops ptr-ref/uint64 ptr-set!/uint64 _uint64 (in-range? 0 18446744073709551616) bytevector-u64-native-ref bytevector-u64-native-set! unsigned-64 3)
978(define-fast-ptr-ops ptr-ref/double ptr-set!/double _double flonum? bytevector-ieee-double-native-ref bytevector-ieee-double-native-set! double 3)
979(define-fast-ptr-ops ptr-ref/float ptr-set!/float _float flonum? bytevector-ieee-single-native-ref bytevector-ieee-single-native-set! float 2)
980
981(define (foreign-set!* who type orig-p offset orig-v)
982  (let ([p (unwrap-cpointer 'foreign-set!* orig-p)])
983    (cond
984     [(compound-ctype? type)
985      ;; Corresponds to a copy, since `v` is represented by a pointer
986      (memcpy* p offset
987               (s->c who type orig-v) 0
988               (compound-ctype-size type)
989               #f)]
990     [else
991      (let ([host-rep (ctype-host-rep type)]
992            [v (s->c who type orig-v)])
993        (let-values ([(memory mem-offset) (cpointer-address+offset p)])
994          (cond
995            [(and (eq? 'scheme-object host-rep)
996                  (reference-bytevector? memory))
997             (bytevector-reference-set! memory (+ mem-offset offset) v)]
998            [(and (or (eq? 'void* host-rep)
999                      (eq? 'uptr host-rep))
1000                  (reference-bytevector? memory))
1001             (let ([v (cond
1002                        [(not v) #f]
1003                        [(bytes? v) v]
1004                        [(flvector? v) v]
1005                        [(authentic-cpointer? v)
1006                         (let-values ([(memory offset) (cpointer-address+offset v)])
1007                           (cond
1008                             [(integer? memory) (+ memory offset)]
1009                             [(zero? offset) memory]
1010                             [else (raise-arguments-error 'ptr-set!
1011                                                          "cannot install value into non-atomic memory"
1012                                                          "value" orig-v
1013                                                          "destination" orig-p)]))])])
1014               (cond
1015                 [(integer? v)
1016                  (if (fx= 8 (foreign-sizeof 'ptr))
1017                      (bytevector-u64-native-set! memory (+ mem-offset offset) v)
1018                      (bytevector-u32-native-set! memory (+ mem-offset offset) v))]
1019                 [else
1020                  (bytevector-reference-set! memory (+ mem-offset offset) v)]))]
1021            [else
1022             ;; Disable interrupts to avoid a GC:
1023             (with-interrupts-disabled*
1024              ;; Special treatment is needed for 'scheme-object, since
1025              ;; the host Scheme rejects the use of 'scheme-object with
1026              ;; `foreign-set!`
1027              (foreign-set! (if (eq? host-rep 'scheme-object)
1028                                'uptr
1029                                host-rep)
1030                            (+ (memory-address memory) mem-offset)
1031                            offset
1032                            (case host-rep
1033                              [(scheme-object) (object->reference-address v)]
1034                              [(void* uptr) (cpointer-address v)]
1035                              [else v])))])))])))
1036
1037(define (memcpy* to to-offset from from-offset len move?)
1038  (let ([to (unwrap-cpointer* 'memcpy to)]
1039        [from (unwrap-cpointer* 'memcpy from)])
1040    (with-interrupts-disabled*
1041     (let ([to (+ (cpointer*-address to) to-offset)]
1042           [from (+ (cpointer*-address from) from-offset)])
1043       (cond
1044         [(and move?
1045               ;; overlap?
1046               (or (<= to from (+ to len -1))
1047                   (<= from to (+ from len -1)))
1048               ;; shifting up?
1049               (< from to))
1050          ;; Copy from high to low to move in overlapping region
1051          (let loop ([len len])
1052            (unless (fx= len 0)
1053              (cond
1054                [(and (> (fixnum-width) 64)
1055                      (fx>= len 8))
1056                 (let ([len (fx- len 8)])
1057                   (foreign-set! 'integer-64 to len
1058                                 (foreign-ref 'integer-64 from len))
1059                   (loop len))]
1060                [(and (> (fixnum-width) 32)
1061                      (fx>= len 4))
1062                 (let ([len (fx- len 4)])
1063                   (foreign-set! 'integer-32 to len
1064                                 (foreign-ref 'integer-32 from len))
1065                   (loop len))]
1066                [(fx>= len 2)
1067                 (let ([len (fx- len 2)])
1068                   (foreign-set! 'integer-16 to len
1069                                 (foreign-ref 'integer-16 from len))
1070                   (loop len))]
1071                [else
1072                 (let ([len (fx- len 1)])
1073                   (foreign-set! 'integer-8 to len
1074                                 (foreign-ref 'integer-8 from len))
1075                   (loop len))])))]
1076         [else
1077          (let loop ([pos 0])
1078            (when (fx< pos len)
1079              (cond
1080                [(and (> (fixnum-width) 64)
1081                      (fx<= (fx+ pos 8) len))
1082                 (foreign-set! 'integer-64 to pos
1083                               (foreign-ref 'integer-64 from pos))
1084                 (loop (fx+ pos 8))]
1085                [(and (> (fixnum-width) 32)
1086                      (fx<= (fx+ pos 4) len))
1087                 (foreign-set! 'integer-32 to pos
1088                               (foreign-ref 'integer-32 from pos))
1089                 (loop (fx+ pos 4))]
1090                [(fx<= (fx+ pos 2) len)
1091                 (foreign-set! 'integer-16 to pos
1092                               (foreign-ref 'integer-16 from pos))
1093                 (loop (fx+ pos 2))]
1094                [else
1095                 (foreign-set! 'integer-8 to pos
1096                               (foreign-ref 'integer-8 from pos))
1097                 (loop (fx+ pos 1))])))])))))
1098
1099(define memcpy/memmove
1100  (case-lambda
1101   [(who cptr src-cptr count)
1102    (check who cpointer? cptr)
1103    (check who cpointer? src-cptr)
1104    (check who exact-nonnegative-integer? count)
1105    (memcpy* cptr 0 src-cptr 0 count (eq? who 'memmove))]
1106   [(who cptr offset/src-cptr/src-cptr src-cptr/offset/count count/count/type)
1107    (check who cpointer? cptr)
1108    (cond
1109     [(cpointer? offset/src-cptr/src-cptr)
1110      ;; use y or z of x/y/z
1111      (cond
1112       [(ctype? count/count/type)
1113        ;; use z of x/y/z
1114        (check who exact-nonnegative-integer? src-cptr/offset/count)
1115        (memcpy* cptr 0 (unwrap-cpointer who offset/src-cptr/src-cptr) 0 (* src-cptr/offset/count (ctype-sizeof count/count/type)) (eq? who 'memmove))]
1116       [else
1117        ;; use y of x/y/z
1118        (check who exact-integer? src-cptr/offset/count)
1119        (check who exact-nonnegative-integer? count/count/type)
1120        (memcpy* cptr 0 (unwrap-cpointer who offset/src-cptr/src-cptr) src-cptr/offset/count src-cptr/offset/count (eq? who 'memmove))])]
1121     [else
1122      ;; use x of x/y/z
1123      (check who exact-integer? offset/src-cptr/src-cptr)
1124      (check who cpointer? src-cptr/offset/count)
1125      (check who exact-nonnegative-integer? count/count/type)
1126      (memcpy* cptr offset/src-cptr/src-cptr src-cptr/offset/count 0 count/count/type (eq? who 'memmove))])]
1127   [(who cptr offset src-cptr src-offset/count count/type)
1128    (check who cpointer? cptr)
1129    (check who exact-integer? offset)
1130    (check who cpointer? src-cptr)
1131    (cond
1132     [(ctype? count/type)
1133      ;; use y of x/y
1134      (check who exact-nonnegative-integer? src-offset/count)
1135      (let ([sz (ctype-sizeof count/type)])
1136        (memcpy* cptr (* sz offset) src-cptr 0 (* src-offset/count sz) (eq? who 'memmove)))]
1137     [else
1138      ;; use x of x/y
1139      (check who exact-integer? src-offset/count)
1140      (check who exact-nonnegative-integer? count/type)
1141      (memcpy* cptr offset src-cptr src-offset/count count/type (eq? who 'memmove))])]
1142   [(who cptr offset src-cptr src-offset count type)
1143    (check who cpointer? cptr)
1144    (check who exact-integer? offset)
1145    (check who cpointer? src-cptr)
1146    (check who exact-integer? src-offset)
1147    (check who ctype? type)
1148    (let ([sz (ctype-sizeof type)])
1149      (memcpy* cptr (* offset sz) src-cptr (* src-offset sz) (* count sz) (eq? who 'memmove)))]))
1150
1151(define/who memcpy
1152  (case-lambda
1153   [(cptr src-cptr count)
1154    (memcpy/memmove who cptr src-cptr count)]
1155   [(cptr offset/src-cptr src-cptr/count count/type)
1156    (memcpy/memmove who cptr offset/src-cptr src-cptr/count count/type)]
1157   [(cptr offset src-cptr src-offset/count count/type)
1158    (memcpy/memmove who cptr offset src-cptr src-offset/count count/type)]
1159   [(cptr offset src-cptr src-offset count type)
1160    (memcpy/memmove who cptr offset src-cptr src-offset count type)]))
1161
1162(define/who memmove
1163  (case-lambda
1164   [(cptr src-cptr count)
1165    (memcpy/memmove who cptr src-cptr count)]
1166   [(cptr offset/src-cptr src-cptr/count count/type)
1167    (memcpy/memmove who cptr offset/src-cptr src-cptr/count count/type)]
1168   [(cptr offset src-cptr src-offset/count count/type)
1169    (memcpy/memmove who cptr offset src-cptr src-offset/count count/type)]
1170   [(cptr offset src-cptr src-offset count type)
1171    (memcpy/memmove who cptr offset src-cptr src-offset count type)]))
1172
1173;; ----------------------------------------
1174
1175(define (memset* to to-offset byte len)
1176  (let ([to (unwrap-cpointer* 'memset to)])
1177    (with-interrupts-disabled*
1178     (let ([to (+ (cpointer*-address to) to-offset)])
1179       (let loop ([to to] [len len])
1180         (unless (fx= len 0)
1181           (foreign-set! 'unsigned-8 to 0 byte)
1182           (loop (+ to 1) (fx- len 1))))))))
1183
1184(define/who memset
1185  (case-lambda
1186   [(cptr byte count)
1187    (check who cpointer? cptr)
1188    (check who byte? byte)
1189    (check who exact-nonnegative-integer? count)
1190    (memset* cptr 0 byte count)]
1191   [(cptr byte/offset count/byte type/count)
1192    (check who cpointer? cptr)
1193    (cond
1194     [(ctype? type/count)
1195      (check who byte? byte/offset)
1196      (check who exact-nonnegative-integer? count/byte)
1197      (memset* cptr 0 byte/offset (fx* count/byte (ctype-sizeof type/count)))]
1198     [else
1199      (check who exact-integer? byte/offset)
1200      (check who byte? count/byte)
1201      (check who exact-nonnegative-integer? type/count)
1202      (memset* cptr byte/offset count/byte type/count)])]
1203   [(cptr offset byte count type)
1204    (check who cpointer? cptr)
1205    (check who exact-integer? offset)
1206    (check who byte? byte)
1207    (check who exact-nonnegative-integer? count)
1208    (check who ctype? type)
1209    (memset* cptr (fx* offset (ctype-sizeof type)) byte (fx* count (ctype-sizeof type)))]))
1210
1211;; ----------------------------------------
1212
1213;; With finalization through an ordered guardian,
1214;; a "late" weak hash table is just a hash table.
1215(define (make-late-weak-hasheq)
1216  (make-weak-hasheq))
1217
1218;; Same for late weak boxes:
1219(define (make-late-weak-box b)
1220  (make-weak-box b))
1221
1222(define malloc
1223  ;; Recognize common ordering as fast cases, and dispatch to
1224  ;; a general handler to arbtrary argument order
1225  (case-lambda
1226   [(arg1)
1227    (cond
1228     [(nonnegative-fixnum? arg1)
1229      (normalized-malloc arg1 'atomic)]
1230     [(ctype? arg1)
1231      (normalized-malloc (ctype-sizeof arg1) (ctype-malloc-mode arg1))]
1232     [else
1233      (do-malloc (list arg1))])]
1234   [(arg1 arg2)
1235    (cond
1236     [(and (nonnegative-fixnum? arg1)
1237           (ctype? arg2))
1238      (normalized-malloc (* arg1 (ctype-sizeof arg2)) (ctype-malloc-mode arg2))]
1239     [(and (ctype? arg1)
1240           (nonnegative-fixnum? arg2))
1241      (normalized-malloc (* arg2 (ctype-sizeof arg1)) (ctype-malloc-mode arg1))]
1242     [(and (nonnegative-fixnum? arg1)
1243           (malloc-mode? arg2))
1244      (normalized-malloc arg1 arg2)]
1245     [else
1246      (do-malloc (list arg1 arg2))])]
1247   [(arg1 arg2 arg3) (do-malloc (list arg1 arg2 arg3))]
1248   [(arg1 arg2 arg3 arg4) (do-malloc (list arg1 arg2 arg3 arg4))]
1249   [(arg1 arg2 arg3 arg4 arg5) (do-malloc (list arg1 arg2 arg3 arg4 arg5))]))
1250
1251(define (do-malloc args)
1252  (let ([duplicate-argument
1253         (lambda (what a1 a2)
1254           (raise-arguments-error 'malloc
1255                                  (string-append "multiple " what " arguments")
1256                                  "first" a1
1257                                  "second" a2))])
1258    (let loop ([args args] [count #f] [type #f] [copy-from #f] [mode #f] [fail-mode #f])
1259      (cond
1260       [(null? args)
1261        (let* ([len (* (or count 1) (if type (ctype-sizeof type) 1))]
1262               [p (normalized-malloc len
1263                                     (or mode (if type (ctype-malloc-mode type) 'atomic)))])
1264          (when copy-from
1265            (memcpy* p 0 copy-from 0 len #f))
1266          p)]
1267       [(nonnegative-fixnum? (car args))
1268        (if count
1269            (duplicate-argument "size" count (car args))
1270            (loop (cdr args) (car args) type copy-from mode fail-mode))]
1271       [(ctype? (car args))
1272        (if type
1273            (duplicate-argument "type" type (car args))
1274            (loop (cdr args) count (car args) copy-from mode fail-mode))]
1275       [(and (cpointer? (car args)) (car args))
1276        (if copy-from
1277            (duplicate-argument "source for copy" copy-from (car args))
1278            (loop (cdr args) count type (car args) mode fail-mode))]
1279       [(malloc-mode? (car args))
1280        (if mode
1281            (duplicate-argument "mode" mode (car args))
1282            (loop (cdr args) count type copy-from (car args) fail-mode))]
1283       [(eq? (car args) 'failok)
1284        (if fail-mode
1285            (duplicate-argument "failure mode" fail-mode (car args))
1286            (loop (cdr args) count type copy-from mode (car args)))]
1287       [else
1288        (raise-argument-error 'malloc
1289                              (string-append "(or/c (and/c exact-nonnegative-integer? fixnum?)\n"
1290                                             "      ctype? cpointer?\n"
1291                                             "      (or/c 'raw 'atomic 'nonatomic 'tagged\n"
1292                                             "            'atomic-interior 'interior\n"
1293                                             "            'stubborn 'uncollectable 'eternal)\n"
1294                                             "      'fail-ok)")
1295                              (car args))]))))
1296
1297(define (normalized-malloc size mode)
1298  (cond
1299   [(eqv? size 0) #f]
1300   [(eq? mode 'raw)
1301    (make-cpointer (foreign-alloc size) #f)]
1302   [(eq? mode 'atomic)
1303    (make-cpointer (make-bytevector size) #f)]
1304   [(eq? mode 'nonatomic)
1305    (make-cpointer (make-reference-bytevector size) #f)]
1306   [(eq? mode 'atomic-interior)
1307    ;; This is not quite the same as Racket BC, because interior
1308    ;; pointers are not allowed as GCable pointers. So, "interior"
1309    ;; just means "doesn't move".
1310    (make-cpointer (make-immobile-bytevector size) #f)]
1311   [(eq? mode 'interior)
1312    ;; Ditto
1313    (make-cpointer (make-immobile-reference-bytevector size) #f)]
1314   [else
1315    (raise-unsupported-error 'malloc
1316                             (format "'~a mode is not supported" mode))]))
1317
1318(define/who (free p)
1319  (let ([p (unwrap-cpointer who p)])
1320    (with-interrupts-disabled*
1321     (foreign-free (cpointer-address p)))))
1322
1323(define (lock-cpointer p)
1324  (when (authentic-cpointer? p)
1325    (lock-object (cpointer-memory p))))
1326
1327(define (unlock-cpointer p)
1328  (when (authentic-cpointer? p)
1329    (unlock-object (cpointer-memory p))))
1330
1331(define-record-type (cpointer/cell make-cpointer/cell cpointer/cell?)
1332  (parent cpointer)
1333  (fields))
1334
1335(define immobile-cells (make-eq-hashtable))
1336
1337(define (malloc-immobile-cell v)
1338  (let ([vec (make-immobile-reference-bytevector (foreign-sizeof 'ptr))])
1339    (bytevector-reference-set! vec 0 v)
1340    (with-global-lock
1341     (eq-hashtable-set! immobile-cells vec #t))
1342    (make-cpointer vec #f)))
1343
1344(define (free-immobile-cell b)
1345  (with-global-lock
1346   (eq-hashtable-delete! immobile-cells (cpointer-memory b))))
1347
1348(define (immobile-cell-ref b)
1349  (bytevector-reference-ref (cpointer-memory b) 0))
1350
1351(define (immobile-cell->address b)
1352  (object->reference-address (cpointer-memory b)))
1353
1354(define (address->immobile-cell a)
1355  (make-cpointer (reference-address->object a) #f))
1356
1357(define (malloc-mode? v)
1358  (#%memq v '(raw atomic nonatomic tagged
1359                  atomic-interior interior
1360                  stubborn uncollectable eternal)))
1361
1362(define (end-stubborn-change p)
1363  (raise-unsupported-error 'end-stubborn-change))
1364
1365(define/who (extflvector->cpointer extfl-vector)
1366  (raise-unsupported-error who))
1367
1368(define/who (vector->cpointer vec)
1369  (raise-unsupported-error who))
1370
1371(define (flvector->cpointer flvec)
1372  (make-cpointer flvec #f))
1373
1374;; ----------------------------------------
1375
1376(define the-foreign-guardian (make-guardian))
1377
1378;; Can be called in any host thread, but all other
1379;; threads are stopped
1380(define (poll-foreign-guardian)
1381  (let ([v (the-foreign-guardian)])
1382    (when v
1383      (v)
1384      (poll-foreign-guardian))))
1385
1386(define (unsafe-add-global-finalizer v proc)
1387  (with-global-lock (the-foreign-guardian v proc)))
1388
1389;; ----------------------------------------
1390
1391(define eval/foreign
1392  (lambda (expr mode)
1393    (call-with-system-wind (lambda () (eval expr)))))
1394
1395(define (set-foreign-eval! proc)
1396  (set! eval/foreign proc))
1397
1398;; Cache generated code for an underlying foreign call or callable shape:
1399(define-thread-local ffi-expr->code (make-weak-hash))         ; expr to weak cell of code
1400(define-thread-local ffi-code->expr (make-weak-eq-hashtable)) ; keep exprs alive as long as code lives
1401
1402(define/who ffi-call
1403  (case-lambda
1404   [(p in-types out-type)
1405    (ffi-call p in-types out-type #f #f #f #f)]
1406   [(p in-types out-type abi)
1407    (ffi-call p in-types out-type abi #f #f #f #f)]
1408   [(p in-types out-type abi save-errno)
1409    (ffi-call p in-types out-type abi save-errno #f #f #f)]
1410   [(p in-types out-type abi save-errno orig-place?)
1411    (ffi-call p in-types out-type abi save-errno orig-place? #f #f #f)]
1412   [(p in-types out-type abi save-errno orig-place? lock-name)
1413    (ffi-call p in-types out-type abi save-errno orig-place? lock-name #f #f #f)]
1414   [(p in-types out-type abi save-errno orig-place? lock-name blocking?)
1415    (ffi-call p in-types out-type abi save-errno orig-place? lock-name blocking? #f #f)]
1416   [(p in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after)
1417    (ffi-call p in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after #f)]
1418   [(p in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after exns?)
1419    (check who cpointer? p)
1420    (check-ffi-call who in-types out-type abi varargs-after save-errno lock-name)
1421    ((ffi-call/callable #t in-types out-type abi varargs-after
1422                        save-errno lock-name (and blocking? #t) (and orig-place? #t) #f (and exns? #t)
1423                        #f)
1424     p)]))
1425
1426(define/who ffi-call-maker
1427  (case-lambda
1428   [(in-types out-type)
1429    (ffi-call-maker in-types out-type #f #f #f #f #f)]
1430   [(in-types out-type abi)
1431    (ffi-call-maker in-types out-type abi #f #f #f #f)]
1432   [(in-types out-type abi save-errno)
1433    (ffi-call-maker in-types out-type abi save-errno #f #f #f)]
1434   [(in-types out-type abi save-errno orig-place?)
1435    (ffi-call-maker in-types out-type abi save-errno orig-place? #f #f #f)]
1436   [(in-types out-type abi save-errno orig-place? lock-name)
1437    (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name #f #f #f)]
1438   [(in-types out-type abi save-errno orig-place? lock-name blocking?)
1439    (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name blocking? #f #f)]
1440   [(in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after)
1441    (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after #f)]
1442   [(in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after exns?)
1443    (check-ffi-call who in-types out-type abi varargs-after save-errno lock-name)
1444    (ffi-call/callable #t in-types out-type abi varargs-after
1445                       save-errno lock-name (and blocking? #t) (and orig-place? #t) #f (and exns? #t)
1446                       #f)]))
1447
1448(define (check-ffi-call who in-types out-type abi varargs-after save-errno lock-name)
1449  (check-ffi who in-types out-type abi varargs-after)
1450  (check who (lambda (save-errno) (#%memq save-errno '(#f posix windows)))
1451         :contract "(or/c #f 'posix 'windows)"
1452         save-errno)
1453  (check who string? :or-false lock-name))
1454
1455;; For sanity checking of callbacks during a blocking callout:
1456(define-virtual-register currently-blocking? #f)
1457
1458(define-syntax-rule (retain v ... e)
1459  ;; Make sure that the `v ...` stay live until `e` produces a result,
1460  ;; so uses of the FFI can rely on passing an argument to a foreign
1461  ;; function as retaining the argument until the function returns.
1462  (let ([result e])
1463    (keep-live v) ...
1464    result))
1465
1466(define call-locks (make-eq-hashtable))
1467
1468(define (ffi-call/callable call? in-types out-type abi varargs-after
1469                           save-errno lock-name blocking? orig-place? atomic? exns?
1470                           async-apply)
1471  (let* ([conv* (let ([conv* (case abi
1472                               [(stdcall) '(__stdcall)]
1473                               [(sysv) '(__cdecl)]
1474                               [else '()])])
1475                  (if varargs-after
1476                      (cons `(__varargs_after ,varargs-after) conv*)
1477                      conv*))]
1478         [by-value? (lambda (type)
1479                      ;; An 'array rep is compound, but should be
1480                      ;; passed as a pointer, so only pass 'struct and
1481                      ;; 'union "by value":
1482                      (#%memq (ctype-host-rep type) '(struct union)))]
1483         [array-rep-to-pointer-rep (lambda (host-rep)
1484                                     (if (eq? host-rep 'array)
1485                                         'void*
1486                                         host-rep))]
1487         [next!-id (let ([counter 0])
1488                     ;; Like `gensym`, but deterministic --- and doesn't
1489                     ;; have to be totally unique, as long as it doesn't
1490                     ;; collide with other code that we generate
1491                     (lambda ()
1492                       (set! counter (add1 counter))
1493                       (string->symbol (string-append "type_" (number->string counter)))))]
1494         [ids (map (lambda (in-type)
1495                     (and (by-value? in-type)
1496                          (next!-id)))
1497                   in-types)]
1498         [ret-id (and (by-value? out-type)
1499                      (next!-id))]
1500         [decls (let loop ([in-types in-types] [ids ids] [decls '()])
1501                  (cond
1502                   [(null? in-types) decls]
1503                   [(car ids)
1504                    (let ([id-decls ((compound-ctype-get-decls (car in-types)) (car ids) next!-id)])
1505                      (loop (cdr in-types) (cdr ids) (append decls id-decls)))]
1506                   [else
1507                    (loop (cdr in-types) (cdr ids) decls)]))]
1508         [ret-decls (if ret-id
1509                        ((compound-ctype-get-decls out-type) ret-id next!-id)
1510                        '())]
1511         [ret-size (and ret-id (ctype-sizeof out-type))]
1512         [ret-malloc-mode (and ret-id (compound-ctype-malloc-mode out-type))]
1513         [gen-proc+ret-maker+arg-makers
1514          (let ([expr `(let ()
1515                         ,@decls
1516                         ,@ret-decls
1517                         (list
1518                          (lambda (to-wrap)
1519                            (,(if call? 'foreign-procedure 'foreign-callable)
1520                             ,@conv*
1521                             ,@(if (or blocking? async-apply) '(__collect_safe) '())
1522                             to-wrap
1523                             ,(map (lambda (in-type id)
1524                                     (if id
1525                                         `(& ,id)
1526                                         (array-rep-to-pointer-rep
1527                                          (ctype-host-rep in-type))))
1528                                   in-types ids)
1529                             ,(if ret-id
1530                                  `(& ,ret-id)
1531                                  (array-rep-to-pointer-rep
1532                                   (ctype-host-rep out-type)))))
1533                          ,(and call?
1534                                ret-id
1535                                `(lambda (p)
1536                                   (make-ftype-pointer ,ret-id p)))
1537                          ,@(if call?
1538                                (map (lambda (id)
1539                                       (and id
1540                                            `(lambda (p)
1541                                               (make-ftype-pointer ,id p))))
1542                                     ids)
1543                                '())))])
1544            (let* ([wb (with-interrupts-disabled*
1545                        (hash-ref ffi-expr->code expr #f))]
1546                   [code (if wb (car wb) #!bwp)])
1547              (if (eq? code #!bwp)
1548                  (let ([code (eval/foreign expr (if call? 'comp-ffi-call 'comp-ffi-back))])
1549                    (hashtable-set! ffi-code->expr (car code) expr)
1550                    (with-interrupts-disabled*
1551                     (hash-set! ffi-expr->code expr (weak-cons code #f)))
1552                    code)
1553                  code)))]
1554         [gen-proc (car gen-proc+ret-maker+arg-makers)]
1555         [ret-maker (cadr gen-proc+ret-maker+arg-makers)]
1556         [arg-makers (cddr gen-proc+ret-maker+arg-makers)]
1557         [async-callback-queue (and (procedure? async-apply) (current-async-callback-queue))]
1558         [lock (and lock-name
1559                    (with-global-lock
1560                     (or (eq-hashtable-ref call-locks (string->symbol lock-name) #f)
1561                         (let ([lock (make-mutex)])
1562                           (eq-hashtable-set! call-locks (string->symbol lock-name) lock)
1563                           lock))))])
1564    (cond
1565     [call?
1566      (cond
1567       [(and (not ret-id)
1568             (not blocking?)
1569             (not orig-place?)
1570             (not exns?)
1571             (not save-errno)
1572             (#%andmap (lambda (in-type)
1573                         (case (ctype-host-rep in-type)
1574                           [(scheme-object struct union) #f]
1575                           [else #t]))
1576                       in-types))
1577        (let ([arity-mask (bitwise-arithmetic-shift-left 1 (length in-types))])
1578          (lambda (to-wrap)
1579            (let* ([proc-p (unwrap-cpointer 'ffi-call to-wrap)]
1580                   [proc (and (not (cpointer-needs-lock? proc-p))
1581                              (gen-proc (cpointer-address proc-p)))]
1582                   [name (cpointer->name proc-p)]
1583                   [unwrap (lambda (arg in-type)
1584                             (let ([c (s->c name in-type arg)])
1585                               (if (cpointer? c)
1586                                   (unwrap-cpointer 'ffi-call c)
1587                                   c)))]
1588                   [unpack (lambda (arg in-type)
1589                             (case (array-rep-to-pointer-rep (ctype-host-rep in-type))
1590                               [(void* uptr) (cpointer-address arg)]
1591                               [else arg]))])
1592              (do-procedure-reduce-arity-mask
1593               (cond
1594                 [proc
1595                  (let-syntax ([gen (lambda (stx)
1596                                      (syntax-case stx ()
1597                                        [(_ id ...)
1598                                         (with-syntax ([(type ...) (generate-temporaries #'(id ...))]
1599                                                       [(orig ...) (generate-temporaries #'(id ...))])
1600                                           (let ([make-proc
1601                                                  (lambda (lock)
1602                                                    #`(lambda (orig ...)
1603                                                        (let ([id (unwrap orig type)] ...)
1604                                                          (when #,lock (mutex-acquire #,lock))
1605                                                          (let ([r (retain
1606                                                                    orig ...
1607                                                                    (with-interrupts-disabled*
1608                                                                     (proc (unpack id type) ...)))])
1609                                                            (when #,lock (mutex-release #,lock))
1610                                                            (c->s out-type r)))))])
1611                                             #`(let*-values ([(type in-types) (values (car in-types) (cdr in-types))]
1612                                                             ...)
1613                                                 (if lock
1614                                                     #,(make-proc #'lock)
1615                                                     #,(make-proc #'#f)))))]))])
1616                    (case arity-mask
1617                      [(1) (gen)]
1618                      [(2) (gen a)]
1619                      [(4) (gen a b)]
1620                      [(8) (gen a b c)]
1621                      [(16) (gen a b c d)]
1622                      [(32) (gen a b c d e)]
1623                      [(64) (gen a b c d e f)]
1624                      [(128) (gen a b c d e f g)]
1625                      [(256) (gen a b c d e f g h)]
1626                      [else
1627                       (lambda orig-args
1628                         (let ([args (map (lambda (a t) (unwrap a t)) orig-args in-types)])
1629                           (c->s out-type (with-interrupts-disabled*
1630                                           (retain
1631                                            orig-args
1632                                            (#%apply proc (map (lambda (a t) (unpack a t)) args in-types)))))))]))]
1633                 [else
1634                  (lambda orig-args
1635                    (let ([args (map (lambda (a t) (unwrap a t)) orig-args in-types)])
1636                      (when lock (mutex-acquire lock))
1637                      (let ([r (with-interrupts-disabled*
1638                                (retain
1639                                 orig-args
1640                                 (#%apply (gen-proc (cpointer-address proc-p))
1641                                          (map (lambda (a t) (unpack a t)) args in-types))))])
1642                        (when lock (mutex-release lock))
1643                        (c->s out-type r))))])
1644               arity-mask
1645               name))))]
1646       [else
1647        (lambda (to-wrap)
1648          (let* ([proc-p (unwrap-cpointer 'ffi-call to-wrap)]
1649                 [name (cpointer->name proc-p)])
1650            (do-procedure-reduce-arity-mask
1651             (lambda orig-args
1652               (let* ([args (map (lambda (orig-arg in-type)
1653                                   (let ([arg (s->c name in-type orig-arg)])
1654                                     (if (and (cpointer? arg)
1655                                              (not (eq? 'scheme-object (ctype-host-rep in-type))))
1656                                         (unwrap-cpointer 'ffi-call arg)
1657                                         arg)))
1658                                 orig-args in-types)]
1659                      [r (let ([ret-ptr (and ret-id
1660                                             ;; result is a struct type; need to allocate space for it
1661                                             (normalized-malloc ret-size ret-malloc-mode))])
1662                           (let ([go (lambda ()
1663                                       (when lock (mutex-acquire lock))
1664                                       (with-interrupts-disabled*
1665                                        (when blocking? (currently-blocking? #t))
1666                                        (retain
1667                                         orig-args
1668                                         (let ([r (let ([args (append
1669                                                               (if ret-ptr
1670                                                                   (begin
1671                                                                     (lock-cpointer ret-ptr)
1672                                                                     (list (ret-maker (cpointer-address ret-ptr))))
1673                                                                   '())
1674                                                               (map (lambda (arg in-type maker)
1675                                                                      (let ([host-rep (array-rep-to-pointer-rep
1676                                                                                       (ctype-host-rep in-type))])
1677                                                                        (case host-rep
1678                                                                          [(void* uptr) (cpointer-address arg)]
1679                                                                          [(struct union)
1680                                                                           (maker (cpointer-address arg))]
1681                                                                          [else arg])))
1682                                                                    args in-types arg-makers))]
1683                                                        [proc (gen-proc (cpointer-address proc-p))])
1684                                                    (cond
1685                                                      [(not exns?)
1686                                                       (#%apply proc args)]
1687                                                      [else
1688                                                       (call-guarding-foreign-escape
1689                                                        (lambda () (#%apply proc args))
1690                                                        (lambda ()
1691                                                          (when lock (mutex-release lock))
1692                                                          (when blocking? (currently-blocking? #f))))]))])
1693                                           (when lock (mutex-release lock))
1694                                           (when blocking? (currently-blocking? #f))
1695                                           (case save-errno
1696                                             [(posix) (thread-cell-set! errno-cell (get-errno))]
1697                                             [(windows) (thread-cell-set! errno-cell (get-last-error))])
1698                                           (cond
1699                                            [ret-ptr (unlock-cpointer ret-ptr) ret-ptr]
1700                                            [(eq? (ctype-our-rep out-type) 'gcpointer)
1701                                             (addr->gcpointer-memory r)]
1702                                            [else r])))))])
1703                             (if (and orig-place?
1704                                      (not (eqv? 0 (get-thread-id))))
1705                                 (async-callback-queue-call orig-place-async-callback-queue (lambda (th) (th)) (lambda () (go)) #f #t #t)
1706                                 (go))))])
1707                 (c->s out-type r)))
1708             (fxsll 1 (length in-types))
1709             name)))])]
1710     [else ; callable
1711      (lambda (to-wrap)
1712        (gen-proc (lambda args ; if ret-id, includes an extra initial argument to receive the result
1713                    (let ([v (call-as-atomic-callback
1714                              (lambda ()
1715                                (unless async-apply
1716                                  ;; Sanity check; if the check fails, things can go bad from here on,
1717                                  ;; but we try to continue, anyway
1718                                  (when (currently-blocking?)
1719                                    (#%printf "non-async in callback during blocking: ~s\n" to-wrap)))
1720                                (s->c
1721                                 'callback
1722                                 out-type
1723                                 (apply to-wrap
1724                                        (let loop ([args (if ret-id (cdr args) args)] [in-types in-types])
1725                                          (cond
1726                                           [(null? args) '()]
1727                                           [else
1728                                            (let* ([arg (car args)]
1729                                                   [type (car in-types)]
1730                                                   [arg (c->s type
1731                                                              (case (ctype-host-rep type)
1732                                                                [(struct union)
1733                                                                 ;; Like Racket BC, refer to argument on stack:
1734                                                                 (make-cpointer (ftype-pointer-address arg) #f)
1735                                                                 #;
1736                                                                 (let* ([size (compound-ctype-size type)]
1737                                                                        [addr (ftype-pointer-address arg)]
1738                                                                        [bstr (make-bytevector size)])
1739                                                                   (memcpy* bstr 0 addr 0 size #f)
1740                                                                   (make-cpointer bstr #f))]
1741                                                                [else
1742                                                                 (cond
1743                                                                  [(eq? (ctype-our-rep type) 'gcpointer)
1744                                                                   (addr->gcpointer-memory arg)]
1745                                                                  [else arg])]))])
1746                                              (cons arg (loop (cdr args) (cdr in-types))))])))))
1747                              (or #t atomic?) ; force all callbacks to be atomic
1748                              async-apply
1749                              async-callback-queue)])
1750                      (if ret-id
1751                          (let* ([size (compound-ctype-size out-type)]
1752                                 [addr (ftype-pointer-address (car args))])
1753                            (memcpy* addr 0 v 0 size #f))
1754                          (case (ctype-host-rep out-type)
1755                            [(void* uptr) (cpointer-address v)]
1756                            [else v]))))))])))
1757
1758(define (types->reps types next!-id)
1759  (let loop ([types types] [reps '()] [decls '()])
1760    (cond
1761     [(null? types) (values (reverse reps) decls)]
1762     [else
1763      (let ([type (car types)])
1764        (if (compound-ctype? type)
1765            (let* ([id (next!-id)]
1766                   [id-decls ((compound-ctype-get-decls type) id next!-id)])
1767              (loop (cdr types) (cons id reps) (append id-decls decls)))
1768            (loop (cdr types) (cons (ctype-host-rep type) reps) decls)))])))
1769
1770;; Rely on the fact that a virtual register defaults to 0 to detect a
1771;; thread that we didn't start.
1772(define PLACE-UNKNOWN-THREAD 0)
1773(define PLACE-KNOWN-THREAD 1)
1774(define PLACE-MAIN-THREAD 2)
1775(define-virtual-register place-thread-category PLACE-KNOWN-THREAD)
1776(define (register-as-place-main!)
1777  (place-thread-category PLACE-MAIN-THREAD))
1778
1779(define orig-place-async-callback-queue #f)
1780(define (remember-original-place!)
1781  (set! orig-place-async-callback-queue (current-async-callback-queue)))
1782
1783;; Can be called in any Scheme thread
1784(define (call-as-atomic-callback thunk atomic? async-apply async-callback-queue)
1785  (cond
1786   [(eqv? (place-thread-category) PLACE-MAIN-THREAD)
1787    ;; In the main thread of a place. We must have gotten here by a
1788    ;; foreign call that called back, so interrupts are currently
1789    ;; disabled.
1790    (cond
1791     [(not atomic?)
1792      ;; reenable interrupts
1793      (enable-interrupts)
1794      (let ([v (thunk)])
1795        (disable-interrupts)
1796        v)]
1797     [else
1798      ;; Inform the scheduler that it's in atomic mode
1799      (scheduler-start-atomic)
1800      ;; Now that the schedule is in atomic mode, reenable interrupts (for GC)
1801      (enable-interrupts)
1802      ;; See also `call-guarding-foreign-escape`, which will need to take
1803      ;; appropriate steps if `(thunk)` escapes, which currently means ending
1804      ;; the scheduler's atomic mode
1805      (let ([v (thunk)])
1806        (disable-interrupts)
1807        (scheduler-end-atomic)
1808        v)])]
1809   [(box? async-apply)
1810    ;; Not in a place's main thread; return the box's content
1811    (unbox async-apply)]
1812   [else
1813    ;; Not in a place's main thread; queue an async callback
1814    ;; and wait for the response
1815    (let ([known-thread? (eqv? (place-thread-category) PLACE-KNOWN-THREAD)])
1816      (unless known-thread? (ensure-virtual-registers))
1817      (async-callback-queue-call async-callback-queue
1818                                 async-apply
1819                                 thunk
1820                                 ;; If we created this thread by `fork-pthread`, we must
1821                                 ;; have gotten here by a foreign call, so interrupts are
1822                                 ;; currently disabled
1823                                 known-thread?
1824                                 ;; In a thread created by `fork-pthread`, we'll have to tell
1825                                 ;; the scheduler to be in atomic mode:
1826                                 known-thread?
1827                                 ;; Wait for result:
1828                                 #t))]))
1829
1830(define (call-enabling-ffi-callbacks proc)
1831  (disable-interrupts)
1832  (let ([v (proc)])
1833    (enable-interrupts)
1834    v))
1835
1836(define scheduler-start-atomic void)
1837(define scheduler-end-atomic void)
1838(define (set-scheduler-atomicity-callbacks! start-atomic end-atomic)
1839  (set! scheduler-start-atomic start-atomic)
1840  (set! scheduler-end-atomic end-atomic))
1841
1842;; ----------------------------------------
1843
1844;; Call `thunk` to enter a foreign call while wrapping it with a way
1845;; to escape with an exception from a foreign callback during the
1846;; call:
1847(define (call-guarding-foreign-escape thunk clean-up)
1848  ((call-with-c-return
1849    (lambda ()
1850      (call-with-current-continuation
1851       (lambda (esc)
1852         (call-with-exception-handler
1853          (lambda (x)
1854            ;; Deliver an exception re-raise after returning back
1855            ;; from `call-with-c-return`:
1856            (|#%app| esc (lambda ()
1857                           (scheduler-end-atomic) ; error in callback means during atomic mode
1858                           (clean-up)
1859                           (raise x))))
1860          (lambda ()
1861            (call-with-values thunk
1862              ;; Deliver successful values after returning back from
1863              ;; `call-with-c-return`:
1864              (case-lambda
1865               [(v) (lambda () v)]
1866               [args (lambda () (#%apply values args))]))))))))))
1867
1868;; `call-with-c-return` looks like a foreign function, due to a "cast"
1869;; to and from a callback, so returning from `call-with-c-return` will
1870;; pop and C frame stacks (via longjmp internally) that were pushed
1871;; since `call-with-c-return` was called.
1872(define call-with-c-return
1873  (let ([call (lambda (thunk) (thunk))])
1874    (define-ftype ptr->ptr (function (ptr) ptr))
1875    (cond
1876      [(not (eq? (machine-type) (#%$target-machine)))
1877       (lambda (thunk) (#%error 'call-with-c-return "cannot use while cross-compiling"))]
1878      [else
1879       (let ([fptr (make-ftype-pointer ptr->ptr call)])
1880         (let ([v (ftype-ref ptr->ptr () fptr)])
1881           ;; must leave the callable code object locked
1882           v))])))
1883
1884;; ----------------------------------------
1885
1886(define-record-type (callback create-callback ffi-callback?)
1887  (fields code))
1888
1889(define/who ffi-callback
1890  (case-lambda
1891   [(proc in-types out-type)
1892    (ffi-callback proc in-types out-type #f #f #f #f)]
1893   [(proc in-types out-type abi)
1894    (ffi-callback proc in-types out-type abi #f #f #f)]
1895   [(proc in-types out-type abi atomic?)
1896    (ffi-callback proc in-types out-type abi atomic? #f #f)]
1897   [(proc in-types out-type abi atomic? async-apply)
1898    (ffi-callback proc in-types out-type abi atomic? #f)]
1899   [(proc in-types out-type abi atomic? async-apply varargs-after)
1900    (check who procedure? proc)
1901    (check-ffi-callback who in-types out-type abi varargs-after async-apply)
1902    ((ffi-callback-maker* in-types out-type abi varargs-after atomic? async-apply) proc)]))
1903
1904(define/who ffi-callback-maker
1905  (case-lambda
1906   [(in-types out-type)
1907    (ffi-callback-maker in-types out-type #f #f #f #f)]
1908   [(in-types out-type abi)
1909    (ffi-callback-maker in-types out-type abi #f #f #f)]
1910   [(in-types out-type abi atomic?)
1911    (ffi-callback-maker in-types out-type abi atomic? #f #f)]
1912   [(in-types out-type abi atomic? async-apply)
1913    (ffi-callback-maker in-types out-type abi atomic? async-apply #f)]
1914   [(in-types out-type abi atomic? async-apply varargs-after)
1915    (check-ffi-callback who in-types out-type abi varargs-after async-apply)
1916    (ffi-callback-maker* in-types out-type abi varargs-after atomic? async-apply)]))
1917
1918(define (ffi-callback-maker* in-types out-type abi varargs-after atomic? async-apply)
1919  (let ([make-code (ffi-call/callable #f in-types out-type abi varargs-after
1920                                      #f #f #f #f (and atomic? #t) #f
1921                                      async-apply)])
1922    (lambda (proc)
1923      (check 'make-ffi-callback procedure? proc)
1924      (create-callback (make-code proc)))))
1925
1926(define (check-ffi-callback who in-types out-type abi varargs-after async-apply)
1927  (check-ffi who in-types out-type abi varargs-after)
1928  (check who (lambda (async-apply)
1929               (or (not async-apply)
1930                   (box? async-apply)
1931                   (and (procedure? async-apply)
1932                        (unsafe-procedure-and-arity-includes? async-apply 1))))
1933         :contract "(or/c #f (procedure-arity-includes/c 1) box?)"
1934         async-apply))
1935
1936(define (check-ffi who in-types out-type abi varargs-after)
1937  (check who (lambda (l)
1938               (and (list? l)
1939                    (andmap ctype? l)))
1940         :contract "(listof ctype?)"
1941         in-types)
1942  (check who ctype? out-type)
1943  (check who (lambda (a) (#%memq a '(#f default stdcall sysv)))
1944         :contract "(or/c #f 'default 'stdcall 'sysv)"
1945         abi)
1946  (check who (lambda (varargs-after) (or (not varargs-after)
1947                                         (and (exact-positive-integer? varargs-after))))
1948         :contract "(or/c #f exact-positive-integer?)"
1949         varargs-after)
1950  (when  varargs-after
1951    (let ([len (length in-types)])
1952      (when (> varargs-after len)
1953        (raise-arguments-error who
1954                               "varargs-after value is too large"
1955                               "given value" varargs-after
1956                               "argument count" len)))))
1957
1958;; ----------------------------------------
1959
1960(define/who (make-sized-byte-string cptr len)
1961  (check who cpointer? cptr)
1962  (check who exact-nonnegative-integer? len)
1963  (raise-unsupported-error who))
1964
1965(define errno-cell (make-thread-cell 0))
1966
1967(define/who saved-errno
1968  (case-lambda
1969   [() (thread-cell-ref errno-cell)]
1970   [(v)
1971    (check who exact-integer? v)
1972    (thread-cell-set! errno-cell v)]))
1973
1974(define/who (lookup-errno sym)
1975  (check who symbol? sym)
1976  (let ([errno-alist
1977         (case (system-type 'os*)
1978           [(linux) (linux-errno-alist)]
1979           [(macosx darwin) (macosx-errno-alist)]
1980           [(windows) (windows-errno-alist)]
1981           [(freebsd) (freebsd-errno-alist)]
1982           [(openbsd) (openbsd-errno-alist)]
1983           [(netbsd) (netbsd-errno-alist)]
1984           [(solaris) (solaris-errno-alist)]
1985           [else (raise-unsupported-error who)])])
1986    (cond
1987     [(assq sym errno-alist) => cdr]
1988     [else #f])))
1989
1990;; function is called with interrupts disabled
1991(define get-errno
1992  (cond
1993   [(not (#%memq (machine-type) '(a6nt ta6nt i3nt ti3nt)))
1994    (foreign-procedure "(cs)s_errno" () int)]
1995   [else
1996    ;; On Windows, `errno` could be a different one from
1997    ;; `_errno` in MSVCRT. Therefore fallback to the foreign function.
1998    ;; See `save_errno_values` in `foreign.c` from Racket BC for more
1999    ;; information.
2000    (load-shared-object "msvcrt.dll")
2001    (let ([get-&errno (foreign-procedure "_errno" () void*)])
2002      (lambda ()
2003        (foreign-ref 'int (get-&errno) 0)))]))
2004
2005;; function is called with interrupts disabled
2006(define get-last-error
2007  (case (machine-type)
2008    [(a6nt ta6nt i3nt ti3nt)
2009     (load-shared-object "kernel32.dll")
2010     (foreign-procedure "GetLastError" () int)]
2011    [else (lambda () 0)]))
2012
2013;; ----------------------------------------
2014
2015(define process-global-table (make-hashtable equal-hash-code equal?))
2016
2017(define/who (unsafe-register-process-global key val)
2018  (check who bytes? key)
2019  (with-global-lock
2020   (cond
2021    [(not val)
2022     (hashtable-ref process-global-table key #f)]
2023    [else
2024     (let ([old-val (hashtable-ref process-global-table key #f)])
2025       (cond
2026        [(not old-val)
2027         (hashtable-set! process-global-table (bytes-copy key) val)
2028         #f]
2029        [else old-val]))])))
2030
2031;; ----------------------------------------
2032
2033(define (set-cpointer-hash!)
2034  (record-type-equal-procedure (record-type-descriptor cpointer)
2035                               (lambda (a b eql?)
2036                                 (ptr-equal? a b)))
2037  (record-type-hash-procedure (record-type-descriptor cpointer)
2038                              (lambda (a hc)
2039                                (if (number? (cpointer-memory a))
2040                                    (hc (+ (cpointer-memory a)
2041                                           (ptr-offset* a)))
2042                                    (eq-hash-code (cpointer-memory a))))))
2043