簡體   English   中英

ghci可以在unsafePerformIO IO塊中重新發送IO操作

[英]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是一個非常大的錘子,並且還有其他風險,但它確實會限制我軟件更改的復雜性。

這可能不適用,因為它基於您的問題中未指定的假設。 特別是,這個答案基於以下兩個假設。 未指定的SData.ByteString.LazytmpDatFile ,它是未定義的,是tmpOutputFile

import qualified Data.ByteString.Lazy as S
...
    let tmpDatFile = tmpOutputFile

可能的原因

如果這些假設成立,即使不使用unsafePerformIOremoveFile也會過早運行。 以下代碼

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.LazyreadFile文檔聲明它會

將整個文件懶惰地讀入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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM