Loop API print proper stack traces for panics in go routines

This commit is contained in:
Kovid Goyal
2025-06-01 12:51:59 +05:30
parent ecb9c46b95
commit d1faccdd1c
4 changed files with 23 additions and 4 deletions

View File

@@ -229,6 +229,7 @@ func (h *Handler) get_results() (ans []*ResultItem, in_progress bool) {
sc.search_text = st sc.search_text = st
sp := h.state.ScorePatterns() sp := h.state.ScorePatterns()
go func() { go func() {
defer h.lp.RecoverFromPanicInGoRoutine()
results := sc.scan(cd, st, sp) results := sc.scan(cd, st, sp)
sc.mutex.Lock() sc.mutex.Lock()
defer sc.mutex.Unlock() defer sc.mutex.Unlock()

View File

@@ -171,6 +171,7 @@ func (self *Handler) initialize() {
self.original_context_count = self.current_context_count self.original_context_count = self.current_context_count
self.async_results = make(chan AsyncResult, 32) self.async_results = make(chan AsyncResult, 32)
go func() { go func() {
self.lp.RecoverFromPanicInGoRoutine()
r := AsyncResult{} r := AsyncResult{}
r.collection, r.err = create_collection(self.left, self.right) r.collection, r.err = create_collection(self.left, self.right)
self.async_results <- r self.async_results <- r
@@ -191,6 +192,7 @@ func (self *Handler) generate_diff() {
return nil return nil
}) })
go func() { go func() {
self.lp.RecoverFromPanicInGoRoutine()
r := AsyncResult{rtype: DIFF} r := AsyncResult{rtype: DIFF}
r.diff_map, r.err = diff(jobs, self.current_context_count) r.diff_map, r.err = diff(jobs, self.current_context_count)
self.async_results <- r self.async_results <- r
@@ -230,6 +232,7 @@ func (self *Handler) highlight_all() {
} }
text_files := utils.Filter(self.collection.paths_to_highlight.AsSlice(), is_path_text) text_files := utils.Filter(self.collection.paths_to_highlight.AsSlice(), is_path_text)
go func() { go func() {
self.lp.RecoverFromPanicInGoRoutine()
r := AsyncResult{rtype: HIGHLIGHT} r := AsyncResult{rtype: HIGHLIGHT}
highlight_all(text_files, use_light_colors) highlight_all(text_files, use_light_colors)
self.async_results <- r self.async_results <- r
@@ -252,6 +255,7 @@ func (self *Handler) load_all_images() {
if self.image_count > 0 { if self.image_count > 0 {
image_collection.Initialize(self.lp) image_collection.Initialize(self.lp)
go func() { go func() {
self.lp.RecoverFromPanicInGoRoutine()
r := AsyncResult{rtype: IMAGE_LOAD} r := AsyncResult{rtype: IMAGE_LOAD}
image_collection.LoadAll() image_collection.LoadAll()
self.async_results <- r self.async_results <- r
@@ -273,6 +277,7 @@ func (self *Handler) resize_all_images_if_needed() {
} }
if sz != self.images_resized_to && self.image_count > 0 { if sz != self.images_resized_to && self.image_count > 0 {
go func() { go func() {
self.lp.RecoverFromPanicInGoRoutine()
image_collection.ResizeForPageSize(sz.Width, sz.Height) image_collection.ResizeForPageSize(sz.Width, sz.Height)
r := AsyncResult{rtype: IMAGE_RESIZE, page_size: sz} r := AsyncResult{rtype: IMAGE_RESIZE, page_size: sz}
self.async_results <- r self.async_results <- r

View File

@@ -49,6 +49,7 @@ type Loop struct {
timers, timers_temp []*timer timers, timers_temp []*timer
timer_id_counter, write_msg_id_counter IdType timer_id_counter, write_msg_id_counter IdType
wakeup_channel chan byte wakeup_channel chan byte
panic_channel chan any
pending_writes []write_msg pending_writes []write_msg
tty_write_channel chan write_msg tty_write_channel chan write_msg
pending_mouse_events *utils.RingBuffer[MouseEvent] pending_mouse_events *utils.RingBuffer[MouseEvent]
@@ -328,11 +329,14 @@ func (self *Loop) Run() (err error) {
lines = append(lines, fmt.Sprintf("%s\r\n\t%s:%d\r\n", frame.Function, frame.File, frame.Line)) lines = append(lines, fmt.Sprintf("%s\r\n\t%s:%d\r\n", frame.Function, frame.File, frame.Line))
} }
text := strings.Join(lines, "") text := strings.Join(lines, "")
os.Stderr.WriteString(text)
tty.DebugPrintln(strings.TrimSpace(text)) tty.DebugPrintln(strings.TrimSpace(text))
if self.terminal_options.Alternate_screen { is_terminal := tty.IsTerminal(os.Stderr.Fd())
term, err := tty.OpenControllingTerm(tty.SetRaw) if is_terminal {
if err == nil { os.Stderr.WriteString("\x1b]\x1b\\\x1bc\x1b[H\x1b[2J") // reset terminal
}
os.Stderr.WriteString(text)
if is_terminal {
if term, err := tty.OpenControllingTerm(tty.SetRaw); err == nil {
defer term.RestoreAndClose() defer term.RestoreAndClose()
fmt.Println("Press any key to exit.\r") fmt.Println("Press any key to exit.\r")
buf := make([]byte, 16) buf := make([]byte, 16)
@@ -588,6 +592,12 @@ type SizedText struct {
Width int Width int
} }
func (self *Loop) RecoverFromPanicInGoRoutine() {
if r := recover(); r != nil {
self.panic_channel <- r
}
}
func (self *Loop) DrawSizedText(text string, spec SizedText) { func (self *Loop) DrawSizedText(text string, spec SizedText) {
b := strings.Builder{} b := strings.Builder{}
b.Grow(len(text) + 24) b.Grow(len(text) + 24)

View File

@@ -394,6 +394,7 @@ func (self *Loop) run() (err error) {
self.write_msg_id_counter = 0 self.write_msg_id_counter = 0
write_done_channel := make(chan IdType) write_done_channel := make(chan IdType)
self.wakeup_channel = make(chan byte, 256) self.wakeup_channel = make(chan byte, 256)
self.panic_channel = make(chan any)
self.pending_writes = make([]write_msg, 0, 256) self.pending_writes = make([]write_msg, 0, 256)
err_channel := make(chan error, 8) err_channel := make(chan error, 8)
self.death_signal = SIGNULL self.death_signal = SIGNULL
@@ -563,6 +564,8 @@ func (self *Loop) run() (err error) {
} }
select { select {
case <-timeout_chan: case <-timeout_chan:
case p := <-self.panic_channel:
panic(p)
case <-self.wakeup_channel: case <-self.wakeup_channel:
for len(self.wakeup_channel) > 0 { for len(self.wakeup_channel) > 0 {
<-self.wakeup_channel <-self.wakeup_channel