exercise 3.47~49

ようやく3.4終了。
ページ自体は短かったが、意外に時間がかかったなぁ。

少し長いので続きを読むから。

ex3.48
;;mutexなやつ。
(define (make-semaphore n)                                     
  (let ((mutex (make-mutex)))                                  
    (define (acquire)                                          
      (mutex 'acquire)                                         
      (if (> n 0)                                              
          (set! n (- n 1))                                     
          (begin (mutex 'release)                              
                 (acquire)))                                   
      (mutex 'release))                                        
    (define (release)                                          
      (mutex 'acquire)                                         
      (set! x (+ x 1))                                         
      (mutex 'release))                                        
    (define (the-semaphore m)                                  
      (cond ((eq? m 'acquire) (acquire))                       
            ((eq? m 'release) (release))                       
            (else (error "Unknown Operation -- SEMAPHORE" m)))\
)                                                              
    the-semaphore)) 

もうひとつはパス。
というか、よくわからんので、解答例見た・・・.orz
よーするにtest-and-set!とcellを使って、nをいじったりする部分に入れるのは1プロセスだけになるようにしときゃいいのか。ほむほむ。
もっと精進せねばなぁ・・・

ex3.48

accountにnumberを割り振っておき、小さい順にアクセスしていけば、PeterもPaulも同じ順番であa1,a2のaccountにアクセスするのでDeadLockは避けれる。
あー、説明しにくい。
コードは適当に書いた↓

(define (make-account-and-serializer-and-number balance number)
  (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) withdraw)
            ((eq? m 'deposit) deposit)
            ((eq? m 'balance) balance)
            ((eq? m 'serializer) balance-serializer)
            ((eq? m 'number) number)
            (else (error "Unknown request -- MAKE-ACCOUNT"
                         m))))
    dispatch))

(define (serialized-exchange account1 account2)
  (let ((number1 (account1 'number))
        (number2 (account2 'number))
        (serializer1 (account1 'serializer))
        (serializer2 (account2 'serializer)))
    ((if (< number1 number2)
         (serializer1 (serializer2 exchange))
         (serializer2 (serializer1 exchange)))
     account1
     account2)))

make-account-and-serializer-and-numbr
・・・なんて適当な名前!!!
まぁいいや、きっとこれでいいはず。

ex3.49

うまい例が思い浮かばない・・・。
以下は適当な妄想。
というか推敲せずに書きなぐった感じなので読みにくい・・・。


1. 何かのaccountからserializerとってきてserialize
2. 適当なshared resourcesにアクセス
3. 2から必要とするaccountを調べて、そこからserializerをとってきて、serialize


このとき、1のaccountより3のaccountの方が番号が若ければDead Lockに持ち込めると思うんだけど、そもそも1と2の順番を入れ替えて、serializeのタイミングをずらすだけで、問題が解決しちゃう場合がある。(ようするに3でaccount得た後に、1と3を比べて、番号が若い順にserializeを行うみたいな)
つまり、1→2の順番であるには、2での操作以降で1のaccountを変更されたくないのでserializeする・・・といったシナリオでないといけないはず。


ということで、考えたのは
a. 1のaccountに別のアカウントの情報があって、そのアカウントに入金しないといけない。(その情報を変更させないためにも1でserializeしないといけない)
b. 1のaccountのbalanceの値と他のaccountのbalanceの値を比べて、その結果から1のaccountとのexchangeの対象を探す。
(途中で1のbalanceが変わるとまずいのでserializeがいる)


とか考えたけど
aだと、そもそもmake-account自体に変更がいるし、まずなぜそんなことをしないといけないかが思い浮かばない・・・。
bだと、「というか比較対象のaccountを全部serializeすればよくね?」


というかんじでハマった…。さぱーりすぐる。