File: proposal.scm

package info (click to toggle)
scheme48 1.9.2-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 18,232 kB
  • sloc: lisp: 88,907; ansic: 87,519; sh: 3,224; makefile: 771
file content (565 lines) | stat: -rw-r--r-- 19,044 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
; Part of Scheme 48 1.9.  See file COPYING for notices and license.

; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber


; Proposals are four-element vectors.  There doesn't seem to be much point
; in making a separate primitive data type for them.  The four values are:
;   in-use?	- boolean value, true if the proposal is the current proposal
;		  for some thread
;   d-log	- a vector of log entries for descriptor reads and writes
;   b-log	- a vector of log entries for single byte reads and writes
;   copy-list	- a list of blocks of bytes to be copied if the proposal
;		  succeeds
;
; Each log is also a vector.  Every log entry has four values:
;   stob	- the STored OBject which is being read and/or written
;   index	- the index within STOB
;   verify	- the value that the proposal assumes will be at INDEX in STOB;
;		  a special marker value is used if the location is written
;		  before it is read
;   value	- the value that should be put at INDEX in STOB when the
;		  proposal succeeds
; Logs may have unused slots after the final entry.  The final entry in a log
; is followed by #f.

; Proposal offsets and accessors.

(define proposal-size 4)
(define proposal-in-use?-index 0)
(define proposal-d-log-index 1)
(define proposal-b-log-index 2)
(define proposal-copy-list-index 3)

(define (proposal-in-use? proposal)
  (vm-vector-ref proposal proposal-in-use?-index))

(define (set-proposal-in-use?! proposal value)
  (vm-vector-set! proposal proposal-in-use?-index value))

(define (proposal-d-log proposal)
  (vm-vector-ref proposal proposal-d-log-index))

(define (proposal-b-log proposal)
  (vm-vector-ref proposal proposal-b-log-index))

(define (proposal-copies proposal)
  (vm-vector-ref proposal proposal-copy-list-index))

(define (set-proposal-copies! proposal value)
  (vm-vector-set! proposal proposal-copy-list-index value))

; Log offsets and accessors.

(define log-entry-size 4)
(define (log-entry-stob   log i) (vm-vector-ref log i))
(define (log-entry-index  log i) (vm-vector-ref log (+ i 1)))
(define (log-entry-value  log i) (vm-vector-ref log (+ i 2)))
(define (log-entry-verify log i) (vm-vector-ref log (+ i 3)))

(define (set-log-entry-value! log i value)
  (vm-vector-set! log (+ i 2) value))

; Block copy accessors.

(define (copy-from copy) (vm-vector-ref copy 0))
(define (copy-to   copy) (vm-vector-ref copy 2))
(define (copy-next copy) (vm-vector-ref copy 5))

(define (copy-from-index copy) (extract-fixnum (vm-vector-ref copy 1)))
(define (copy-to-index   copy) (extract-fixnum (vm-vector-ref copy 3)))
(define (copy-count      copy) (extract-fixnum (vm-vector-ref copy 4)))

;----------------
; A length-one vector containing #f.  This is used when clearing a protocols
; logs after the protocol has committed.

(define *empty-log*)

; Initialize the above registers and trace them when a GC occurs.

(define (initialize-proposals!+gc)
  (set! *empty-log* (vm-make-vector+gc 1))
  (vm-vector-set! *empty-log* 0 false))

(add-gc-root!
  (lambda ()
    (set! *empty-log* (s48-trace-value *empty-log*))))

; Just return the current proposal.

(define-primitive current-proposal ()
  (lambda ()
    (current-proposal))
  return)

; Install a new proposol.  We do a little error checking and set and clear
; the in-use? flags of the incoming and outgoing proposals.

(define-primitive set-current-proposal! (any->)
  (lambda (proposal)
    (cond ((or (false? proposal)
	       (and (vm-vector? proposal)
		    (= proposal-size (vm-vector-length proposal))
		    (false? (proposal-in-use? proposal))))
	   (let ((old (current-proposal)))
	     (if (not (false? old))
		 (set-proposal-in-use?! old false)))
	   (if (not (false? proposal))
	       (set-proposal-in-use?! proposal true))
	   (set-current-proposal! proposal)
	   (goto return unspecific-value))
	  ((vm-eq? proposal (current-proposal))
	   (goto return unspecific-value))
	  (else
	   (raise-exception wrong-type-argument 0 proposal)))))

