Skip to content

Commit a4d8b02

Browse files
committed
fix: fixed race conditions
1 parent dc1e1a5 commit a4d8b02

1 file changed

Lines changed: 72 additions & 35 deletions

File tree

keyboard.go

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package keyboard
33
import (
44
"fmt"
55
"os"
6+
"sync"
7+
"sync/atomic"
68

79
"github.com/containerd/console"
810

@@ -64,27 +66,39 @@ func stopListener() error {
6466
// return false, nil // Return false to continue listening
6567
// })
6668
func Listen(onKeyPress func(key keys.Key) (stop bool, err error)) error {
67-
cancel := make(chan bool)
68-
stopRoutine := false
69-
70-
go func() {
71-
for {
72-
select {
73-
case c := <-cancel:
74-
if c {
75-
return
69+
var (
70+
callbackMu sync.Mutex
71+
closeOnce sync.Once
72+
stopOnce sync.Once
73+
stopRoutine atomic.Bool
74+
)
75+
76+
stopped := make(chan struct{})
77+
stop := func() {
78+
stopOnce.Do(func() {
79+
stopRoutine.Store(true)
80+
closeOnce.Do(func() {
81+
closeInput()
82+
83+
if inputTTY != nil {
84+
_ = inputTTY.Close()
7685
}
86+
})
87+
close(stopped)
88+
})
89+
}
7790

78-
case keyInfo := <-mockChannel:
79-
stopRoutine, _ = onKeyPress(keyInfo)
80-
if stopRoutine {
81-
closeInput()
91+
handleKeyPress := func(key keys.Key) (bool, error) {
92+
callbackMu.Lock()
93+
defer callbackMu.Unlock()
8294

83-
_ = inputTTY.Close()
84-
}
85-
}
95+
shouldStop, err := onKeyPress(key)
96+
if shouldStop {
97+
stop()
8698
}
87-
}()
99+
100+
return shouldStop, err
101+
}
88102

89103
err := startListener()
90104
if err != nil {
@@ -93,28 +107,53 @@ func Listen(onKeyPress func(key keys.Key) (stop bool, err error)) error {
93107
}
94108
}
95109

96-
for !stopRoutine {
97-
key, keyErr := getKeyPress()
98-
if keyErr != nil {
99-
return keyErr
100-
}
110+
cancel := make(chan struct{})
111+
var mockWG sync.WaitGroup
112+
mockWG.Add(1)
113+
114+
go func() {
115+
defer mockWG.Done()
101116

102-
// check if returned key is empty
103-
// if reflect.DeepEqual(key, keys.Key{}) {
104-
// return nil
105-
// }
117+
for {
118+
select {
119+
case <-cancel:
120+
return
106121

107-
stop, stopErr := onKeyPress(key)
108-
if stopErr != nil {
109-
return stopErr
122+
case keyInfo := <-mockChannel:
123+
shouldStop, _ := handleKeyPress(keyInfo)
124+
if shouldStop {
125+
return
126+
}
127+
}
110128
}
129+
}()
111130

112-
if stop {
113-
closeInput()
131+
defer func() {
132+
close(cancel)
133+
mockWG.Wait()
134+
}()
135+
136+
if inputTTY == nil {
137+
<-stopped
138+
} else {
139+
for !stopRoutine.Load() {
140+
key, keyErr := getKeyPress()
141+
if keyErr != nil {
142+
return keyErr
143+
}
144+
145+
if stopRoutine.Load() {
146+
break
147+
}
114148

115-
_ = inputTTY.Close()
149+
shouldStop, stopErr := handleKeyPress(key)
150+
if stopErr != nil {
151+
return stopErr
152+
}
116153

117-
break
154+
if shouldStop {
155+
break
156+
}
118157
}
119158
}
120159

@@ -123,8 +162,6 @@ func Listen(onKeyPress func(key keys.Key) (stop bool, err error)) error {
123162
return err
124163
}
125164

126-
cancel <- true
127-
128165
return nil
129166
}
130167

0 commit comments

Comments
 (0)