##もう少し先だけど忘れないように再掲。2月14日(日)札幌「hs.hs #1」 #hshs http://hshs.connpass.com/event/25948/ #「Haskellによる並列・並行プログラミング」で質問させてください。
12.3 チャットサーバ で、トランザクションの挙動に関する質問です。
クライアントとして Aさん と Bさん が接続しているとして、もし以下のような順で処理が行われた場合、
- Aさんがブロードキャストしようとしてトランザクションが開始する
- Bさんが Aさんを kick しようとしてトランザクションが開始する
- Bさんのトランザクションが完了する
- Aさんのブロードキャストのトランザクションが完了しようとする
このときAさんのトランザクションは失敗してリトライし、キックされてることを検知して接続が終了する。
トランザクションが失敗するのはなぜなら、完了しようとしたときに、 さっき readTVar clientKicked で読んでログに残した値と、現在のメモリの値が一致しないことを検出したから。
という認識なのですが、合っていますか?
#おおよそ合ってるとと思います。ただここではAさんがブロードキャストするには二つのトランザクションが関わってます
#"receive"スレッドによるsendMessageのトランザクションと、"server"スレッドによるトランザクションです
#"server"スレッドのclientSendChanのメッセージが空の場合、"server"のトランザクションは開きっぱなしで、readTChan clientSendChanの箇所で止まったままです。このスレッドが動きだすのは、このスレッドが触った二つのTVar, clientKickedもしくはclientSendChanに他のスレッドから書き込みが行われたときです。
#ところでchatonで複数行入力ってどうやるんですかね
#AさんがBroadcastの為に入力を行うと"receive"スレッドでclientSendChanへ書き込みが行われます(トランザクション1)
#その後"server"スレッドはclientSendChanへの書き込みを検知して動き出し、メッセージを取り出し、トランザクションをcommitしようとします(トランザクション2)
#このcommit時より前にBさんによってclientKickedが変更されているとcommitが失敗し、リトライされ、今度はトランザクション開始直後のclientKickedでキックされたことからさっきとは別の分岐に入る、という流れですね。
#ありがとうございます。把握できました。
#(receive スレッドのトランザクションの方は書き損ねてました。ありがとうございます)
#"server"スレッドがreadTChanでメッセージ待ちの時にBさんからclientKickedが書き込まれた時もAさんの"server"スレッドは動きだそうとしますが、結局メッセージが来てないので止まったままのはずです..(あれ、これメッセージ来ないとkickされないのか?)
#複数行の入力は、今気付いたのですが、
Nickname を入れずに Text を書けば改行しても送信されないので、
それで書ききった後に、Nickname を入力すれば、複数行の入力ができるようです。
#な、なんだって
本当だ..
#あれ? server スレッドが readTChan で待ってるときに clientKicked が Bさんに書き込まれれば、retry でトランザクション最初からやり直されて、キックされて切断されるんじゃないです?
#STMでblockされてるスレッドって動きだす時点で読んだTVarのチェックするんでしたっけ..
#と書いてあったかと。どこだったかというと...
#まず p.180 で "retry の意味は単に「現在のトランザクションを破棄してもう一度やり直す」。
#p181 に「違いが起こるのは現在のトランザクションで読み込まれた1つ以上の TVar が変更されたときだけ」
#なお p197 にて、readTVar した際に TVar からのスレッドの監視リストに追加されて、その TVar に変更があった場合にそのスレッドを起こす旨の記載がありました。
#スレッドを起こすこととretryすることは違いますよね
#commitする時には読んだTVarを全部走査する、というのは覚えていますが起こした時にTVar走査はしなかったような..
#あれ、スレッドを起こす = retry するじゃない? (というよりスレッドがブロックする == retry に入るでは)
#トランザクションAがTVar T1をreadして、他のトランザクション(複数)がそのT1を何回か書き換えたとしても、Aがcommitする時、結果的にcommitできる状態ならトランザクションAは成功する、ということを勉強会で話したのを覚えてます
#> Aがcommitする時、結果的にcommitできる状態ならトランザクションAは成功する
#というのは、read したときと、コミットしようとした時点での T1 の値が同じだったら、ということですか?ならそれは違和感ないですね。
#そうです。
#まあ確かに書き込まれた時点で捨ててしまった方がが合理的そうですね
#あ、takeTVarやputTVarとかの内部がretry使ってた..なるほど僕の勘違いですね
#そうそう、私も前に1回そこ勘違いして見直して「内部は結局 retry で、唯一の方法が retry なのね」となりました。
#ということでこの件は納得しました。ありがとうございます。
#なるほどretryとblockingが別のものかとなんとなく思ってました。勉強になりました。
#確かにそんな挙動してたら実装した時に気づいてるはずだなあ