sicp-ex-3.48



<< Previous exercise (3.47) | Index | Next exercise (3.49) >>


gws

  
  
  
 (define (serialized-exchange account1 account2) 
   (let ((serializer1 'serializer-for-bigger-id--acc) 
         (serializer2 'serializer-for-smaller-id-acc)) 
     (cond ((> (get-id account1) (get-id account2)) 
            (set! serializer1 (account1 'serializer)) 
            (set! serializer2 (account2 'serializer))) 
           (else (set! serializer1 (account2 'serializer)) 
                 (set! serializer2 (account1 'serializer)))) 
     ((serializer1 (serializer2 exchange)) account1 account2))) 
  

leafac

This works because it makes impossible for one process to have acquired the lock for a resource A and be waiting for a lock for a resource B while other process has the lock for the resource B and is waiting for a lock for the resource A.

A complete solution that also implements the necessary modifications to make-account-and-serializer and avoids using state:

 (define (make-account-and-serializer id 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 'id) id) 
             ((eq? m 'withdraw) withdraw) 
             ((eq? m 'deposit) deposit) 
             ((eq? m 'balance) balance) 
             ((eq? m 'serializer) balance-serializer) 
             (else (error "Unknown request -- MAKE-ACCOUNT" 
                          m)))) 
     dispatch)) 
  
 (define (serialized-exchange account1 account2) 
   (let* ((serializer1 (account1 'serializer)) 
          (serializer2 (account2 'serializer)) 
          (exchanger (if (< (account1 'id) (account2 'id)) 
                         (serializer1 (serializer2 exchange)) 
                         (serializer2 (serializer1 exchange))))) 
     (exchanger account1 account2))) 
  

Sphinxsky

  
  
  
  
 (define (make-counter initial)     
     (define (count-and-read) 
         (set! initial (1+ initial)) 
         initial) 
     ((make-serializer) count-and-read)) 
 (define get-id (make-counter 0)) 
  
  
 (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)) 
           (id (get-id))) 
         (define (dispatch m) 
             (cond ((eq? m 'withdraw) withdraw) 
                 ((eq? m 'deposit) deposit) 
                 ((eq? m 'balance) balance) 
                 ((eq? m 'serializer) balance-serializer) 
                 ((eq? m 'id) id) 
                 (else (error "Unknown operation -- MAKE-ACCOUNT" m)))) 
         dispatch)) 
  
 (define (deposit account amount) 
     (let ((s (account 'serializer)) 
           (d (account 'deposit))) 
         ((s d) amount))) 
  
 (define (withdraw account amount) 
     (let ((s (account 'serializer)) 
           (w (account 'withdraw))) 
         ((s w) 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))) 
         ((if (< (account1 'id) (account2 'id)) 
             (serializer2 (serializer1 exchange)) 
             (serializer1 (serializer2 exchange))) 
             account1 
             account2)))