Skip to content

Commit e678e55

Browse files
dharmabclaude
andcommitted
Replace gammazero/deque with dharmab/collections
The old deque panicked on empty access, forcing callers to guard with length checks or risk crashes. The new Counting deque returns (T, bool) from Newest/At/Pop, letting us propagate empty-state cleanly. Also fixes a potential deadlock where Direction() re-acquired RLock via Course(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1078da5 commit e678e55

3 files changed

Lines changed: 42 additions & 36 deletions

File tree

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
module github.com/dharmab/skyeye
22

3-
go 1.26.0
3+
go 1.26.2
44

55
require (
66
github.com/DCS-gRPC/go-bindings v0.7.1
77
github.com/amitybell/piper-asset v0.0.0-20231030194325-d36a29e3b1fd
88
github.com/amitybell/piper-voice-alan v0.0.0-20231118093148-059963c24dbd
99
github.com/amitybell/piper-voice-jenny v0.0.0-20231118093224-dcf0d49e46b7
1010
github.com/bwmarrin/discordgo v0.28.1
11+
github.com/dharmab/collections v1.0.0
1112
github.com/dharmab/goacmi v1.0.3
1213
github.com/dharmab/numwords v1.0.1
13-
github.com/gammazero/deque v0.2.1
1414
github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20260319084013-9386f2394010
1515
github.com/go-audio/aiff v1.1.0
1616
github.com/gofrs/flock v0.13.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
180180
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
181181
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
182182
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
183+
github.com/dharmab/collections v1.0.0 h1:61nyF4uDE8JvyiGC2VPz0rDXO6WVqKTlIpuXmzhreyo=
184+
github.com/dharmab/collections v1.0.0/go.mod h1:XL7nAecAuQ07ZGWSM0/kqVyl1z8ucE/jLUiIWQo6jCA=
183185
github.com/dharmab/goacmi v1.0.3 h1:NSxBKlLgJlI/V7Q68lh2uLZC6BJgZkphelKKEmSaEMc=
184186
github.com/dharmab/goacmi v1.0.3/go.mod h1:wZArjjRgFNYtQJkA9vBTbpM3FqkP3fg+bhQJzkEwpq0=
185187
github.com/dharmab/numwords v1.0.1 h1:6zFMoWEiavZmQAiyd1/0h8x/5gzpj8ijbZWgcIHoQAA=
@@ -208,8 +210,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
208210
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
209211
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
210212
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
211-
github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
212-
github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
213213
github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20260319084013-9386f2394010 h1:+m6yO3VYSAUUzpG9lz4ktBx0cCba/Nvww8OKDgnapQQ=
214214
github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20260319084013-9386f2394010/go.mod h1:qyHjS/50ORo01H0NsuEEGsQR9VCtOcEye0gUl2sx1s8=
215215
github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1f+MzD0=

pkg/trackfiles/trackfile.go

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import (
77
"sync"
88
"time"
99

10+
"github.com/dharmab/collections/deques"
1011
"github.com/dharmab/skyeye/pkg/bearings"
1112
"github.com/dharmab/skyeye/pkg/brevity"
1213
"github.com/dharmab/skyeye/pkg/coalitions"
1314
"github.com/dharmab/skyeye/pkg/spatial"
14-
"github.com/gammazero/deque"
1515
"github.com/martinlindhe/unit"
1616
"github.com/paulmach/orb"
1717
"github.com/rs/zerolog/log"
@@ -36,7 +36,7 @@ type Trackfile struct {
3636
// Contact contains identifying information.
3737
Contact Labels
3838
// track is a collection of frames, ordered from most recent to least recent.
39-
track deque.Deque[Frame]
39+
track *deques.Counting[Frame]
4040
lock sync.RWMutex
4141
}
4242

@@ -61,7 +61,7 @@ type Frame struct {
6161
func New(labels Labels) *Trackfile {
6262
return &Trackfile{
6363
Contact: labels,
64-
track: *deque.New[Frame](),
64+
track: deques.NewCounting[Frame](maxLength),
6565
}
6666
}
6767

@@ -84,13 +84,10 @@ func (t *Trackfile) String() string {
8484
func (t *Trackfile) Update(f Frame) {
8585
t.lock.Lock()
8686
defer t.lock.Unlock()
87-
if t.track.Len() > 0 && f.Time.Before(t.track.Front().Time) {
87+
if newest, ok := t.track.Newest(); ok && f.Time.Before(newest.Time) {
8888
return
8989
}
90-
t.track.PushFront(f)
91-
for t.track.Len() > maxLength {
92-
t.track.PopBack()
93-
}
90+
t.track.Push(f)
9491
}
9592

9693
// Bullseye returns the bearing and distance from the bullseye to the track's last known position.
@@ -108,16 +105,10 @@ func (t *Trackfile) Bullseye(bullseye orb.Point, opts ...spatial.Option) *brevit
108105
func (t *Trackfile) LastKnown() Frame {
109106
t.lock.RLock()
110107
defer t.lock.RUnlock()
111-
return t.unsafeLastKnown()
112-
}
113-
114-
// unsafeLastKnown is like LastKnown, but it does not acquire a lock. The calling
115-
// function must acquire t.lock before calling this function.
116-
func (t *Trackfile) unsafeLastKnown() Frame {
117-
if t.track.Len() == 0 {
118-
return Frame{}
108+
if f, ok := t.track.Newest(); ok {
109+
return f
119110
}
120-
return t.track.Front()
111+
return Frame{}
121112
}
122113

123114
// IsLastKnownPointZero returns true if the last known point is at (0, 0).
@@ -126,8 +117,12 @@ func (t *Trackfile) IsLastKnownPointZero() bool {
126117
return spatial.IsZero(t.LastKnown().Point)
127118
}
128119

120+
// bestAvailableDeclination returns the magnetic declination at the track's most recent position, or 0 if unavailable.
129121
func (t *Trackfile) bestAvailableDeclination() unit.Angle {
130-
latest := t.unsafeLastKnown()
122+
latest, ok := t.track.Newest()
123+
if !ok {
124+
return 0
125+
}
131126
declincation, err := bearings.Declination(latest.Point, latest.Time)
132127
if err != nil {
133128
return 0
@@ -141,14 +136,22 @@ func (t *Trackfile) bestAvailableDeclination() unit.Angle {
141136
func (t *Trackfile) Course(opts ...spatial.Option) bearings.Bearing {
142137
t.lock.RLock()
143138
defer t.lock.RUnlock()
139+
return t.computeCourse(opts...)
140+
}
141+
142+
// computeCourse returns the magnetic bearing between the two most recent frames, or the heading if only one frame exists. Caller must hold t.lock.
143+
func (t *Trackfile) computeCourse(opts ...spatial.Option) bearings.Bearing {
144+
latest, ok := t.track.Newest()
145+
if !ok {
146+
return bearings.NewTrueBearing(0)
147+
}
144148
if t.track.Len() == 1 {
145149
return bearings.NewTrueBearing(
146-
t.track.Front().Heading,
150+
latest.Heading,
147151
).Magnetic(t.bestAvailableDeclination())
148152
}
149153

150-
latest := t.track.Front()
151-
previous := t.track.At(1)
154+
previous, _ := t.track.At(1)
152155

153156
declination := t.bestAvailableDeclination()
154157
course := spatial.TrueBearing(previous.Point, latest.Point, opts...).Magnetic(declination)
@@ -166,18 +169,19 @@ func (t *Trackfile) Direction() brevity.Track {
166169
return brevity.UnknownDirection
167170
}
168171

169-
course := t.Course()
170-
return brevity.TrackFromBearing(course)
172+
return brevity.TrackFromBearing(t.computeCourse())
171173
}
172174

173-
// groundSpeed returns the approximate speed of the track along the ground (i.e. in two dimensions).
175+
// groundSpeed returns the approximate ground speed of the track in two dimensions. Caller must hold t.lock.
174176
func (t *Trackfile) groundSpeed(opts ...spatial.Option) unit.Speed {
175-
if t.track.Len() < 2 {
177+
latest, ok := t.track.Newest()
178+
if !ok {
179+
return 0
180+
}
181+
previous, ok := t.track.At(1)
182+
if !ok {
176183
return 0
177184
}
178-
179-
latest := t.track.Front()
180-
previous := t.track.At(1)
181185

182186
timeDelta := latest.Time.Sub(previous.Time)
183187
if timeDelta == 0 {
@@ -197,12 +201,14 @@ func (t *Trackfile) groundSpeed(opts ...spatial.Option) unit.Speed {
197201
func (t *Trackfile) Speed() unit.Speed {
198202
t.lock.RLock()
199203
defer t.lock.RUnlock()
200-
if t.track.Len() < 2 {
204+
latest, ok := t.track.Newest()
205+
if !ok {
206+
return 0
207+
}
208+
previous, ok := t.track.At(1)
209+
if !ok {
201210
return 0
202211
}
203-
204-
latest := t.track.Front()
205-
previous := t.track.At(1)
206212

207213
timeDelta := latest.Time.Sub(previous.Time)
208214
if timeDelta == 0 {

0 commit comments

Comments
 (0)