Skip to content

Commit 6854748

Browse files
committed
Remove data races
1 parent b25cf9b commit 6854748

File tree

11 files changed

+84
-43
lines changed

11 files changed

+84
-43
lines changed

internal/app/ci/ci.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslconn"
77
"log"
88
"net"
9+
"sync"
910
"time"
1011
)
1112

@@ -17,6 +18,7 @@ type Server struct {
1718
conn net.Conn
1819
latestTime time.Time
1920
tickChan chan time.Time
21+
mutex sync.Mutex
2022
TrackerConsumer func(*tracker.TrackerWrapperPacket)
2123
}
2224

@@ -77,7 +79,9 @@ func (s *Server) serve(conn net.Conn) {
7779
if input.Timestamp != nil {
7880
sec := *input.Timestamp / 1e9
7981
nSec := *input.Timestamp - sec*1e9
82+
s.mutex.Lock()
8083
s.latestTime = time.Unix(sec, nSec)
84+
s.mutex.Unlock()
8185
select {
8286
case s.tickChan <- time.Now():
8387
default:
@@ -99,6 +103,8 @@ func (s *Server) SendMessage(refMsg *state.Referee) {
99103
}
100104

101105
func (s *Server) Time() time.Time {
106+
s.mutex.Lock()
107+
defer s.mutex.Unlock()
102108
return s.latestTime
103109
}
104110

internal/app/engine/consume_geometry.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import (
77
)
88

99
func (e *Engine) ProcessGeometry(data *vision.SSL_GeometryData) {
10+
e.mutex.Lock()
11+
defer e.mutex.Unlock()
1012

11-
currentGeometry := e.GetGeometry()
13+
currentGeometry := e.stateMachine.Geometry
1214
newGeometry := currentGeometry
1315

1416
newGeometry.FieldWidth = float64(*data.Field.FieldWidth) / 1000.0
@@ -22,7 +24,7 @@ func (e *Engine) ProcessGeometry(data *vision.SSL_GeometryData) {
2224
}
2325
}
2426

25-
e.SetGeometry(newGeometry)
27+
e.stateMachine.Geometry = newGeometry
2628

2729
if currentGeometry != newGeometry {
2830
log.Printf("Geometry changed from %v to %v", currentGeometry, newGeometry)

internal/app/engine/consume_tracker.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
)
66

77
func (e *Engine) ProcessTrackerFrame(wrapperFrame *tracker.TrackerWrapperPacket) {
8+
e.mutex.Lock()
9+
defer e.mutex.Unlock()
10+
811
if wrapperFrame.TrackedFrame == nil {
912
return
1013
}
@@ -17,17 +20,15 @@ func (e *Engine) ProcessTrackerFrame(wrapperFrame *tracker.TrackerWrapperPacket)
1720

1821
readyToContinue := e.readyToContinue()
1922

20-
e.UpdateGcState(func(gcState *GcState) {
21-
gcState.TrackerState[*wrapperFrame.Uuid] = &state
23+
e.gcState.TrackerState[*wrapperFrame.Uuid] = &state
2224

23-
// for now, all tracker sources update the GC state
24-
e.gcState.TrackerStateGc = &state
25+
// for now, all tracker sources update the GC state
26+
e.gcState.TrackerStateGc = &state
2527

26-
if e.gcState.ReadyToContinue == nil {
27-
e.gcState.ReadyToContinue = new(bool)
28-
}
29-
*e.gcState.ReadyToContinue = readyToContinue
30-
})
28+
if e.gcState.ReadyToContinue == nil {
29+
e.gcState.ReadyToContinue = new(bool)
30+
}
31+
*e.gcState.ReadyToContinue = readyToContinue
3132
}
3233

3334
func convertRobots(robots []*tracker.TrackedRobot) (rs []*Robot) {

internal/app/engine/engine.go

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type Engine struct {
2727
timeProvider timer.TimeProvider
2828
lastTimeUpdate time.Time
2929
gcState *GcState
30-
gcStateMutex sync.Mutex
30+
mutex sync.Mutex
3131
noProgressDetector NoProgressDetector
3232
ballPlacementCoordinator BallPlacementCoordinator
3333
tickChanProvider func() <-chan time.Time
@@ -68,29 +68,30 @@ func (e *Engine) Enqueue(change *statemachine.Change) {
6868
e.queue <- change
6969
}
7070

71-
// SetGeometry sets a new geometry
72-
func (e *Engine) SetGeometry(geometry config.Geometry) {
73-
e.stateMachine.Geometry = geometry
74-
}
75-
76-
// GetGeometry returns the current geometry
77-
func (e *Engine) GetGeometry() config.Geometry {
71+
// getGeometry returns the current geometry
72+
func (e *Engine) getGeometry() config.Geometry {
7873
return e.stateMachine.Geometry
7974
}
8075

8176
// SetTimeProvider sets a new time provider for this engine
8277
func (e *Engine) SetTimeProvider(provider timer.TimeProvider) {
78+
e.mutex.Lock()
79+
defer e.mutex.Unlock()
8380
e.timeProvider = provider
8481
e.lastTimeUpdate = e.timeProvider()
8582
}
8683

8784
// SetTickChanProvider sets an alternative provider for the tick channel
8885
func (e *Engine) SetTickChanProvider(provider func() <-chan time.Time) {
86+
e.mutex.Lock()
87+
defer e.mutex.Unlock()
8988
e.tickChanProvider = provider
9089
}
9190

9291
// Start loads the state store and runs a go routine that consumes the change queue
9392
func (e *Engine) Start() error {
93+
e.mutex.Lock()
94+
defer e.mutex.Unlock()
9495
if err := e.stateStore.Open(); err != nil {
9596
return errors.Wrap(err, "Could not open state store")
9697
}
@@ -105,6 +106,8 @@ func (e *Engine) Start() error {
105106

106107
// Stop stops the go routine that processes the change queue
107108
func (e *Engine) Stop() {
109+
e.mutex.Lock()
110+
defer e.mutex.Unlock()
108111
close(e.queue)
109112
if err := e.stateStore.Close(); err != nil {
110113
log.Printf("Could not close store: %v", err)
@@ -113,28 +116,30 @@ func (e *Engine) Stop() {
113116

114117
// CurrentState returns a deep copy of the current state
115118
func (e *Engine) CurrentState() (s *state.State) {
116-
s = new(state.State)
117-
proto.Merge(s, e.currentState)
118-
return
119+
e.mutex.Lock()
120+
defer e.mutex.Unlock()
121+
return e.currentState.Clone()
119122
}
120123

121124
// CurrentGcState returns a deep copy of the current GC state
122125
func (e *Engine) CurrentGcState() (s *GcState) {
123-
e.gcStateMutex.Lock()
124-
defer e.gcStateMutex.Unlock()
126+
e.mutex.Lock()
127+
defer e.mutex.Unlock()
125128
s = new(GcState)
126129
proto.Merge(s, e.gcState)
127130
return
128131
}
129132

130133
func (e *Engine) UpdateGcState(fn func(gcState *GcState)) {
131-
e.gcStateMutex.Lock()
132-
defer e.gcStateMutex.Unlock()
134+
e.mutex.Lock()
135+
defer e.mutex.Unlock()
133136
fn(e.gcState)
134137
}
135138

136139
// LatestChangesUntil returns all changes with a id larger than the given id
137140
func (e *Engine) LatestChangesUntil(id int32) (changes []*statemachine.StateChange) {
141+
e.mutex.Lock()
142+
defer e.mutex.Unlock()
138143
for _, change := range e.stateStore.Entries() {
139144
if *change.Id > id {
140145
changes = append(changes, change)
@@ -145,6 +150,8 @@ func (e *Engine) LatestChangesUntil(id int32) (changes []*statemachine.StateChan
145150

146151
// LatestChangeId returns the latest change id or -1, if there is no change
147152
func (e *Engine) LatestChangeId() int32 {
153+
e.mutex.Lock()
154+
defer e.mutex.Unlock()
148155
entries := e.stateStore.Entries()
149156
if len(entries) > 0 {
150157
return *entries[len(entries)-1].Id
@@ -169,8 +176,8 @@ func (e *Engine) processChanges() {
169176

170177
// ResetMatch creates a backup of the current state store, removes it and starts with a fresh state
171178
func (e *Engine) ResetMatch() {
172-
e.gcStateMutex.Lock()
173-
defer e.gcStateMutex.Unlock()
179+
e.mutex.Lock()
180+
defer e.mutex.Unlock()
174181
if err := e.stateStore.Reset(); err != nil {
175182
log.Printf("Could not reset store: %v", err)
176183
} else {
@@ -179,6 +186,8 @@ func (e *Engine) ResetMatch() {
179186
}
180187

181188
func (e *Engine) processChange(change *statemachine.Change) {
189+
e.mutex.Lock()
190+
defer e.mutex.Unlock()
182191

183192
var newChanges []*statemachine.Change
184193
entry := statemachine.StateChange{}
@@ -218,7 +227,7 @@ func (e *Engine) processChange(change *statemachine.Change) {
218227
entry.State, newChanges = e.stateMachine.Process(e.currentState, change)
219228
}
220229

221-
e.currentState = entry.State
230+
e.currentState = entry.State.Clone()
222231

223232
e.postProcessChange(entry)
224233

@@ -229,8 +238,12 @@ func (e *Engine) processChange(change *statemachine.Change) {
229238
if err := e.stateStore.Add(&entry); err != nil {
230239
log.Println("Could not add new state to store: ", err)
231240
}
241+
stateCopy := e.currentState.Clone()
232242
for _, hook := range e.hooks {
233-
hook <- HookOut{Change: entry.Change, State: e.CurrentState()}
243+
select {
244+
case hook <- HookOut{Change: entry.Change, State: stateCopy}:
245+
default:
246+
}
234247
}
235248
}
236249

internal/app/engine/hook.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ type HookOut struct {
1212

1313
// RegisterHook registers given hook for post processing after each change
1414
func (e *Engine) RegisterHook(hook chan HookOut) {
15+
e.mutex.Lock()
16+
defer e.mutex.Unlock()
1517
e.hooks = append(e.hooks, hook)
1618
}
1719

1820
// UnregisterHook unregisters hooks that were registered before
1921
func (e *Engine) UnregisterHook(hook chan HookOut) bool {
22+
e.mutex.Lock()
23+
defer e.mutex.Unlock()
2024
for i, h := range e.hooks {
2125
if h == hook {
2226
e.hooks = append(e.hooks[:i], e.hooks[i+1:]...)

internal/app/engine/process_noprogress.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func (d *NoProgressDetector) process() {
4646
duration := float32(timeSinceLastProgress.Seconds())
4747
location := d.gcEngine.gcState.TrackerStateGc.Ball.Pos.ToVector2()
4848
for _, team := range state.BothTeams() {
49-
defenseArea := geom.NewDefenseArea(d.gcEngine.GetGeometry(), *d.gcEngine.currentState.TeamState[team.String()].OnPositiveHalf)
49+
defenseArea := geom.NewDefenseArea(d.gcEngine.getGeometry(), *d.gcEngine.currentState.TeamState[team.String()].OnPositiveHalf)
5050
if defenseArea.IsPointInside(d.gcEngine.gcState.TrackerStateGc.Ball.Pos.ToVector2()) {
5151
d.gcEngine.Enqueue(createGameEventChange(state.GameEvent_KEEPER_HELD_BALL, state.GameEvent{
5252
Event: &state.GameEvent_KeeperHeldBall_{

internal/app/engine/process_prepare.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (e *Engine) robotPos(robotId *state.RobotId) *geom.Vector2 {
105105
func (e *Engine) posInsideGoal(pos *geom.Vector2) bool {
106106
forTeam := *e.currentState.Command.ForTeam
107107
teamInfo := e.currentState.TeamState[forTeam.String()]
108-
goalCenter := geom.GoalCenter(e.GetGeometry(), *teamInfo.OnPositiveHalf)
109-
goalArea := geom.NewRectangleFromCenter(goalCenter, robotRadius*2, e.GetGeometry().GoalWidth)
108+
goalCenter := geom.GoalCenter(e.getGeometry(), *teamInfo.OnPositiveHalf)
109+
goalArea := geom.NewRectangleFromCenter(goalCenter, robotRadius*2, e.getGeometry().GoalWidth)
110110
return goalArea.IsPointInside(pos)
111111
}

internal/app/engine/process_runningtostop.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ func (e *Engine) ballPlacementRequired() bool {
4444
}
4545

4646
// The ball is inside the field.
47-
field := geom.NewRectangleFromCenter(geom.NewVector2(0, 0), e.GetGeometry().FieldLength, e.GetGeometry().FieldWidth)
47+
field := geom.NewRectangleFromCenter(geom.NewVector2(0, 0), e.getGeometry().FieldLength, e.getGeometry().FieldWidth)
4848
if !field.IsPointInside(ballPos) {
4949
return true
5050
}
5151

5252
// The ball is at least 0.7m away from any defense area.
5353
for _, sign := range []float64{1, -1} {
54-
defenseArea := geom.NewDefenseAreaBySign(e.GetGeometry(), sign)
54+
defenseArea := geom.NewDefenseAreaBySign(e.getGeometry(), sign)
5555
forbiddenArea := defenseArea.WithMargin(e.gameConfig.BallPlacementMinDistanceToDefenseArea)
5656
if forbiddenArea.IsPointInside(ballPos) {
5757
return true

internal/app/engine/process_tick.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99

1010
// processTick updates the timers of the state and triggers changes if required
1111
func (e *Engine) processTick() {
12+
e.mutex.Lock()
13+
defer e.mutex.Unlock()
14+
1215
currentTime := e.timeProvider()
1316
delta := currentTime.Sub(e.lastTimeUpdate)
1417
e.lastTimeUpdate = currentTime
@@ -40,8 +43,12 @@ func (e *Engine) processTick() {
4043
e.processPrepare()
4144
e.processBotNumber()
4245

46+
stateCopy := e.currentState.Clone()
4347
for _, hook := range e.hooks {
44-
hook <- HookOut{State: e.CurrentState()}
48+
select {
49+
case hook <- HookOut{State: stateCopy}:
50+
default:
51+
}
4552
}
4653
}
4754

internal/app/gc/gc.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ func NewGameController(cfg config.Controller) (c *GameController) {
5151

5252
// Start starts all go routines
5353
func (c *GameController) Start() {
54-
if err := c.gcEngine.Start(); err != nil {
55-
panic(err)
56-
}
5754

5855
switch c.config.TimeAcquisitionMode {
5956
case config.TimeAcquisitionModeSystem:
@@ -80,6 +77,10 @@ func (c *GameController) Start() {
8077
c.autoRefServerTls.Server.Start()
8178
c.teamServer.Server.Start()
8279
c.teamServerTls.Server.Start()
80+
81+
if err := c.gcEngine.Start(); err != nil {
82+
panic(err)
83+
}
8384
}
8485

8586
// Stop stops all go routines

0 commit comments

Comments
 (0)