mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
Make various goroutines panic-safe
This commit is contained in:
@@ -32,7 +32,7 @@ var highlighter = sync.OnceValue(func() highlight.Highlighter {
|
|||||||
func highlight_all(paths []string, light bool) {
|
func highlight_all(paths []string, light bool) {
|
||||||
ctx := images.Context{}
|
ctx := images.Context{}
|
||||||
srd := prefer_light_colors(light)
|
srd := prefer_light_colors(light)
|
||||||
ctx.Parallel(0, len(paths), func(nums <-chan int) {
|
if err := ctx.SafeParallel(0, len(paths), func(nums <-chan int) {
|
||||||
for i := range nums {
|
for i := range nums {
|
||||||
path := paths[i]
|
path := paths[i]
|
||||||
raw, err := highlighter().HighlightFile(path, &srd)
|
raw, err := highlighter().HighlightFile(path, &srd)
|
||||||
@@ -45,5 +45,7 @@ func highlight_all(paths []string, light bool) {
|
|||||||
dark_highlighted_lines_cache.Set(path, text_to_lines(raw))
|
dark_highlighted_lines_cache.Set(path, text_to_lines(raw))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,14 +358,16 @@ func diff(jobs []diff_job, context_count int) (ans map[string]*Patch, err error)
|
|||||||
patch *Patch
|
patch *Patch
|
||||||
}
|
}
|
||||||
results := make(chan result, len(jobs))
|
results := make(chan result, len(jobs))
|
||||||
ctx.Parallel(0, len(jobs), func(nums <-chan int) {
|
if err := ctx.SafeParallel(0, len(jobs), func(nums <-chan int) {
|
||||||
for i := range nums {
|
for i := range nums {
|
||||||
job := jobs[i]
|
job := jobs[i]
|
||||||
r := result{file1: job.file1, file2: job.file2}
|
r := result{file1: job.file1, file2: job.file2}
|
||||||
r.patch, r.err = do_diff(job.file1, job.file2, context_count)
|
r.patch, r.err = do_diff(job.file1, job.file2, context_count)
|
||||||
results <- r
|
results <- r
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
close(results)
|
close(results)
|
||||||
for r := range results {
|
for r := range results {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func (self *Search) search(logical_lines *LogicalLines) {
|
|||||||
self.matches = make(map[ScrollPos][]Span)
|
self.matches = make(map[ScrollPos][]Span)
|
||||||
ctx := images.Context{}
|
ctx := images.Context{}
|
||||||
mutex := sync.Mutex{}
|
mutex := sync.Mutex{}
|
||||||
ctx.Parallel(0, logical_lines.Len(), func(nums <-chan int) {
|
if err := ctx.SafeParallel(0, logical_lines.Len(), func(nums <-chan int) {
|
||||||
for i := range nums {
|
for i := range nums {
|
||||||
line := logical_lines.At(i)
|
line := logical_lines.At(i)
|
||||||
if line.line_type == EMPTY_LINE || line.line_type == IMAGE_LINE {
|
if line.line_type == EMPTY_LINE || line.line_type == IMAGE_LINE {
|
||||||
@@ -121,7 +121,9 @@ func (self *Search) search(logical_lines *LogicalLines) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
for _, spans := range self.matches {
|
for _, spans := range self.matches {
|
||||||
slices.SortFunc(spans, func(a, b Span) int { return a.start - b.start })
|
slices.SortFunc(spans, func(a, b Span) int { return a.start - b.start })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ func DownloadFileWithProgress(destpath, url string, kill_if_signaled bool) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
do_download := func() {
|
do_download := func() {
|
||||||
|
lp.RecoverFromPanicInGoRoutine()
|
||||||
dl_data.mutex.Lock()
|
dl_data.mutex.Lock()
|
||||||
dl_data.download_started = true
|
dl_data.download_started = true
|
||||||
dl_data.mutex.Unlock()
|
dl_data.mutex.Unlock()
|
||||||
|
|||||||
@@ -136,14 +136,16 @@ func (self *ImageCollection) ResizeForPageSize(width, height int) {
|
|||||||
|
|
||||||
ctx := images.Context{}
|
ctx := images.Context{}
|
||||||
keys := utils.Keys(self.images)
|
keys := utils.Keys(self.images)
|
||||||
ctx.Parallel(0, len(keys), func(nums <-chan int) {
|
if err := ctx.SafeParallel(0, len(keys), func(nums <-chan int) {
|
||||||
for i := range nums {
|
for i := range nums {
|
||||||
img := self.images[keys[i]]
|
img := self.images[keys[i]]
|
||||||
if img.src.loaded && img.err == nil {
|
if img.src.loaded && img.err == nil {
|
||||||
img.ResizeForPageSize(width, height)
|
img.ResizeForPageSize(width, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ImageCollection) DeleteAllVisiblePlacements(lp *loop.Loop) {
|
func (self *ImageCollection) DeleteAllVisiblePlacements(lp *loop.Loop) {
|
||||||
@@ -294,7 +296,7 @@ func (self *ImageCollection) LoadAll() {
|
|||||||
defer self.mutex.Unlock()
|
defer self.mutex.Unlock()
|
||||||
ctx := images.Context{}
|
ctx := images.Context{}
|
||||||
all := utils.Values(self.images)
|
all := utils.Values(self.images)
|
||||||
ctx.Parallel(0, len(self.images), func(nums <-chan int) {
|
if err := ctx.SafeParallel(0, len(self.images), func(nums <-chan int) {
|
||||||
for i := range nums {
|
for i := range nums {
|
||||||
img := all[i]
|
img := all[i]
|
||||||
if !img.src.loaded {
|
if !img.src.loaded {
|
||||||
@@ -305,7 +307,9 @@ func (self *ImageCollection) LoadAll() {
|
|||||||
img.src.loaded = true
|
img.src.loaded = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImageCollection(paths ...string) *ImageCollection {
|
func NewImageCollection(paths ...string) *ImageCollection {
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ func read_ignoring_temporary_errors(f *tty.Term, buf []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func read_from_tty(pipe_r *os.File, term *tty.Term, results_channel chan<- []byte, err_channel chan<- error, quit_channel <-chan byte, leftover_channel chan<- []byte) {
|
func read_from_tty(pipe_r *os.File, term *tty.Term, results_channel chan<- []byte, err_channel chan<- error, quit_channel <-chan byte, leftover_channel chan<- []byte) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
text, _ := utils.Format_stacktrace_on_panic(r)
|
||||||
|
err_channel <- fmt.Errorf("%s", text)
|
||||||
|
}
|
||||||
|
}()
|
||||||
keep_going := true
|
keep_going := true
|
||||||
pipe_fd := int(pipe_r.Fd())
|
pipe_fd := int(pipe_r.Fd())
|
||||||
tty_fd := term.Fd()
|
tty_fd := term.Fd()
|
||||||
@@ -115,6 +121,12 @@ func read_until_primary_device_attributes_response(term *tty.Term, initial_bytes
|
|||||||
}
|
}
|
||||||
received := make(chan error)
|
received := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
text, _ := utils.Format_stacktrace_on_panic(r)
|
||||||
|
received <- fmt.Errorf("%s", text)
|
||||||
|
}
|
||||||
|
}()
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, err := read_ignoring_temporary_errors(term, buf)
|
n, err := read_ignoring_temporary_errors(term, buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
|||||||
@@ -168,6 +168,12 @@ func write_to_tty(
|
|||||||
pipe_r *os.File, term *tty.Term,
|
pipe_r *os.File, term *tty.Term,
|
||||||
job_channel <-chan write_msg, err_channel chan<- error, write_done_channel chan<- IdType,
|
job_channel <-chan write_msg, err_channel chan<- error, write_done_channel chan<- IdType,
|
||||||
) {
|
) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
text, _ := utils.Format_stacktrace_on_panic(r)
|
||||||
|
err_channel <- fmt.Errorf("%s", text)
|
||||||
|
}
|
||||||
|
}()
|
||||||
keep_going := true
|
keep_going := true
|
||||||
defer func() {
|
defer func() {
|
||||||
pipe_r.Close()
|
pipe_r.Close()
|
||||||
|
|||||||
@@ -110,11 +110,13 @@ func marks_for_query(query string) (ans mark_set) {
|
|||||||
prefixes := strings.Split(strings.ToLower(query), " ")
|
prefixes := strings.Split(strings.ToLower(query), " ")
|
||||||
results := make(chan mark_set, len(prefixes))
|
results := make(chan mark_set, len(prefixes))
|
||||||
ctx := images.Context{}
|
ctx := images.Context{}
|
||||||
ctx.Parallel(0, len(prefixes), func(nums <-chan int) {
|
if err := ctx.SafeParallel(0, len(prefixes), func(nums <-chan int) {
|
||||||
for i := range nums {
|
for i := range nums {
|
||||||
results <- find_matching_codepoints(prefixes[i])
|
results <- find_matching_codepoints(prefixes[i])
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
close(results)
|
close(results)
|
||||||
for x := range results {
|
for x := range results {
|
||||||
if ans == nil {
|
if ans == nil {
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ func (self *Context) run_paste(src Scanner, background image.Image, pos image.Po
|
|||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unsupported image type: %v", v))
|
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 {
|
for y := range ys {
|
||||||
x1 := interRect.Min.X - pasteRect.Min.X
|
x1 := interRect.Min.X - pasteRect.Min.X
|
||||||
x2 := interRect.Max.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)
|
src.scan(x1, y1, x2, y2, dst)
|
||||||
postprocess(dst)
|
postprocess(dst)
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
func (self *Context) FlipPixelsH(bytes_per_pixel, width, height int, pix []uint8) {
|
||||||
stride := bytes_per_pixel * width
|
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 {
|
for y := range ys {
|
||||||
i := y * stride
|
i := y * stride
|
||||||
reverse_row(bytes_per_pixel, pix[i:i+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) {
|
func (self *Context) FlipPixelsV(bytes_per_pixel, width, height int, pix []uint8) {
|
||||||
stride := bytes_per_pixel * width
|
stride := bytes_per_pixel * width
|
||||||
num := height / 2
|
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 {
|
for y := range ys {
|
||||||
upper := y
|
upper := y
|
||||||
lower := height - 1 - 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]
|
as[i], bs[i] = bs[i], as[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/kovidgoyal/kitty/tools/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@@ -31,8 +33,10 @@ func (self *Context) EffectiveNumberOfThreads() int {
|
|||||||
return ans
|
return ans
|
||||||
}
|
}
|
||||||
|
|
||||||
// parallel processes the data in separate goroutines.
|
// parallel processes the data in separate goroutines. If any of them panics,
|
||||||
func (self *Context) Parallel(start, stop int, fn func(<-chan int)) {
|
// 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
|
count := stop - start
|
||||||
if count < 1 {
|
if count < 1 {
|
||||||
return
|
return
|
||||||
@@ -49,9 +53,16 @@ func (self *Context) Parallel(start, stop int, fn func(<-chan int)) {
|
|||||||
for range procs {
|
for range procs {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
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)
|
fn(c)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user