関数を評価しようとする関数を作成しようとしていますが、特定のタイムアウト後に停止します。
を使用しようとしましたDeferred.any
。これは、基になる遅延オブジェクトの 1 つが満たされると、満たされる遅延オブジェクトを返します。
type 'a output = OK of 'a | Exn of exn
let fun_test msg f eq (inp,ans) =
let outp = wait_for (Deferred.any
[ return (try OK (f inp) with e -> Exn e)
; (after (Core.Std.sec 0.0) >>| (fun () -> Exn TIMEOUT))])
in {msg = msg;inp = inp;outp = outp;ans = ans;pass = eq outp ans}
遅延モナドから値を抽出する方法がわからなかったので、基になる値が決定されるまでスピンする関数「wait_for」を作成しました。
let rec wait_for x =
match Deferred.peek x with
| None -> wait_for x
| Some done -> done;;
これはうまくいきませんでした。Real World OCaml の Async の章を読んだ後、スケジューラを起動する必要があることに気付きました。Schedule.go
ただし、コードのどこで呼び出すかはわかりません。go : ?raise_unhandled_exn:bool -> unit -> Core.Std.never_returns
非同期コードを実際に返したいコードのどこに型が収まるかわかりません。のドキュメントには、「非同期プログラムは呼び出されるgo
まで終了しない」と書かれています。shutdown
このコーネルのウェブサイトで同じ問題に対する非常によく似た解決策を見つけるまで、私は問題に対して完全に間違ったアプローチをとったのではないかと疑い始めていました
let timeout (thunk:unit -> 'a Deferred.t) (n:float) : ('a option) Deferred.t
= Deferred.any
[ after (sec n) >>| (fun () -> None) ;
thunk () >>= (fun x -> Some x) ]
とにかく、私の使用法wait_for
が正しいかどうかはよくわかりません。遅延モナドから値を抽出する標準的な方法はありますか? また、スケジューラを起動するにはどうすればよいですか?
Core.Std.Thread
更新: と のみを使用してタイムアウト関数を作成しようとしましたCore.Std.Mutex
。
let rec wait_for lck ptr =
Core.Std.Thread.delay 0.25;
Core.Std.Mutex.lock lck;
(match !ptr with
| None -> Core.Std.Mutex.unlock lck; wait_for lck ptr
| Some x -> Core.Std.Mutex.unlock lck; x);;
let timeout t f =
let lck = Core.Std.Mutex.create () in
let ptr = ref None in
let _ = Core.Std.Thread.create
(fun () -> Core.Std.Thread.delay t;
Core.Std.Mutex.lock lck;
(match !ptr with
| None -> ptr := Some (Exn TIMEOUT)
| Some _ -> ());
Core.Std.Mutex.unlock lck;) () in
let _ = Core.Std.Thread.create
(fun () -> let x = f () in
Core.Std.Mutex.lock lck;
(match !ptr with
| None -> ptr := Some x
| Some _ -> ());
Core.Std.Mutex.unlock lck;) () in
wait_for lck ptr
これはかなり実用に近いと思います。のような計算let rec loop x = print_string ".\n"; loop x
では機能しますが、 のような計算では機能しませんlet rec loop x = loop x
。現在の問題は、計算f ()
が無限にループする場合、そのスレッドがプリエンプトされないため、他のスレッドがタイムアウトに気付かないことだと思います。スレッドが文字列を出力するような IO を行う場合、スレッドは横取りされます。また、スレッドを強制終了する方法もわかりません。Core.Std.Thread のドキュメントでそのような関数を見つけることができませんでした。