;----------------
; Add an entry to a current log, increasing the size of the log if necessary.
; VERIFY? is true if we need to verify that VALUE is in STOB at INDEX when
; trying to commit to the proposal.  VERIFY? is false if a write occurs before
; any reads.

(define (add-log-entry+gc proposal-index i stob index value verify?)
  (let* ((proposal (current-proposal))
	 (log-size (vm-vector-length
		    (vm-vector-ref proposal proposal-index))))
    (receive (proposal stob value)
	(if (= i (- log-size 1))
	    (begin
	      (save-temp0! stob)
	      (save-temp1! value)
	      (extend-log!+gc proposal-index log-size)
	      (values (current-proposal)
		      (recover-temp0!)
		      (recover-temp1!)))
	    (values proposal stob value))
      (let ((log (vm-vector-ref proposal proposal-index)))
	(vm-vector-set! log i       stob)
	(vm-vector-set! log (+ i 1) index)
	(vm-vector-set! log (+ i 2) value)
	(vm-vector-set! log (+ i 3) (if verify? value unreleased-value))
	(vm-vector-set! log (+ i 4) false)	; end marker
	value))))

; The new log has twice the number of entries as the old one unless the old
; one has no entries, in which case we make room for four.
; We clear the currently unused portion of the log to avoid GC problems.

(define (extend-log!+gc proposal-index log-size)
  (let* ((new-size (if (= log-size 1)
		       (+ 1 (* log-entry-size 4))
		       (- (* log-size 2) 1)))
	 (new (vm-make-vector+gc new-size))
	 (proposal (current-proposal)))
    (if (< 1 log-size)
	(copy-memory! (address-after-header (proposal-d-log proposal))
		      (address-after-header new)
		      (cells->a-units (- log-size 1))))
    (do ((i (+ log-size log-entry-size) (+ i 1)))
	((= i new-size))
      (vm-vector-set! new i (enter-fixnum 0)))
    (vm-vector-set! proposal proposal-index new)))

; Add a byte-copy-block record to the current proposal.

(define (add-block-copy!+gc from from-index to to-index count)
  (save-temp0! from)
  (save-temp1! to)
  (let* ((entry (vm-make-vector+gc 6))
	 (proposal (current-proposal)))
    (vm-vector-set! entry 0 (recover-temp0!))
    (vm-vector-set! entry 1 from-index)
    (vm-vector-set! entry 2 (recover-temp1!))
    (vm-vector-set! entry 3 to-index)
    (vm-vector-set! entry 4 count)
    (vm-vector-set! entry 5 (proposal-copies proposal))
    (set-proposal-copies! proposal entry)))

;----------------
; Accessors and setters for stored objects.  There are three kinds, with an
; accessor and setter for each.  They are:
;   fixed-sized stored objects (pairs, closures, ...); no range check is done
;    for these
;   variable-sized stored-objects (vectors, records, ...)
;   records with types; these take an additional argument, the record type,
;    which is checked against the value in the record's first slot
;
; For efficency reasons there are two versions of stored-object-ref, one which
; logs the read in the current proposal and one which doesn't.  For all of the
; others the logging/not-logging is determined by a byte in the instruction
; stream.
;
; All of these are mostly argument checks.

(define-primitive stored-object-ref
  (any->)
  (lambda (stob)
    (let ((type (code-byte 0))
	  (offset (code-byte 1)))
      (if (stob-of-type? stob type)
	  (goto continue-with-value
		(d-vector-ref stob offset)
		2)
	  (raise-exception wrong-type-argument 2
			   stob
			   (enter-fixnum type)
			   (enter-fixnum offset))))))

(define-primitive stored-object-logging-ref
  (any->)
  (lambda (stob)
    (let ((type (code-byte 0))
	  (offset (code-byte 1)))
      (if (stob-of-type? stob type)
	  (goto continue-with-value
		(if (false? (current-proposal))
		    (d-vector-ref stob offset)
		    (proposal-d-read stob (enter-fixnum offset)))
		2)
	  (raise-exception wrong-type-argument 2
			   stob
			   (enter-fixnum type)
			   (enter-fixnum offset))))))

