Skip to content

Commit 65b91c5

Browse files
committed
Add implementation of exercise 3-48
1 parent 5c839e9 commit 65b91c5

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

README.org

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
习题完成情况:
2626
- 章节一: 43/46
2727
- 章节二: 88/97
28-
- 章节三: 47/82
28+
- 章节三: 48/82
2929
- 章节四: TODO
3030
- 章节五: TODO
3131
* 运行

chapter3/exercise3-48.rkt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#lang racket
2+
(require "mutex.rkt")
3+
4+
;;; 设想 Peter 企图去交换 a1 和 a2,同时 Paul 并发地企图交换 a2 和 a1
5+
;;; 如果对账户编号,并使进程先试图获取编号较小的账户,那么意味着账户的
6+
;;; 顺序是确定的;即 Peter 和 Paul 都会先获取 a1 (假设 a1 编号更小),
7+
;;; 那么只有一个进程会获取成功,那么它就可以继续获取 a2, 从而避免死锁
8+
9+
(define (make-account-and-serializer id balance)
10+
(define (withdraw amount)
11+
(if (>= balance amount)
12+
(begin (set! balance (- balance amount))
13+
balance)
14+
"Insufficient funds"))
15+
(define (deposit amount)
16+
(set! balance (+ balance amount))
17+
balance)
18+
19+
(let ((balance-serializer (make-serializer)))
20+
(define (dispatch m)
21+
(cond ((eq? m 'withdraw) withdraw)
22+
((eq? m 'deposit) deposit)
23+
((eq? m 'balance) balance)
24+
((eq? m 'serializer) balance-serializer)
25+
((eq? m 'id) id)
26+
(else (error "Unknown request -- MAKE-ACCOUNT" m))))
27+
dispatch))
28+
29+
(define (exchange account1 account2)
30+
(let ((difference (- (account1 'balance)
31+
(account2 'balance))))
32+
((account1 'withdraw) difference)
33+
((account2 'deposit) difference)))
34+
35+
(define (serializer-exchange account1 account2)
36+
(let* ((id1 (account1 'id))
37+
(id2 (account2 'id))
38+
(small-account (if (< id1 id2) account1 account2))
39+
(large-account (if (< id1 id2) account2 account1))
40+
(s-serializer (small-account 'serializer))
41+
(l-serializer (large-account 'serializer)))
42+
;; always lock smaller Id first, then large Id
43+
((s-serializer (l-serializer exchange)) account1 account2)))
44+
45+
(module+ test
46+
(require rackunit)
47+
48+
(test-case "Test for no-dead lock version exchange"
49+
(define account1 (make-account-and-serializer 1 40))
50+
(define account2 (make-account-and-serializer 2 20))
51+
(check-equal? (account1 'id) 1)
52+
(check-equal? (account2 'id) 2)
53+
54+
(define t1 (thread (lambda ()(serializer-exchange account1 account2))))
55+
(define t2 (thread (lambda ()(serializer-exchange account2 account1))))
56+
57+
;; should avoid deadlock
58+
(thread-wait t1)
59+
(thread-wait t2)
60+
61+
;; exchange twice means each account has the original balance
62+
(check-equal? (account1 'balance) 40)
63+
(check-equal? (account2 'balance) 20)
64+
)
65+
)

chapter3/mutex.rkt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,14 @@
1717
(define (clear! cell)
1818
(box-cas! cell #t #f))
1919

20-
(provide make-mutex)
20+
(define (make-serializer)
21+
(let ((mutex (make-mutex)))
22+
(lambda (p)
23+
(define (serializer-p . args)
24+
(mutex 'acquire)
25+
(let ((val (apply p args)))
26+
(mutex 'release)
27+
val))
28+
serializer-p)))
29+
30+
(provide make-mutex make-serializer)

0 commit comments

Comments
 (0)