commit d40d8dc
Hubert Hirtz
·
2021-11-16 08:20:21 +0000 UTC
parent 0e73077
Allow App.Close() and App.Run() to be run concurrently
4 files changed,
+43,
-17
M
app.go
+21,
-13
1@@ -137,7 +137,11 @@ func NewApp(cfg Config) (app *App, err error) {
2 }
3
4 func (app *App) Close() {
5- app.win.Close()
6+ app.win.Exit() // tell all instances of app.ircLoop to stop when possible
7+ app.events <- event{ // tell app.eventLoop to stop
8+ src: "*",
9+ content: nil,
10+ }
11 for _, session := range app.sessions {
12 session.Close()
13 }
14@@ -166,6 +170,8 @@ func (app *App) CurrentBuffer() (netID, buffer string) {
15 // eventLoop retrieves events (in batches) from the event channel and handle
16 // them, then draws the interface after each batch is handled.
17 func (app *App) eventLoop() {
18+ defer app.win.Close()
19+
20 evs := make([]event, 0, eventChanSize)
21 for !app.win.ShouldExit() {
22 ev := <-app.events
23@@ -181,7 +187,17 @@ func (app *App) eventLoop() {
24 }
25 }
26
27- app.handleEvents(evs)
28+ for _, ev := range evs {
29+ if ev.src == "*" {
30+ if ev.content == nil {
31+ return
32+ }
33+ app.handleUIEvent(ev.content)
34+ } else {
35+ app.handleIRCEvent(ev.src, ev.content)
36+ }
37+ }
38+
39 if !app.pasting {
40 app.setStatus()
41 app.updatePrompt()
42@@ -255,6 +271,9 @@ func (app *App) ircLoop(netID string) {
43 HeadColor: tcell.ColorRed,
44 Body: ui.PlainString("Connection lost"),
45 })
46+ if app.win.ShouldExit() {
47+ break
48+ }
49 time.Sleep(10 * time.Second)
50 }
51 }
52@@ -340,17 +359,6 @@ func (app *App) uiLoop() {
53 }
54 }
55
56-// handleEvents handles a batch of events.
57-func (app *App) handleEvents(evs []event) {
58- for _, ev := range evs {
59- if ev.src == "*" {
60- app.handleUIEvent(ev.content)
61- } else {
62- app.handleIRCEvent(ev.src, ev.content)
63- }
64- }
65-}
66-
67 func (app *App) handleUIEvent(ev interface{}) {
68 switch ev := ev.(type) {
69 case *tcell.EventResize:
+1,
-0
1@@ -170,6 +170,7 @@ func (s *Session) Close() {
2 return
3 }
4 s.closed = true
5+ s.typings.Close()
6 close(s.out)
7 }
8
+14,
-3
1@@ -16,6 +16,7 @@ type Typing struct {
2 // Typings keeps track of typing notification timeouts.
3 type Typings struct {
4 l sync.Mutex
5+ closed bool // whether Close has been called
6 targets map[Typing]time.Time // @+typing TAGMSG timestamps.
7 timeouts chan Typing // transmits unfiltered timeout notifications.
8 stops chan Typing // transmits filtered timeout notifications.
9@@ -45,10 +46,14 @@ func NewTypings() *Typings {
10 return ts
11 }
12
13-// Stop cleanly closes all channels and stops all coroutines.
14-func (ts *Typings) Stop() {
15+// Close cleanly closes all channels and stops all goroutines.
16+func (ts *Typings) Close() {
17+ ts.l.Lock()
18+ defer ts.l.Unlock()
19+
20 close(ts.timeouts)
21 close(ts.stops)
22+ ts.closed = true
23 }
24
25 // Stops is a channel that transmits typing timeouts.
26@@ -65,7 +70,13 @@ func (ts *Typings) Active(target, name string) {
27
28 go func() {
29 time.Sleep(6 * time.Second)
30- ts.timeouts <- t
31+
32+ ts.l.Lock()
33+ defer ts.l.Unlock()
34+
35+ if !ts.closed {
36+ ts.timeouts <- t
37+ }
38 }()
39 }
40
M
ui/ui.go
+7,
-1
1@@ -60,8 +60,14 @@ func New(config Config) (ui *UI, err error) {
2 ui.Events = make(chan tcell.Event, 128)
3 go func() {
4 for !ui.ShouldExit() {
5- ui.Events <- ui.screen.PollEvent()
6+ ev := ui.screen.PollEvent()
7+ if ev == nil {
8+ ui.Exit()
9+ break
10+ }
11+ ui.Events <- ev
12 }
13+ close(ui.Events)
14 }()
15
16 ui.bs = NewBufferList()