diff options
author | Hubert Hirtz <hubert@hirtz.pm> | 2021-11-16 09:20:21 +0100 |
---|---|---|
committer | Hubert Hirtz <hubert@hirtz.pm> | 2021-11-16 22:22:10 +0100 |
commit | d40d8dc36a608a1349f8353c50c2c71649e6fa75 (patch) | |
tree | d43e01ac710680c5e765e4726ff8928ec2431499 | |
parent | Don't merge message bounds from multiple networks (diff) |
Allow App.Close() and App.Run() to be run concurrently
-rw-r--r-- | app.go | 34 | ||||
-rw-r--r-- | irc/session.go | 1 | ||||
-rw-r--r-- | irc/typing.go | 17 | ||||
-rw-r--r-- | ui/ui.go | 8 |
4 files changed, 43 insertions, 17 deletions
@@ -137,7 +137,11 @@ func NewApp(cfg Config) (app *App, err error) { } func (app *App) Close() { - app.win.Close() + app.win.Exit() // tell all instances of app.ircLoop to stop when possible + app.events <- event{ // tell app.eventLoop to stop + src: "*", + content: nil, + } for _, session := range app.sessions { session.Close() } @@ -166,6 +170,8 @@ func (app *App) CurrentBuffer() (netID, buffer string) { // eventLoop retrieves events (in batches) from the event channel and handle // them, then draws the interface after each batch is handled. func (app *App) eventLoop() { + defer app.win.Close() + evs := make([]event, 0, eventChanSize) for !app.win.ShouldExit() { ev := <-app.events @@ -181,7 +187,17 @@ func (app *App) eventLoop() { } } - app.handleEvents(evs) + for _, ev := range evs { + if ev.src == "*" { + if ev.content == nil { + return + } + app.handleUIEvent(ev.content) + } else { + app.handleIRCEvent(ev.src, ev.content) + } + } + if !app.pasting { app.setStatus() app.updatePrompt() @@ -255,6 +271,9 @@ func (app *App) ircLoop(netID string) { HeadColor: tcell.ColorRed, Body: ui.PlainString("Connection lost"), }) + if app.win.ShouldExit() { + break + } time.Sleep(10 * time.Second) } } @@ -340,17 +359,6 @@ func (app *App) uiLoop() { } } -// handleEvents handles a batch of events. -func (app *App) handleEvents(evs []event) { - for _, ev := range evs { - if ev.src == "*" { - app.handleUIEvent(ev.content) - } else { - app.handleIRCEvent(ev.src, ev.content) - } - } -} - func (app *App) handleUIEvent(ev interface{}) { switch ev := ev.(type) { case *tcell.EventResize: diff --git a/irc/session.go b/irc/session.go index 084c6a7..07df816 100644 --- a/irc/session.go +++ b/irc/session.go @@ -170,6 +170,7 @@ func (s *Session) Close() { return } s.closed = true + s.typings.Close() close(s.out) } diff --git a/irc/typing.go b/irc/typing.go index fd1576c..4a8cf55 100644 --- a/irc/typing.go +++ b/irc/typing.go @@ -16,6 +16,7 @@ type Typing struct { // Typings keeps track of typing notification timeouts. type Typings struct { l sync.Mutex + closed bool // whether Close has been called targets map[Typing]time.Time // @+typing TAGMSG timestamps. timeouts chan Typing // transmits unfiltered timeout notifications. stops chan Typing // transmits filtered timeout notifications. @@ -45,10 +46,14 @@ func NewTypings() *Typings { return ts } -// Stop cleanly closes all channels and stops all coroutines. -func (ts *Typings) Stop() { +// Close cleanly closes all channels and stops all goroutines. +func (ts *Typings) Close() { + ts.l.Lock() + defer ts.l.Unlock() + close(ts.timeouts) close(ts.stops) + ts.closed = true } // Stops is a channel that transmits typing timeouts. @@ -65,7 +70,13 @@ func (ts *Typings) Active(target, name string) { go func() { time.Sleep(6 * time.Second) - ts.timeouts <- t + + ts.l.Lock() + defer ts.l.Unlock() + + if !ts.closed { + ts.timeouts <- t + } }() } @@ -60,8 +60,14 @@ func New(config Config) (ui *UI, err error) { ui.Events = make(chan tcell.Event, 128) go func() { for !ui.ShouldExit() { - ui.Events <- ui.screen.PollEvent() + ev := ui.screen.PollEvent() + if ev == nil { + ui.Exit() + break + } + ui.Events <- ev } + close(ui.Events) }() ui.bs = NewBufferList() |