2012-02-29 12 views
3

私はコマンドxを取る関数Haskellでどのようにフォークのtry-catchを実装できますか?

forkos_try :: IO (Maybe α) -> IO (Maybe α) 

を書きたいです。 xは、最初に状態を変更し、その状態が乱れているかどうかを確認する命令的な操作です。 (これは、状態を元に戻すためにOSレベルのサンドボックスのいくつかの種類を必要とする外部の何かを、行いません。)

  • xJust yforkos_try戻りJust yに評価された場合。それ以外の場合はforkos_tryロールバック状態となり、Nothingを返します。

内部的には、xとスレッドparentchildfork()は、child上で動作している必要があります。

  • xが成功した場合、childは(xの結果を返す)とparentは(Nothingを返す)を実行しておく必要がありそう
  • parentを死ぬべきで、child

質問を死ぬ必要があります実行し続ける必要がありますforkos_tryと同等以上の強力なセマンティクスで何かを書く方法はありますか? N.B. -xによる)の状態は、外部ライブラリにあり、スレッドの間を渡すことはできません。したがって、どのスレッドが生き続けるのかという意味は重要です。

正式には、「継続して実行する」とは、「continue continuity rest :: Maybe α -> IO()を実行する」という意味です。しかし、その継続はコード内のどこにでも明示されません。私の場合のために

は、私はそれが(時間のために)私は restの明示的な式を書くことができるので、(実行される計算全体 childを取る) forkOSを使用して、異なるスタイルでそれを書くために働くだろうと思います。しかし、それは私がどのようにプリミティブ関数 forkOSでこれを行うのか分からないという問題を抱えています。特定のケース( forkos_tryのような高水準APIとして表示される可能性があります)をサポートするのに十分一般的だと思います。

EDIT - 問題は、[http://pastebin.com/nJ1NNdda]まだはっきりしていない場合は、明示的なrestとのコード例を参照してください。

p.s.私はしばらくの間に並行コードを書いていません。 POSIX fork()の私の知識がうまくいけばうれしいです!前もって感謝します。

+0

ここには初心者向けの実装が明示的に続きます:http://pastebin.com/nJ1NNdda – gatoatigrado

+7

質問の理解に問題があります。状態はIOモナドにあるように見えますが、一般的には実行できないため(たとえば、ミサイルをいくつか起動したなど)、状態をロールバックする方法はありますか? –

+0

私は、OPがOSレベルのフォーク(つまり、 'forkIO'ではなく)を使用したいと思っています。これにより、 'IORef'のような内部状態をすべてロールバックすることができます。もちろん、ファイルに書き込まれたものをロールバックすることはできません。 – nominolo

答えて

2

状態を明示的にモデル化すると、事実は非常に簡単になります。"ロールバック"

不変の状態で
someStateFunc :: (s -> Maybe (a, s)) 

-- inside some other function 
case someStateFunc initialState of 
    Nothing -> ... -- it failed. stick with initial state 
    Just (a, newState) -> ... -- it suceeded. do something with 
          -- the result and new state 

は、簡単です:ちょうどinitialStateを使用してください。 「ロールバックしない」というのも簡単です。ちょうどnewStateを使用してください。

だから私はあなたの説明から、この「外部ライブラリ」はいくつかのわかりやすい可逆操作(ファイル、IORefの変更など)にもかかわらず、重要ではないIO効果を実行すると仮定しています。そこ(など、stdoutに書き込み、ミサイルを発射)いくつかのことを逆にする方法はありませんので、私はここにあなたのための二つの選択肢のいずれかを参照してください。

  1. クローン世界を、そしてサンドボックス内のアクションを実行します。成功した場合は、実世界でそのアクションを実行してください。
  2. 世界をクローンし、現実世界でアクションを実行します。失敗した場合は、Real Worldを先に取ったスナップショットに置き換えます。

もちろん、どちらも同じアプローチです。つまり、世界をフォークします。 1つの世界は行動を実行し、1つの世界は行動しない。行動が成功すれば、その世界は続く。そうでなければ、もう一方の世界は続く。 forkOSをビルドすることでこれを達成することを提案していますが、これはプログラムの状態全体を複製しますが、ファイルの変更などを処理するには十分ではありません。私が代わりに近い不変状態の単純さにあるアプローチを提案することを許可する:

tryIO :: IO s -> (s -> IO()) -> IO (Maybe a) -> IO (Maybe a) 
tryIO save restore action = do 
    initialState <- save 
    result <- action 
    case result of 
    Nothing -> restore initialState >> return Nothing 
    Just x -> return (Just x) 

ここからいくつかのデータ構造s、とにsaveへの道とrestoreを提供しなければならないデータ構造は語りました。これにより、必要なクローンを柔軟に実行できます。 (saveは特定のファイルを一時的な場所にコピーした後、restoreをコピーして一時ファイルを削除するか、saveが特定のIORefsの値をコピーしてからrestoreが値を返す可能性があります)。最も効率的ですが、それはとても簡単です。

+0

申し訳ありませんが、太字のテキストでは、スレッド間で 's'を渡すことはできません。特に、ハスケル型に変えることはできません。これは、Cライブラリに存在するいくつかの状態です。 – gatoatigrado

+1

注: 'forkOS'は' forkIO'のようにスレッドを開始しますが、新しいOSレベルのスレッドにバインドされます。プロセスをフォークするには、['System.Posix.forkProcess'](http://www.haskell.org/ghc/docs/latest/html/libraries/unix/System-Posix-Process.html#v:forkProcess )。 – hammar

+1

@gatoatigrado私が示唆している解決策は単糸であるため、問題ではありません。しかし、この状態をスナップショットする方法はありませんか?あなたがそれをロールバックできるようにしたいのであれば、それはかなり問題があると証明されます。プロセスフォークに頼る以外の手段を持たないことは、非常に強力なコードの匂いです。 –

関連する問題