How to ignore "broken pipe" exception in nim

Viewed 247

I'm writing a CLI program, and when I execute the binary like program | head I get:

Error: unhandled exception: errno: 32 `Broken pipe` [IOError]

I'd like to silence this exception and tried to add:

import posix
signal(SIG_PIPE, SIG_IGN)

in my main procedure, but this didn't silence the error. The program uses threadspool, calling another procedure to analyze a pool of input lines. (I don't know if this is relevant to the question)

UPDATE:

  • This is my non-working program: https://pastebin.com/aQaRgfXR (threads spawned with responses.add(spawn parseArray(readspool, mergeOptions)))
  • This is the full error:
/Users/telatina/miniconda3/nim/lib/pure/concurrency/threadpool.nim(377) slave
/Users/telatina/git/nim-stuff/orf/src/porfidus.nim(307) parseArrayWrapper
/Users/telatina/git/nim-stuff/orf/src/porfidus.nim(247) parseArray
/Users/telatina/miniconda3/nim/lib/system/io.nim(155) checkErr
/Users/telatina/miniconda3/nim/lib/system/io.nim(138) raiseEIO
Error: unhandled exception: errno: 0 `Undefined error: 0` [IOError]
2 Answers

building on @julian-fondren 's answer and your code i was able to replicate your error.

import os, posix, threadpool
signal(SIG_PIPE, SIG_IGN)

proc noise =
  for i in 0 ..< int(1e6):
    stdout.write($i & '\n')

threadpool.spawn noise()
threadpool.spawn noise()
threadpool.sync()

for those following along.

you're using stdout.write in your code as well, which may throw an IO Exception
since you use SIG_IGN the thread does not stop running when it receives the SIG_PIPE but continues to call stdout.write on a broken pipe, which throws the exception.

To fix this I've found three options

  1. Handle the Exception

    This is the most general solution to any situation where your code panics with "Error: unhandled exception"

    to handle an exception in Nim use try and except.

stdout.write($i & '\n') becomes

try:
  stdout.write($i & '\n')
except IOError:
  #handle it by ending the program
  quit()
  #or ignore it with
  #discard
  1. Handle the signal On my machine using SIG_DFL results in the thread terminating silently, thus behaving as you want but this is platform dependent. So you can also register your own signal handler (sorry about the ugly cast)
#signal(SIG_PIPE,SIG_DFL)
signal(SIG_PIPE,cast[typeof(SIG_IGN)](proc(signal:cint) =
    stderr.write("handled sigpipe\n")
    quit()
  ))
  1. Just use echo echo, unlike stdout.write does not throw exceptions, so for this situation it's probably your easiest fix

With nim 1.2.6 and the following program (don't take it for good style):

import os, posix, threadpool
signal(SIG_PIPE, SIG_IGN)

proc noise =
  for i in 0 ..< int(1e6):
    echo i

threadpool.spawn noise()
threadpool.spawn noise()
threadpool.sync()

When run as follows it clearly ignores the EPIPE and is clearly printing from multiple threads (as both 60s are seen and then there's a delay as both threads reach the end of the loop):

$ ./brokenpipe |grep -m2 -F 60
60
60

With the signal call commented out, the last command throws an error:

$ ./brokenpipe |grep -m2 -F 60
60
60
Traceback (most recent call last)
/home/jfondren/.choosenim/toolchains/nim-1.2.6/lib/pure/concurrency/threadpool.nim(377) slave
/home/jfondren/nim/learn/brokenpipe.nim(8) noiseWrapper
/home/jfondren/nim/learn/brokenpipe.nim(6) noise
/home/jfondren/.choosenim/toolchains/nim-1.2.6/lib/system/io.nim(648) echoBinSafe
SIGPIPE: Pipe closed.

So your solution appears to work, and you're really going to need a minimal example of the problem that isn't fixed by it.

Related