(define-primitive stored-object-set!
  (any-> any->)
  (lambda (stob value)
    (let ((type (code-byte 0))
	  (offset (code-byte 1)))
      (cond ((not (stob-of-type? stob type))
	     (raise-exception wrong-type-argument 3
			      stob
			      (enter-fixnum type)
			      (enter-fixnum offset)
			      value))
	    ((immutable? stob)
	     (raise-exception immutable-argument 3
			      stob
			      (enter-fixnum type)
			      (enter-fixnum offset)
			      value))
	    (else
	     (if (or (= 0 (code-byte 2))
		     (false? (current-proposal)))
		 (d-vector-set! stob offset value)
		 (proposal-d-write stob (enter-fixnum offset) value))
	     (goto continue-with-value
		   unspecific-value
		   3))))))

; Indexed objects

(define-primitive stored-object-indexed-ref (any-> any->)
  (lambda (stob index)
    (let ((type (code-byte 0)))
      (cond ((or (not (fixnum? index))
	         (not (stob-of-type? stob type)))
	     (raise-exception wrong-type-argument 2
			      stob (enter-fixnum type) index))
	    ((valid-index? (extract-fixnum index) (d-vector-length stob))
	     (goto continue-with-value
		   (if (or (= 0 (code-byte 1))
			   (false? (current-proposal)))
		       (d-vector-ref stob (extract-fixnum index))
		       (proposal-d-read stob index))
		   2))
	    (else
	     (raise-exception index-out-of-range 2
			      stob (enter-fixnum type) index))))))

(define-primitive stored-object-indexed-set! (any-> any-> any->)
  (lambda (stob index value)
    (let ((type (code-byte 0)))
      (cond ((or (not (fixnum? index))
		 (not (stob-of-type? stob type)))
	     (raise-exception wrong-type-argument 2
			      stob (enter-fixnum type) index value))
	    ((immutable? stob)
	     (raise-exception immutable-argument 2
			      stob (enter-fixnum type) index value))
	    ((valid-index? (extract-fixnum index)
			   (d-vector-length stob))
	     (if (or (= 0 (code-byte 1))
		     (false? (current-proposal)))
		 (d-vector-set! stob (extract-fixnum index) value)
		 (proposal-d-write stob index value))		 
	     (goto continue-with-value unspecific-value 2))
	    (else
	     (raise-exception index-out-of-range 2
			      stob (enter-fixnum type) index value))))))

; Byte vectors

(define-primitive byte-vector-logging-ref (code-vector-> fixnum->)
  (lambda (vector index)
    (if (valid-index? index (code-vector-length vector))
	(goto continue-with-value
	      (if (false? (current-proposal))
		  (enter-fixnum (code-vector-ref vector index))
		  (proposal-b-read vector (enter-fixnum index)))
	      0)
	(raise-exception index-out-of-range 0 vector (enter-fixnum index)))))

(define-primitive byte-vector-logging-set! (code-vector-> fixnum-> any->)
  (lambda (vector index byte)
    (cond ((immutable? vector)
	   (raise-exception immutable-argument 0
			    vector (enter-fixnum index) byte))
	  ((not (fixnum? byte))
	   (raise-exception wrong-type-argument 0
			    vector (enter-fixnum index) byte))
	  ((valid-index? index (code-vector-length vector))
	   (if (false? (current-proposal))
	       (code-vector-set! vector index (extract-fixnum byte))
	       (proposal-b-write vector (enter-fixnum index) byte))
	   (goto continue-with-value unspecific-value 0))
	  (else
	   (raise-exception index-out-of-range 0
			    vector (enter-fixnum index) byte)))))

; Hacko record handlers done for speed.

