proposal.scm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Proposals are four-element vectors. There doesn't seem to be much point
  3. ; in making a separate primitive data type for them. The four values are:
  4. ; in-use? - boolean value, true if the proposal is the current proposal
  5. ; for some thread
  6. ; d-log - a vector of log entries for descriptor reads and writes
  7. ; b-log - a vector of log entries for single byte reads and writes
  8. ; copy-list - a list of blocks of bytes to be copied if the proposal
  9. ; succeeds
  10. ;
  11. ; Each log is also a vector. Every log entry has four values:
  12. ; stob - the STored OBject which is being read and/or written
  13. ; index - the index within STOB
  14. ; verify - the value that the proposal assumes will be at INDEX in STOB;
  15. ; a special marker value is used if the location is written
  16. ; before it is read
  17. ; value - the value that should be put at INDEX in STOB when the
  18. ; proposal succeeds
  19. ; Logs may have unused slots after the final entry. The final entry in a log
  20. ; is followed by #f.
  21. ; Proposal offsets and accessors.
  22. (define proposal-size 4)
  23. (define proposal-in-use?-index 0)
  24. (define proposal-d-log-index 1)
  25. (define proposal-b-log-index 2)
  26. (define proposal-copy-list-index 3)
  27. (define (proposal-in-use? proposal)
  28. (vm-vector-ref proposal proposal-in-use?-index))
  29. (define (set-proposal-in-use?! proposal value)
  30. (vm-vector-set! proposal proposal-in-use?-index value))
  31. (define (proposal-d-log proposal)
  32. (vm-vector-ref proposal proposal-d-log-index))
  33. (define (proposal-b-log proposal)
  34. (vm-vector-ref proposal proposal-b-log-index))
  35. (define (proposal-copies proposal)
  36. (vm-vector-ref proposal proposal-copy-list-index))
  37. (define (set-proposal-copies! proposal value)
  38. (vm-vector-set! proposal proposal-copy-list-index value))
  39. ; Log offsets and accessors.
  40. (define log-entry-size 4)
  41. (define (log-entry-stob log i) (vm-vector-ref log i))
  42. (define (log-entry-index log i) (vm-vector-ref log (+ i 1)))
  43. (define (log-entry-value log i) (vm-vector-ref log (+ i 2)))
  44. (define (log-entry-verify log i) (vm-vector-ref log (+ i 3)))
  45. (define (set-log-entry-value! log i value)
  46. (vm-vector-set! log (+ i 2) value))
  47. ; Block copy accessors.
  48. (define (copy-from copy) (vm-vector-ref copy 0))
  49. (define (copy-to copy) (vm-vector-ref copy 2))
  50. (define (copy-next copy) (vm-vector-ref copy 5))
  51. (define (copy-from-index copy) (extract-fixnum (vm-vector-ref copy 1)))
  52. (define (copy-to-index copy) (extract-fixnum (vm-vector-ref copy 3)))
  53. (define (copy-count copy) (extract-fixnum (vm-vector-ref copy 4)))
  54. ;----------------
  55. ; A length-one vector containing #f. This is used when clearing a protocols
  56. ; logs after the protocol has committed.
  57. (define *empty-log*)
  58. ; Initialize the above registers and trace them when a GC occurs.
  59. (define (initialize-proposals!+gc)
  60. (set! *empty-log* (vm-make-vector+gc 1))
  61. (vm-vector-set! *empty-log* 0 false))
  62. (add-gc-root!
  63. (lambda ()
  64. (set! *empty-log* (s48-trace-value *empty-log*))))
  65. ; Just return the current proposal.
  66. (define-primitive current-proposal ()
  67. (lambda ()
  68. (current-proposal))
  69. return)
  70. ; Install a new proposol. We do a little error checking and set and clear
  71. ; the in-use? flags of the incoming and outgoing proposals.
  72. (define-primitive set-current-proposal! (any->)
  73. (lambda (proposal)
  74. (cond ((or (false? proposal)
  75. (and (vm-vector? proposal)
  76. (= proposal-size (vm-vector-length proposal))
  77. (false? (proposal-in-use? proposal))))
  78. (let ((old (current-proposal)))
  79. (if (not (false? old))
  80. (set-proposal-in-use?! old false)))
  81. (if (not (false? proposal))
  82. (set-proposal-in-use?! proposal true))
  83. (set-current-proposal! proposal)
  84. (goto return unspecific-value))
  85. ((vm-eq? proposal (current-proposal))
  86. (goto return unspecific-value))
  87. (else
  88. (raise-exception wrong-type-argument 0 proposal)))))
  89. ;----------------
  90. ; Add an entry to a current log, increasing the size of the log if necessary.
  91. ; VERIFY? is true if we need to verify that VALUE is in STOB at INDEX when
  92. ; trying to commit to the proposal. VERIFY? is false if a write occurs before
  93. ; any reads.
  94. (define (add-log-entry+gc proposal-index i stob index value verify?)
  95. (let* ((proposal (current-proposal))
  96. (log-size (vm-vector-length
  97. (vm-vector-ref proposal proposal-index))))
  98. (receive (proposal stob value)
  99. (if (= i (- log-size 1))
  100. (begin
  101. (save-temp0! stob)
  102. (save-temp1! value)
  103. (extend-log!+gc proposal-index log-size)
  104. (values (current-proposal)
  105. (recover-temp0!)
  106. (recover-temp1!)))
  107. (values proposal stob value))
  108. (let ((log (vm-vector-ref proposal proposal-index)))
  109. (vm-vector-set! log i stob)
  110. (vm-vector-set! log (+ i 1) index)
  111. (vm-vector-set! log (+ i 2) value)
  112. (vm-vector-set! log (+ i 3) (if verify? value unreleased-value))
  113. (vm-vector-set! log (+ i 4) false) ; end marker
  114. value))))
  115. ; The new log has twice the number of entries as the old one unless the old
  116. ; one has no entries, in which case we make room for four.
  117. ; We clear the currently unused portion of the log to avoid GC problems.
  118. (define (extend-log!+gc proposal-index log-size)
  119. (let* ((new-size (if (= log-size 1)
  120. (+ 1 (* log-entry-size 4))
  121. (- (* log-size 2) 1)))
  122. (new (vm-make-vector+gc new-size))
  123. (proposal (current-proposal)))
  124. (if (< 1 log-size)
  125. (copy-memory! (address-after-header (proposal-d-log proposal))
  126. (address-after-header new)
  127. (cells->a-units (- log-size 1))))
  128. (do ((i (+ log-size log-entry-size) (+ i 1)))
  129. ((= i new-size))
  130. (vm-vector-set! new i (enter-fixnum 0)))
  131. (vm-vector-set! proposal proposal-index new)))
  132. ; Add a byte-copy-block record to the current proposal.
  133. (define (add-block-copy!+gc from from-index to to-index count)
  134. (save-temp0! from)
  135. (save-temp1! to)
  136. (let* ((entry (vm-make-vector+gc 6))
  137. (proposal (current-proposal)))
  138. (vm-vector-set! entry 0 (recover-temp0!))
  139. (vm-vector-set! entry 1 from-index)
  140. (vm-vector-set! entry 2 (recover-temp1!))
  141. (vm-vector-set! entry 3 to-index)
  142. (vm-vector-set! entry 4 count)
  143. (vm-vector-set! entry 5 (proposal-copies proposal))
  144. (set-proposal-copies! proposal entry)))
  145. ;----------------
  146. ; Accessors and setters for stored objects. There are three kinds, with an
  147. ; accessor and setter for each. They are:
  148. ; fixed-sized stored objects (pairs, closures, ...); no range check is done
  149. ; for these
  150. ; variable-sized stored-objects (vectors, records, ...)
  151. ; records with types; these take an additional argument, the record type,
  152. ; which is checked against the value in the record's first slot
  153. ;
  154. ; For efficency reasons there are two versions of stored-object-ref, one which
  155. ; logs the read in the current proposal and one which doesn't. For all of the
  156. ; others the logging/not-logging is determined by a byte in the instruction
  157. ; stream.
  158. ;
  159. ; All of these are mostly argument checks.
  160. (define-primitive stored-object-ref
  161. (any->)
  162. (lambda (stob)
  163. (let ((type (code-byte 0))
  164. (offset (code-byte 1)))
  165. (if (stob-of-type? stob type)
  166. (goto continue-with-value
  167. (d-vector-ref stob offset)
  168. 2)
  169. (raise-exception wrong-type-argument 2
  170. stob
  171. (enter-fixnum type)
  172. (enter-fixnum offset))))))
  173. (define-primitive stored-object-logging-ref
  174. (any->)
  175. (lambda (stob)
  176. (let ((type (code-byte 0))
  177. (offset (code-byte 1)))
  178. (if (stob-of-type? stob type)
  179. (goto continue-with-value
  180. (if (false? (current-proposal))
  181. (d-vector-ref stob offset)
  182. (proposal-d-read stob (enter-fixnum offset)))
  183. 2)
  184. (raise-exception wrong-type-argument 2
  185. stob
  186. (enter-fixnum type)
  187. (enter-fixnum offset))))))
  188. (define-primitive stored-object-set!
  189. (any-> any->)
  190. (lambda (stob value)
  191. (let ((type (code-byte 0))
  192. (offset (code-byte 1)))
  193. (cond ((and (stob-of-type? stob type)
  194. (not (immutable? stob)))
  195. (if (or (= 0 (code-byte 2))
  196. (false? (current-proposal)))
  197. (d-vector-set! stob offset value)
  198. (proposal-d-write stob (enter-fixnum offset) value))
  199. (goto continue-with-value
  200. unspecific-value
  201. 3))
  202. (else
  203. (raise-exception wrong-type-argument 3
  204. stob
  205. (enter-fixnum type)
  206. (enter-fixnum offset)
  207. value))))))
  208. ; Indexed objects
  209. (define-primitive stored-object-indexed-ref (any-> any->)
  210. (lambda (stob index)
  211. (let ((type (code-byte 0)))
  212. (cond ((or (not (fixnum? index))
  213. (not (stob-of-type? stob type)))
  214. (raise-exception wrong-type-argument 2
  215. stob (enter-fixnum type) index))
  216. ((valid-index? (extract-fixnum index) (d-vector-length stob))
  217. (goto continue-with-value
  218. (if (or (= 0 (code-byte 1))
  219. (false? (current-proposal)))
  220. (d-vector-ref stob (extract-fixnum index))
  221. (proposal-d-read stob index))
  222. 2))
  223. (else
  224. (raise-exception index-out-of-range 2
  225. stob (enter-fixnum type) index))))))
  226. (define-primitive stored-object-indexed-set! (any-> any-> any->)
  227. (lambda (stob index value)
  228. (let ((type (code-byte 0)))
  229. (cond ((or (not (fixnum? index))
  230. (not (stob-of-type? stob type))
  231. (immutable? stob))
  232. (raise-exception wrong-type-argument 2
  233. stob (enter-fixnum type) index value))
  234. ((valid-index? (extract-fixnum index)
  235. (d-vector-length stob))
  236. (if (or (= 0 (code-byte 1))
  237. (false? (current-proposal)))
  238. (d-vector-set! stob (extract-fixnum index) value)
  239. (proposal-d-write stob index value))
  240. (goto continue-with-value unspecific-value 2))
  241. (else
  242. (raise-exception index-out-of-range 2
  243. stob (enter-fixnum type) index value))))))
  244. ; Byte vectors
  245. (define-primitive byte-vector-logging-ref (code-vector-> fixnum->)
  246. (lambda (vector index)
  247. (if (valid-index? index (code-vector-length vector))
  248. (goto continue-with-value
  249. (if (false? (current-proposal))
  250. (enter-fixnum (code-vector-ref vector index))
  251. (proposal-b-read vector (enter-fixnum index)))
  252. 0)
  253. (raise-exception index-out-of-range 0 vector (enter-fixnum index)))))
  254. (define-primitive byte-vector-logging-set! (code-vector-> fixnum-> any->)
  255. (lambda (vector index byte)
  256. (cond ((or (immutable? vector)
  257. (not (fixnum? byte)))
  258. (raise-exception wrong-type-argument 0
  259. vector (enter-fixnum index) byte))
  260. ((valid-index? index (code-vector-length vector))
  261. (if (false? (current-proposal))
  262. (code-vector-set! vector index (extract-fixnum byte))
  263. (proposal-b-write vector (enter-fixnum index) byte))
  264. (goto continue-with-value unspecific-value 0))
  265. (else
  266. (raise-exception index-out-of-range 0
  267. vector (enter-fixnum index) byte)))))
  268. ; Hacko record handlers done for speed.
  269. (define-primitive checked-record-ref
  270. (any-> any-> fixnum->)
  271. (lambda (record type index)
  272. (cond ((not (and (stob-of-type? record (enum stob record))
  273. (vm-eq? type (record-ref record 0))))
  274. (raise-exception wrong-type-argument 1
  275. record type (enter-fixnum index)))
  276. ((valid-index? index (record-length record))
  277. (goto continue-with-value
  278. (if (or (= 0 (code-byte 0))
  279. (false? (current-proposal)))
  280. (record-ref record index)
  281. (proposal-d-read record (enter-fixnum index)))
  282. 1))
  283. (else
  284. (raise-exception index-out-of-range 1
  285. record type (enter-fixnum index))))))
  286. (define-primitive checked-record-set! (any-> any-> fixnum-> any->)
  287. (lambda (record type index value)
  288. (cond ((not (and (stob-of-type? record (enum stob record))
  289. (vm-eq? type (record-ref record 0))
  290. (not (immutable? record))))
  291. (raise-exception wrong-type-argument 1
  292. record type (enter-fixnum index) value))
  293. ((valid-index? index (record-length record))
  294. (if (or (= 0 (code-byte 0))
  295. (false? (current-proposal)))
  296. (record-set! record index value)
  297. (proposal-d-write record (enter-fixnum index) value))
  298. (goto continue-with-value unspecific-value 1))
  299. (else
  300. (raise-exception index-out-of-range 1
  301. record type (enter-fixnum index) value)))))
  302. ; Get the right log out of the current proposal and look for an entry for
  303. ; STOB at INDEX. If it is there we return the value, otherwise we add a
  304. ; new entry with the current value.
  305. (define (proposal-reader proposal-index accessor)
  306. (lambda (stob index)
  307. (let ((log (vm-vector-ref (current-proposal) proposal-index)))
  308. (let loop ((i 0))
  309. (let ((next-stob (log-entry-stob log i)))
  310. (cond ((false? next-stob)
  311. (add-log-entry+gc proposal-index
  312. i
  313. stob
  314. index
  315. (accessor stob (extract-fixnum index))
  316. #t))
  317. ((and (eq? stob next-stob)
  318. (= index (log-entry-index log i)))
  319. (log-entry-value log i))
  320. (else
  321. (loop (+ i log-entry-size)))))))))
  322. (define proposal-d-read
  323. (proposal-reader proposal-d-log-index d-vector-ref))
  324. (define proposal-b-read
  325. (proposal-reader proposal-b-log-index
  326. (lambda (b-vector index)
  327. (enter-fixnum (b-vector-ref b-vector index)))))
  328. ; Make the value at INDEX in STOB be VALUE in the current log.
  329. (define (proposal-writer proposal-index)
  330. (lambda (stob index value)
  331. (let ((log (vm-vector-ref (current-proposal) proposal-index)))
  332. (let loop ((i 0))
  333. (let ((next-stob (log-entry-stob log i)))
  334. (cond ((false? next-stob)
  335. (add-log-entry+gc proposal-index i stob index value #f)
  336. (unspecific))
  337. ((and (eq? stob next-stob)
  338. (= index (log-entry-index log i)))
  339. (set-log-entry-value! log i value))
  340. (else
  341. (loop (+ i log-entry-size)))))))))
  342. (define proposal-d-write
  343. (proposal-writer proposal-d-log-index))
  344. (define proposal-b-write
  345. (proposal-writer proposal-b-log-index))
  346. ;----------------
  347. ; Mostly error-checking as usual.
  348. (define-primitive copy-bytes! (code-vector-> fixnum-> code-vector-> fixnum-> fixnum->)
  349. (lambda (from from-index to to-index count)
  350. (let ((lose (lambda ()
  351. (raise-exception wrong-type-argument 1
  352. from (enter-fixnum from-index)
  353. to (enter-fixnum to-index)
  354. (enter-fixnum count))))
  355. (no-log? (= 0 (code-byte 0))))
  356. (cond ((not (and (okay-copy-code-vector? from from-index count)
  357. (okay-copy-code-vector? to to-index count)
  358. (not (immutable? to))
  359. (<= 0 count)))
  360. (lose))
  361. ((or no-log?
  362. (false? (current-proposal)))
  363. (copy-memory! (address+ (address-after-header from) from-index)
  364. (address+ (address-after-header to) to-index)
  365. count)
  366. (goto continue-with-value unspecific-value 1))
  367. ((<= count (remaining-block-copying))
  368. (add-block-copy!+gc from
  369. (enter-fixnum from-index)
  370. to
  371. (enter-fixnum to-index)
  372. (enter-fixnum count))
  373. (goto continue-with-value unspecific-value 1))
  374. (else
  375. (lose))))))
  376. (define maximum-proposal-block-copies 4096)
  377. ; Return the number of bytes we have remaining in our block-copy quota.
  378. (define (remaining-block-copying)
  379. (let loop ((left maximum-proposal-block-copies)
  380. (copies (proposal-copies (current-proposal))))
  381. (if (false? copies)
  382. left
  383. (loop (- left (copy-count copies))
  384. (copy-next copies)))))
  385. (define (okay-copy-code-vector? c index count)
  386. (and (<= 0 index)
  387. (<= (+ index count)
  388. (code-vector-length c))))
  389. ;----------------
  390. ; Committing a proposal.
  391. ;
  392. ; If the two logs are up-to-date we do all writes and copies and then empty
  393. ; the logs. We return #t if the proposal worked and #f if it didn't.
  394. (define-primitive maybe-commit ()
  395. (lambda ()
  396. (let ((proposal (current-proposal)))
  397. (if (false? proposal)
  398. (raise-exception no-current-proposal 0)
  399. (begin
  400. (get-proposal-lock!)
  401. (cond ((and (d-log-in-sync? (proposal-d-log proposal))
  402. (b-log-in-sync? (proposal-b-log proposal))
  403. (copies-in-sync? (proposal-copies proposal)))
  404. (commit-d-log! (proposal-d-log proposal))
  405. (commit-b-log! (proposal-b-log proposal))
  406. (do-copies! (proposal-copies proposal))
  407. (vm-vector-set! proposal proposal-d-log-index *empty-log*)
  408. (vm-vector-set! proposal proposal-b-log-index *empty-log*)
  409. (vm-vector-set! proposal proposal-copy-list-index false)
  410. (release-proposal-lock!)
  411. (set-current-proposal! false)
  412. (goto return true))
  413. (else
  414. (release-proposal-lock!)
  415. (set-current-proposal! false)
  416. (goto return false))))))))
  417. ; Go down LOG checking that each entry is consistent with the current state.
  418. ; Write entries require a check that the stob is still mutable. Read entries
  419. ; require a check that the originally seen value is still there. Read+write
  420. ; entries to both.
  421. (define (log-synchronizer accessor)
  422. (lambda (log)
  423. (let loop ((i 0))
  424. (let ((stob (log-entry-stob log i)))
  425. (if (false? stob)
  426. #t
  427. (let ((value (log-entry-value log i))
  428. (verify (log-entry-verify log i)))
  429. (and (if (vm-eq? verify unreleased-value)
  430. (not (immutable? stob))
  431. (and (vm-eq? verify
  432. (accessor stob
  433. (extract-fixnum
  434. (log-entry-index log i))))
  435. (or (vm-eq? verify value)
  436. (not (immutable? stob)))))
  437. (loop (+ i log-entry-size)))))))))
  438. (define d-log-in-sync? (log-synchronizer d-vector-ref))
  439. (define b-log-in-sync?
  440. (log-synchronizer (lambda (stob index)
  441. (enter-fixnum (b-vector-ref stob index)))))
  442. ; The only thing to check is that the TO object is still mutable.
  443. (define (copies-in-sync? copies)
  444. (let loop ((copies copies))
  445. (cond ((false? copies)
  446. #t)
  447. ((immutable? (copy-to copies))
  448. #f)
  449. (else
  450. (loop (copy-next copies))))))
  451. ; Actually do all writes listed in LOG.
  452. (define (log-committer setter)
  453. (lambda (log)
  454. (let loop ((i 0))
  455. (let ((stob (log-entry-stob log i)))
  456. (if (not (false? stob))
  457. (begin
  458. (let ((value (log-entry-value log i))
  459. (verify (log-entry-verify log i)))
  460. (if (not (vm-eq? verify value))
  461. (setter stob
  462. (extract-fixnum (log-entry-index log i))
  463. value)))
  464. (loop (+ i log-entry-size))))))))
  465. (define commit-d-log! (log-committer d-vector-set!))
  466. (define commit-b-log!
  467. (log-committer (lambda (stob index value)
  468. (b-vector-set! stob index (extract-fixnum value)))))
  469. (define (do-copies! copies)
  470. (let loop ((copies copies))
  471. (if (not (false? copies))
  472. (begin
  473. (copy-memory! (address+ (address-after-header (copy-from copies))
  474. (copy-from-index copies))
  475. (address+ (address-after-header (copy-to copies))
  476. (copy-to-index copies))
  477. (copy-count copies))
  478. (loop (copy-next copies))))))