可以重新排序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
是一个非常重要的锤子并且还有其他风险,但它确实会限制我软件更改的复杂性。
答案 0 :(得分:4)
这可能不适用,因为它基于您的问题中未指明的假设。特别是,这个答案基于以下两个假设。未明确的S
Data.ByteString.Lazy
和tmpDatFile
未定义,tmpOutputFile
。
import qualified Data.ByteString.Lazy as S
...
let tmpDatFile = tmpOutputFile
如果这些假设属实,即使不使用removeFile
,unsafePerformIO
也会过早运行。以下代码
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&#34; dataFile.txt&#34;:权限被拒绝(进程无法访问该文件,因为它正由另一个进程使用。)
删除行removeFile tmpDatFile
将使此代码正确执行,就像您描述的那样,但留下临时文件并不是您想要的。
将导入S
更改为
import qualified Data.ByteString as S
导致输出正确,
&#34;一些文字&#34;。
Data.ByteSting.Lazy
readFile
的文档说明
懒惰地将整个文件读入
ByteString
。手柄将保持打开状态,直到遇到EOF。
在内部,readfile
通过调用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
时,Handle
打开的readFile
仍处于打开状态,并且该文件无法删除。
答案 1 :(得分:2)
即使您使用unsafePerformIO
,也不应重新排序IO操作。如果你想确定这一点,你可以使用-ddump-simpl
标志来查看GHC产生的中间核心语言,甚至可以使用other -dump-*
flags中的一个显示汇编所有编译中间步骤。< / p>
我知道这可以回答你的问题,而不是你真正需要的,但你至少可以排除GHC错误。但是,似乎不太可能存在影响GHC的错误。
答案 2 :(得分:2)
完全是我的错......对不起大家。 GHC不会在上述条件下对IO块中的IO操作进行重新排序。汇编程序无法汇编输出并创建假定文件。我只是忘了检查汇编程序的退出代码或输出流。我假设输入在语法上是正确的,因为它是生成的,汇编程序拒绝它并且根本无法创建文件。它也提供了有效的错误代码和错误诊断,所以这对我来说非常糟糕。我可能第一次使用readProcess
,这会在非零退出时引发异常,但最终必须改变它。我认为汇编程序有一个错误,它没有正确指出某些情况下失败的退出代码,我不得不从readProcessWithExitCode
更改。
当我遗漏removeFile
时,我仍然不确定错误消失的原因。
我考虑过删除这个问题,但我希望上面的建议能帮助其他人调试类似(更有效)的问题。我已经被Cirdec提到的懒惰的IO事件所灼烧,并且chi提到的-ddump-simpl
旗帜也很有用。