(define-primitive checked-record-ref
  (any-> any-> fixnum->)
  (lambda (record type index)
    (cond ((not (and (stob-of-type? record (enum stob record))
		     (record-has-type? record type)))
	   (raise-exception wrong-type-argument 1
			    record type (enter-fixnum index)))
	  ((valid-index? index (record-length record))
	   (goto continue-with-value
		 (if (or (= 0 (code-byte 0))
			 (false? (current-proposal)))
		     (record-ref record index)
		     (proposal-d-read record (enter-fixnum index)))
		 1))
	  (else
	   (raise-exception index-out-of-range 1
			    record type (enter-fixnum index))))))

(define-primitive checked-record-set! (any-> any-> fixnum-> any->)
  (lambda (record type index value)
    (cond ((not (and (stob-of-type? record (enum stob record))
		     (record-has-type? record type)))
	   (raise-exception wrong-type-argument 1
			    record type (enter-fixnum index) value))
	  ((immutable? record)
	   (raise-exception immutable-argument 1
			    record type (enter-fixnum index) value))
	  ((valid-index? index (record-length record))
	   (if (or (= 0 (code-byte 0))
		   (false? (current-proposal)))
	       (record-set! record index value)
	       (proposal-d-write record (enter-fixnum index) value))
	   (goto continue-with-value unspecific-value 1))
	  (else
	   (raise-exception index-out-of-range 1
			    record type (enter-fixnum index) value)))))

; Get the right log out of the current proposal and look for an entry for
; STOB at INDEX.  If it is there we return the value, otherwise we add a
; new entry with the current value.

