Skip to content
Browse files

concurrency

  • Loading branch information...
1 parent 965c160 commit 8f8c8c05e4235fa0d1724010575133f26d0639d4 @sarabander committed Mar 14, 2012
Showing with 185 additions and 0 deletions.
  1. +8 −0 3.4/3.39.scm
  2. +47 −0 3.4/3.40.scm
  3. +24 −0 3.4/3.41.scm
  4. +39 −0 3.4/3.42.scm
  5. +15 −0 3.4/3.43.scm
  6. +10 −0 3.4/3.44.scm
  7. +42 −0 3.4/3.45.scm
View
8 3.4/3.39.scm
@@ -0,0 +1,8 @@
+
+(define x 10)
+(define s (make-serializer))
+(parallel-execute (lambda () (set! x ((s (lambda () (* x x))))))
+ (s (lambda () (set! x (+ x 1)))))
+
+;; Eliminated: 110, 11.
+;; Remain: 101, 121, 100.
View
47 3.4/3.40.scm
@@ -0,0 +1,47 @@
+
+(define x 10)
+(parallel-execute (lambda () (set! x (* x x)))
+ (lambda () (set! x (* x x x))))
+
+;; 1.
+(set! x (* x x))
+(set! x (* x x x))
+;; or
+(set! x (* x x x))
+(set! x (* x x))
+;; Same result:
+; x = 1000000
+
+;; 2.
+;; First lambda accesses x twice, * evaluates to 100.
+;; Second lambda sets x to 1000.
+;; First lambda sets x to 100.
+;; x = 100
+
+;; 3.
+;; Second λ accesses x three times, * evaluates to 1000.
+;; First λ sets x to 100.
+;; Second lambda sets x to 1000.
+;; x = 1000
+
+;; 4.
+;; First λ accesses x inside * and gets 10,
+;; second lambda sets x to 1000, first λ accesses second x, gets 1000,
+;; and sets x to (* 10 1000). Same result if second λ accesses x inside
+;; * twice and gets 10, then first λ sets x to 100, second λ accesses x
+;; third time, and sets x to (* 10 10 100):
+;; x = 10000
+
+;; 5.
+;; Second λ accesses x inside * and gets 10,
+;; first λ sets x to 100, second λ accesses x second and third time,
+;; gets 100, and sets x to (* 10 100 100):
+;; x = 100000
+
+(define x 10)
+(define s (make-serializer))
+(parallel-execute (s (lambda () (set! x (* x x))))
+ (s (lambda () (set! x (* x x x)))))
+
+;; After this, only the first possibility remains:
+;; x = 1000000
View
24 3.4/3.41.scm
@@ -0,0 +1,24 @@
+
+(define (make-account balance)
+ (define (withdraw amount)
+ (if (>= balance amount)
+ (begin (set! balance (- balance amount))
+ balance)
+ "Insufficient funds"))
+ (define (deposit amount)
+ (set! balance (+ balance amount))
+ balance)
+ (let ((protected (make-serializer)))
+ (define (dispatch m)
+ (cond ((eq? m 'withdraw) (protected withdraw))
+ ((eq? m 'deposit) (protected deposit))
+ ((eq? m 'balance)
+ ((protected (lambda () balance))))
+ (else (error "Unknown request -- MAKE-ACCOUNT"
+ m))))
+ dispatch))
+
+;; I don't see a reason to serialize access to balance. There is only
+;; one program step to read the balance variable. It is hardly possible
+;; that set! manages to change some bits of balance during the time
+;; someone else accesses balance, unless the operations are not atomic.
View
39 3.4/3.42.scm
@@ -0,0 +1,39 @@
+
+(define (make-account balance)
+ (define (withdraw amount)
+ (if (>= balance amount)
+ (begin (set! balance (- balance amount))
+ balance)
+ "Insufficient funds"))
+ (define (deposit amount)
+ (set! balance (+ balance amount))
+ balance)
+ (let ((protected (make-serializer)))
+ (let ((protected-withdraw (protected withdraw))
+ (protected-deposit (protected deposit)))
+ (define (dispatch m)
+ (cond ((eq? m 'withdraw) protected-withdraw)
+ ((eq? m 'deposit) protected-deposit)
+ ((eq? m 'balance) balance)
+ (else (error "Unknown request -- MAKE-ACCOUNT"
+ m))))
+ dispatch)))
+
+;; I suspect that this is not entirely safe. Suppose we create a shared
+;; account:
+
+(define shared-account (make-account 100))
+
+;; Now, when two people happen to withdraw at the same exact moment:
+((shared-account 'withdraw) 50) ; 1st person
+((shared-account 'withdraw) 70) ; 2nd person
+
+;; they invoke the same protected-withdraw procedure concurrently. There is
+;; no protection against evaluating procedure's subexpressions twice in row.
+;; For example, (>= balance amount) could be evaluated by 1st person, then
+;; by 2nd person, before 1st person updates balance with set!. Both will
+;; proceed under the assumption that there are enough funds.
+;; Result is overdraft.
+
+;; One person withdrawing and other depositing concurrently will be safe.
+;; Now they invoke separate procedures that are executed serially.
View
15 3.4/3.43.scm
@@ -0,0 +1,15 @@
+
+;; Sequential processes just swap the account balances, so it is obvious
+;; that after any number of exchanges we end up with some permutation of
+;; the original list of balances.
+
+;; Non-sequential exchange still preserves the sum of balances, as long
+;; as both withdrawal and deposit succeed or fail together. This way
+;; the system is closed -- at any moment the total sum is distributed
+;; among accounts and temporary variables (like difference). The amount
+;; taken away from one place will be put back in another place.
+
+;; If withdrawal and deposit are not serialized, we are not guaranteed
+;; to possess the latest information about any account's balance. It could
+;; change between accessing the balance and setting new balance,
+;; as shown in figure 3.29. So we leak money in or out from the system.
View
10 3.4/3.44.scm
@@ -0,0 +1,10 @@
+
+(define (transfer from-account to-account amount)
+ ((from-account 'withdraw) amount)
+ ((to-account 'deposit) amount))
+
+;; There is no problem with transfer as long as the transaction is
+;; guaranteed to succeed or fail atomically. Transfer differs from
+;; exchange such that we don't need to swap the account balances,
+;; and we don't expect the sum of the balances to remain the same
+;; after the transaction.
View
42 3.4/3.45.scm
@@ -0,0 +1,42 @@
+
+(define (make-account-and-serializer balance)
+ (define (withdraw amount)
+ (if (>= balance amount)
+ (begin (set! balance (- balance amount))
+ balance)
+ "Insufficient funds"))
+ (define (deposit amount)
+ (set! balance (+ balance amount))
+ balance)
+ (let ((balance-serializer (make-serializer)))
+ (define (dispatch m)
+ (cond ((eq? m 'withdraw) (balance-serializer withdraw))
+ ((eq? m 'deposit) (balance-serializer deposit))
+ ((eq? m 'balance) balance)
+ ((eq? m 'serializer) balance-serializer)
+ (else (error "Unknown request -- MAKE-ACCOUNT"
+ m))))
+ dispatch))
+
+(define (deposit account amount)
+ ((account 'deposit) amount))
+
+(define (exchange account1 account2)
+ (let ((difference (- (account1 'balance)
+ (account2 'balance))))
+ ((account1 'withdraw) difference)
+ ((account2 'deposit) difference)))
+
+(define (serialized-exchange account1 account2)
+ (let ((serializer1 (account1 'serializer))
+ (serializer2 (account2 'serializer)))
+ ((serializer1 (serializer2 exchange))
+ account1
+ account2)))
+
+;; Serialized-exchange uses both accounts' serializers to call exchange.
+;; But exchange tries to use the same (already initialized) serializers
+;; to withdraw and deposit. It can't do that, because the previous
+;; invocation of each serializer must finish before the next can start.
+;; But the previous can't finish without completing the evaluation
+;; of exchange. So we have a deadlock.

0 comments on commit 8f8c8c0

Please sign in to comment.
Something went wrong with that request. Please try again.