2012-02-04 10 views
0

(1)キーを押すか、または(2)以前に入力した時刻のうちの前の時刻がhh:mm形式になるまでブロックするにはどうすればよいですか。私は問題がある場合にWindowsを使用しています。これはDOS assembler program(これもWindowsでも動作します)は、Windowsコンソールからbatchman waittil 16:30のようなものを介して欲しいものですが、私はそれをHaskellで完全にやりたいとします(つまり、はそのプログラムを使用しています。なし)。キー押下または指定時刻までブロックする

答えて

4

2つのスレッドを開始できます.1つは文字を読み取り、もう1つは指定された時間に達するまで待機します。それらは両方とも、完了を通知するために単一のMVarに書き込みます。

これはちょっと難しいですが、ほとんどの細部のためです:stdinをバッファリングされていないエコーモードにして、1つのキーを押すだけで何も印刷せずに待機させてから元の状態に戻したいとします。いずれかの終了後も両方のスレッドを終了させる必要があります。たとえば、タイムアウトが終了すると、stdinからの読み取りを停止します。さらに、例外が発生した場合、適切にクリーンアップされるようにする必要があります。 bracketは、ここでクリーンアップロジックを簡素化しますが、それはまだかなり醜いです:

import Prelude hiding (catch) 
import Control.Exception 
import Control.Concurrent 
import System.IO 

withRawStdin :: IO a -> IO a 
withRawStdin = bracket uncook restore . const 
    where 
    uncook = do 
     oldBuffering <- hGetBuffering stdin 
     oldEcho <- hGetEcho stdin 
     hSetBuffering stdin NoBuffering 
     hSetEcho stdin False 
     return (oldBuffering, oldEcho) 
    restore (oldBuffering, oldEcho) = do 
     hSetBuffering stdin oldBuffering 
     hSetEcho stdin oldEcho 

waitFor :: Int -> IO() 
waitFor delay = do 
    done <- newEmptyMVar 
    withRawStdin . bracket (start done) cleanUp $ \_ -> takeMVar done 
    where 
    start done = do 
     t1 <- forkIO $ getChar >> putMVar done() 
     t2 <- forkIO $ threadDelay delay >> putMVar done() 
     return (t1, t2) 
    cleanUp (t1, t2) = do 
     killThread t1 
     killThread t2 

でもすべてのことの後に、この解決策はまだ特定の時間まで待つ処理しません - ちょうどマイクロ秒の一定数を待っています。睡眠時間をマイクロ秒にするには、this previous SO questionが役に立ちます。スリープ時間が十分に長い場合は、マイクロ秒に収まらない場合がありますので、ループでthreadDelayを使用するか、unbounded-delaysパッケージのdelayを使用する必要があります。

+2

また、長時間スリープ状態にしたい場合は、32ビットシステムのオーバーフローに注意してください。 'threadDelay'はマイクロ秒を指定する' Int'を取るので、32ビットの 'Int'で指定できる最大時間は約2147秒(35分)です。ループ内で複数の 'threadDelay'呼び出しに分割する必要があるかもしれません。 – hammar

+0

@ハマー、ありがとう、良い情報です。私はそれを知らなかった。 –

+0

このループを行う 'Integer - > IO()'型の関数を含むHackage上のライブラリは間違いなくありますが、今見つけられません... –

関連する問題