(define (proposal-reader proposal-index accessor)
  (lambda (stob index)
    (let ((log (vm-vector-ref (current-proposal) proposal-index)))
      (let loop ((i 0))
	(let ((next-stob (log-entry-stob log i)))
	  (cond ((false? next-stob)
		 (add-log-entry+gc proposal-index
				   i  
				   stob
				   index
				   (accessor stob (extract-fixnum index))
				   #t))
		((and (eq? stob next-stob)
		      (= index (log-entry-index log i)))
		 (log-entry-value log i))
		(else
		 (loop (+ i log-entry-size)))))))))
  
(define proposal-d-read
  (proposal-reader proposal-d-log-index d-vector-ref))

(define proposal-b-read
  (proposal-reader proposal-b-log-index
		   (lambda (b-vector index)
		     (enter-fixnum (b-vector-ref b-vector index)))))

; Make the value at INDEX in STOB be VALUE in the current log.

(define (proposal-writer proposal-index)
  (lambda (stob index value)
    (let ((log (vm-vector-ref (current-proposal) proposal-index)))
      (let loop ((i 0))	     
	(let ((next-stob (log-entry-stob log i)))
	  (cond ((false? next-stob)
		 (add-log-entry+gc proposal-index i stob index value #f)
		 (unspecific))
		((and (eq? stob next-stob)
		      (= index (log-entry-index log i)))
		 (set-log-entry-value! log i value))
		(else
		 (loop (+ i log-entry-size)))))))))

(define proposal-d-write
  (proposal-writer proposal-d-log-index))

(define proposal-b-write
  (proposal-writer proposal-b-log-index))

;----------------
; Mostly error-checking as usual.

(define-primitive copy-bytes! (code-vector-> fixnum-> code-vector-> fixnum-> fixnum->)
  (lambda (from from-index to to-index count)
    (let ((lose (lambda ()
		  (raise-exception wrong-type-argument 1
				   from (enter-fixnum from-index)
				   to (enter-fixnum to-index)
				   (enter-fixnum count))))
	  (no-log? (= 0 (code-byte 0))))
      (cond ((not (and (okay-copy-code-vector? from from-index count)
		       (okay-copy-code-vector? to   to-index   count)
		       (<= 0 count)))
	     (lose))
	    ((immutable? to)
	     (raise-exception immutable-argument 1
			      from (enter-fixnum from-index)
			      to (enter-fixnum to-index)
			      (enter-fixnum count)))
	    ((or no-log?
		 (false? (current-proposal)))
	     (copy-memory! (address+ (address-after-header from) from-index)
			   (address+ (address-after-header to) to-index)
			   count)
	     (goto continue-with-value unspecific-value 1))
	    ((<= count (remaining-block-copying))
	     (add-block-copy!+gc from
				 (enter-fixnum from-index)
				 to
				 (enter-fixnum to-index)
				 (enter-fixnum count))
	     (goto continue-with-value unspecific-value 1))
	    (else
	     (lose))))))

(define maximum-proposal-block-copies 4096)

; Return the number of bytes we have remaining in our block-copy quota.

(define (remaining-block-copying)
  (let loop ((left maximum-proposal-block-copies)
	     (copies (proposal-copies (current-proposal))))
    (if (false? copies)
	left
	(loop (- left (copy-count copies))
	      (copy-next copies)))))

(define (okay-copy-code-vector? c index count)
  (and (<= 0 index)
       (<= (+ index count)
	   (code-vector-length c))))

;----------------
; Committing a proposal.
;
; If the two logs are up-to-date we do all writes and copies and then empty
; the logs.  We return #t if the proposal worked and #f if it didn't.

(define-primitive maybe-commit ()
  (lambda ()
    (let ((proposal (current-proposal)))
      (if (false? proposal)
	  (raise-exception no-current-proposal 0)
	  (begin
	    (get-proposal-lock!)
	    (cond ((and (d-log-in-sync? (proposal-d-log proposal))
			(b-log-in-sync? (proposal-b-log proposal))
			(copies-in-sync? (proposal-copies proposal)))
		   (commit-d-log! (proposal-d-log proposal))
		   (commit-b-log! (proposal-b-log proposal))
		   (do-copies! (proposal-copies proposal))
		   (vm-vector-set! proposal proposal-d-log-index *empty-log*)
		   (vm-vector-set! proposal proposal-b-log-index *empty-log*)
		   (vm-vector-set! proposal proposal-copy-list-index false)
		   (release-proposal-lock!)
		   (set-current-proposal! false)
		   (goto return true))
		  (else
		   (release-proposal-lock!)
		   (set-current-proposal! false)
		   (goto return false))))))))

; Go down LOG checking that each entry is consistent with the current state.
; Write entries require a check that the stob is still mutable.  Read entries
; require a check that the originally seen value is still there.  Read+write
; entries to both.

(define (log-synchronizer accessor)
  (lambda (log)
    (let loop ((i 0))
      (let ((stob (log-entry-stob log i)))
	(if (false? stob)
	    #t
	    (let ((value (log-entry-value log i))
		  (verify (log-entry-verify log i)))
	      (and (if (vm-eq? verify unreleased-value)
		       (not (immutable? stob))
		       (and (vm-eq? verify
				    (accessor stob
					      (extract-fixnum
					       (log-entry-index log i))))
			    (or (vm-eq? verify value)
				(not (immutable? stob)))))
		   (loop (+ i log-entry-size)))))))))

(define d-log-in-sync? (log-synchronizer d-vector-ref))

(define b-log-in-sync?
  (log-synchronizer (lambda (stob index)
		      (enter-fixnum (b-vector-ref stob index)))))

; The only thing to check is that the TO object is still mutable.

(define (copies-in-sync? copies)
  (let loop ((copies copies))
    (cond ((false? copies)
	   #t)
	  ((immutable? (copy-to copies))
	   #f)
	  (else
	   (loop (copy-next copies))))))

; Actually do all writes listed in LOG.

(define (log-committer setter)
  (lambda (log)
    (let loop ((i 0))
      (let ((stob (log-entry-stob log i)))
	(if (not (false? stob))
	    (begin
	      (let ((value (log-entry-value log i))
		    (verify (log-entry-verify log i)))
		(if (not (vm-eq? verify value))
		    (setter stob 
			    (extract-fixnum (log-entry-index log i))
			    value)))
	      (loop (+ i log-entry-size))))))))

(define commit-d-log! (log-committer d-vector-set!))
(define commit-b-log!
  (log-committer (lambda (stob index value)
		   (b-vector-set! stob index (extract-fixnum value)))))

(define (do-copies! copies)
  (let loop ((copies copies))
    (if (not (false? copies))
	(begin
	  (copy-memory! (address+ (address-after-header (copy-from copies))
				  (copy-from-index copies))
			(address+ (address-after-header (copy-to copies))
				  (copy-to-index copies))
			(copy-count copies))
	  (loop (copy-next copies))))))