![](/img/trans.png)
[英]How do I force evaluation of an IO action within `unsafePerformIO`?
[英]Can ghci reoder IO actions within unsafePerformIO IO blocks
可以重新排序在unsafePerformIO
中的IO塊調用中的IO操作嗎?
我有效的IO功能。
assembleInsts :: ... -> IO S.ByteString
assembleInsts ... = do
tmpInputFile <- generateUniqueTmpFile
writeFile tmpInputFile str
(ec,out,err) <- readProcessWithExitCode asm_exe [tmpInputFile] ""
-- asm generates binary output in tmpOutputFile
removeFile tmpInputFile
let tmpOutputFile = replaceExtension tmpIsaFile "bits" -- assembler creates this
bs <- S.readFile tmpOutputFile -- fails due to tmpOutputFile not existing
removeFile tmpOutputFile
return bs
其中S.ByteString
是嚴格的字節字符串。
遺憾的是,我需要在遠離IO monad的純代碼樹中調用它,但由於我的匯編程序表現為一個引用透明(給定的唯一文件)工具,我想我暫時可以為它做一個不安全的接口暫且。
{-# NOINLINE assembleInstsUnsafe #-}
assembleInstsUnsafe :: ... -> S.ByteString
assembleInstsUnsafe args = unsafePerformIO (assembleInsts args)
另外,我根據文檔( System.IO.Unsafe
)的說明在模塊的頂部添加了以下注釋。
{-# OPTIONS -fno-cse #-}
module Gen.IsaAsm where
(我也嘗試添加-fnofull-laziness
,根據我所咨詢的引用,但這被編譯器拒絕了。我不認為這種情況適用於此。)
在ghci
運行它會報告以下錯誤。
*** Exception: C:\Users\trbauer\AppData\Local\Temp\tempfile_13516_0.dat: openBinaryFile: does not exist (No such file or directory)
但是,如果我刪除removeFile tmpOutputFile
,那么它神奇地起作用。 因此,似乎removeFile
在進程終止之前執行。 這可能嗎? bytestring是嚴格的,我甚至試圖用一個強制輸出:
S.length bs `seq` return ()
在removeFile
之前。
有沒有辦法轉儲中間代碼,以找出發生了什么? (也許我可以使用Process Monitor或其他東西來查找。)不幸的是,我想在此操作中清理(刪除文件)。
我認為exe版本可能有用,但在ghci下它失敗(解釋)。 我使用的是上一個Haskell平台的GHC 7.6.3。
我知道unsafePerformIO
是一個非常大的錘子,並且還有其他風險,但它確實會限制我軟件更改的復雜性。
這可能不適用,因為它基於您的問題中未指定的假設。 特別是,這個答案基於以下兩個假設。 未指定的S
是Data.ByteString.Lazy
和tmpDatFile
,它是未定義的,是tmpOutputFile
。
import qualified Data.ByteString.Lazy as S
...
let tmpDatFile = tmpOutputFile
如果這些假設成立,即使不使用unsafePerformIO
, removeFile
也會過早運行。 以下代碼
import System.Directory
import qualified Data.ByteString.Lazy as S
assembleInsts = do
-- prepare a file, like asm might have generated
let tmpOutputFile = "dataFile.txt"
writeFile tmpOutputFile "a bit of text"
-- read the prepared file
let tmpDatFile = tmpOutputFile
bs <- S.readFile tmpOutputFile
removeFile tmpDatFile
return bs
main = do
bs <- assembleInsts
print bs
導致錯誤
lazyIOfail.hs:DeleteFile“dataFile.txt”:權限被拒絕(進程無法訪問該文件,因為它正由另一個進程使用。)
刪除行removeFile tmpDatFile
將使這段代碼正確執行,就像你描述的那樣,但是留下臨時文件並不是你想要的。
將導入S
更改為
import qualified Data.ByteString as S
而是導致正確的輸出,
“一點文字”。
Data.ByteSting.Lazy
的readFile
文檔聲明它會
將整個文件懶惰地讀入
ByteString
。 手柄將保持打開狀態,直到遇到EOF。
在內部, readfile
通過調用unsafeInterleaveIO
來實現這unsafeInterleaveIO
。 unsafeInterleaveIO
推遲執行IO代碼,直到它返回的術語被評估。
hGetContentsN :: Int -> Handle -> IO ByteString
hGetContentsN k h = lazyRead -- TODO close on exceptions
where
lazyRead = unsafeInterleaveIO loop
loop = do
c <- S.hGetSome h k -- only blocks if there is no data available
if S.null c
then do hClose h >> return Empty
else do cs <- lazyRead
return (Chunk c cs)
因為沒有任何東西試圖查看上面例子中定義的bs
的構造函數,直到它被print
為止,直到執行了removeFile
之后才會發生這種情況,之前沒有從文件中讀取任何塊(並且文件沒有關閉) removeFile
被執行。 因此,當執行removeFile
時, readFile
打開的Handle
仍然打開,並且無法刪除該文件。
即使您使用的是unsafePerformIO
,也不應重新排序IO操作。 如果你想確定這一點,你可以使用-ddump-simpl
標志來查看GHC產生的中間核心語言,或者甚至是其他-dump-*
標志之一,顯示匯編之前的所有編譯中間步驟。
我知道這可以回答你的問題,而不是你真正需要的,但你至少可以排除GHC錯誤。 但是,在GHC中似乎不太可能存在影響這種情況的錯誤。
完全是我的錯......對不起大家。 GHC不會在上述條件所述的上述條件下對IO塊中的IO操作重新排序。 匯編程序只是無法匯編輸出並創建假定的文件。 我只是忘了檢查匯編程序的退出代碼或輸出流。 我假設輸入在語法上是正確的,因為它是生成的,匯編程序拒絕它並且根本無法創建文件。 它也提供了有效的錯誤代碼和錯誤診斷,所以這對我來說非常糟糕。 我可能第一次使用readProcess
,這會在非零退出時引發異常,但最終必須改變它。 我認為匯編程序有一個錯誤,它沒有正確指出某些情況下失敗的退出代碼,我不得不從readProcessWithExitCode
更改。
當我省略removeFile
時,我仍然不確定為什么錯誤會消失。
我想刪除這個問題,但我希望上面的建議能幫助其他人調試類似(更有效)的問題。 我被Cirdec提到的惰性IO事件所灼燒,並且chi提到的-ddump-simpl
標志也很好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.