Make various goroutines panic-safe

This commit is contained in:
Kovid Goyal
2025-10-09 07:17:53 +05:30
parent 49d8b1a9d0
commit f067e9cd92
11 changed files with 69 additions and 22 deletions

View File

@@ -321,7 +321,7 @@ func (self *Context) run_paste(src Scanner, background image.Image, pos image.Po
default:
panic(fmt.Sprintf("Unsupported image type: %v", v))
}
self.Parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
if err := self.SafeParallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
for y := range ys {
x1 := interRect.Min.X - pasteRect.Min.X
x2 := interRect.Max.X - pasteRect.Min.X
@@ -333,7 +333,9 @@ func (self *Context) run_paste(src Scanner, background image.Image, pos image.Po
src.scan(x1, y1, x2, y2, dst)
postprocess(dst)
}
})
}); err != nil {
panic(err)
}
}

View File

@@ -27,18 +27,20 @@ func reverse_row(bytes_per_pixel int, pix []uint8) {
func (self *Context) FlipPixelsH(bytes_per_pixel, width, height int, pix []uint8) {
stride := bytes_per_pixel * width
self.Parallel(0, height, func(ys <-chan int) {
if err := self.SafeParallel(0, height, func(ys <-chan int) {
for y := range ys {
i := y * stride
reverse_row(bytes_per_pixel, pix[i:i+stride])
}
})
}); err != nil {
panic(err)
}
}
func (self *Context) FlipPixelsV(bytes_per_pixel, width, height int, pix []uint8) {
stride := bytes_per_pixel * width
num := height / 2
self.Parallel(0, num, func(ys <-chan int) {
if err := self.SafeParallel(0, num, func(ys <-chan int) {
for y := range ys {
upper := y
lower := height - 1 - y
@@ -50,6 +52,7 @@ func (self *Context) FlipPixelsV(bytes_per_pixel, width, height int, pix []uint8
as[i], bs[i] = bs[i], as[i]
}
}
})
}); err != nil {
panic(err)
}
}

View File

@@ -7,6 +7,8 @@ import (
"runtime"
"sync"
"sync/atomic"
"github.com/kovidgoyal/kitty/tools/utils"
)
var _ = fmt.Print
@@ -31,8 +33,10 @@ func (self *Context) EffectiveNumberOfThreads() int {
return ans
}
// parallel processes the data in separate goroutines.
func (self *Context) Parallel(start, stop int, fn func(<-chan int)) {
// parallel processes the data in separate goroutines. If any of them panics,
// returns an error. Note that if multiple goroutines panic, only one error is
// returned.
func (self *Context) SafeParallel(start, stop int, fn func(<-chan int)) (err error) {
count := stop - start
if count < 1 {
return
@@ -49,9 +53,16 @@ func (self *Context) Parallel(start, stop int, fn func(<-chan int)) {
for range procs {
wg.Add(1)
go func() {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
text, _ := utils.Format_stacktrace_on_panic(r)
err = fmt.Errorf("%s", text)
}
wg.Done()
}()
fn(c)
}()
}
wg.Wait()
return
}