I'm importing Leptonica C library into my Go app and using this for rotating and cropping an image.
For some reason it randomly produces a panic. I do mean random because I'm just testing at this point and so I'm processing exactly the same 20 images with exactly the same rotate/crop instructions repeatedly during my testing. Usually it works but around 1/50 times it will panic on a random one of the image, albeit usually one that has less on it (a mostly white image).
The panic given is:
fatal error: unexpected signal during runtime execution
[signal 0xb code=0x2 addr=0x7fed87890340 pc=0x7fed91e2de3e]
runtime stack:
runtime: unexpected return pc for runtime.sigpanic called from 0x7fed91e2de3e
runtime.throw(0x88bcc5)
/usr/local/go/src/pkg/runtime/panic.c:520 +0x69
runtime: unexpected return pc for runtime.sigpanic called from 0x7fed91e2de3e
runtime.sigpanic()
/usr/local/go/src/pkg/runtime/os_linux.c:222 +0x3d
goroutine 16 [syscall]:
runtime.cgocall(0x401550, 0xc213ca98e8)
/usr/local/go/src/pkg/runtime/cgocall.c:143 +0xe5 fp=0xc213ca98d0 sp=0xc213ca9888
private/leptonica._Cfunc_pixClipRectangle(0x7fed8788ff00, 0x7fed8788ffd0, 0x0, 0x8c2a28)
private/leptonica/_obj/_cgo_defun.c:80 +0x31 fp=0xc213ca98e8 sp=0xc213ca98d0
private/leptonica.(*goPix).Crop(0xc20af90000, 0x900, 0x90, 0x4d8, 0x9a8, 0x4202c1, 0x0, 0x0)
/home/go/src/private/leptonica/leptonica.go:154 +0xd8 fp=0xc213ca9960 sp=0xc213ca98e8
main.(*pageStruct).transformOriginal(0xc20805ae10, 0xc224772000, 0x69, 0x1)
/home/root/go/frankenstein.go:1353 +0x30d fp=0xc213ca99c8 sp=0xc213ca9960
main.(*pageStruct).transformOriginalWithRecover(0xc20805ae10, 0xc224772000, 0x69, 0x1, 0x0, 0x5, 0x0, 0x0)
/home/root/go/frankenstein.go:1403 +0xe1 fp=0xc213ca9a38 sp=0xc213ca99c8
main.(*book).processPages(0xc208a90000, 0x0, 0x0)
/home/root/go/frankenstein.go:1542 +0x305 fp=0xc213ca9e48 sp=0xc213ca9a38
main.main()
/home/root/go/frankenstein.go:1970 +0x3f5 fp=0xc213ca9f50 sp=0xc213ca9e48
runtime.main()
/usr/local/go/src/pkg/runtime/proc.c:247 +0x11a fp=0xc213ca9fa8 sp=0xc213ca9f50
runtime.goexit()
/usr/local/go/src/pkg/runtime/proc.c:1445 fp=0xc213ca9fb0 sp=0xc213ca9fa8
created by _rt0_go
/usr/local/go/src/pkg/runtime/asm_amd64.s:97 +0x120
goroutine 19 [finalizer wait]:
runtime.park(0x424c70, 0x8a9c08, 0x88e8a9)
/usr/local/go/src/pkg/runtime/proc.c:1369 +0x89
runtime.parkunlock(0x8a9c08, 0x88e8a9)
/usr/local/go/src/pkg/runtime/proc.c:1385 +0x3b
runfinq()
/usr/local/go/src/pkg/runtime/mgc0.c:2644 +0xcf
runtime.goexit()
/usr/local/go/src/pkg/runtime/proc.c:1445
goroutine 33 [syscall, 7 minutes]:
runtime.goexit()
/usr/local/go/src/pkg/runtime/proc.c:1445
I have tried to recover from this and redo the transformOriginal function with a function called transformOriginalWithRecover. Which implements a defer recover and then calls transformOriginal. Unfortunately that doesn't work.
My recovery function, which calls the function that then calls Leptonica via C is:
func (p *pageStruct) transformOriginalWithRecover(savefile string, detectOrientation bool, ontry, maxtries int) error {
var err error = nil
defer func() {
if r := recover(); r != nil {
ontry++
if ontry >= maxtries {
err = errors.New(`Failed to transform original.`)
} else {
if verbose {
fmt.Println(`Transform panicked. Recovered and retrying.`)
}
p.transformOriginalWithRecover(savefile, detectOrientation, ontry, maxtries)
}
}
}()
p.transformOriginal(savefile, detectOrientation)
return err
}
Which I call with p.transformOriginalWithRecover(filename, true, 0, 5)
to indicate it should try to recover 5 times before giving up.
Any idea how I can either solve this or somehow recover from a panic that was instigated from C?
UPDATE
Here is the specific function that is causing this problem. The panic happens inside of C.pixClipRectangle
randomly. It is more likely to happen the longer Leptonica takes to clip the image, which of course makes sense as that gives the GC more time to decide to activate.
func (p *goPix) Crop(x, y, w, h int) (*goPix, error) {
startX, startY, width, height := C.l_int32(x), C.l_int32(y), C.l_int32(w), C.l_int32(h)
box := C.boxCreateValid(startX, startY, width, height)
if box == nil {
box = C.boxCreateValid(startX, startY, width, height)
if box == nil {
return p, errors.New(`Unable to create box.`)
}
}
newpix := C.pixClipRectangle(p.cPix, box, nil)
if newpix != p.cPix {
p.Free()
}
C.boxDestroy(&box)
C.free(unsafe.Pointer(box))
if newpix == nil {
return nil, errors.New(`Crop failed`)
}
pix := &goPix{
cPix: newpix,
}
return pix, nil
}