Skip to content

Commit 2363e81

Browse files
authored
core: add Parser.Close() to fix memory leak (#216) [backport] (#224)
Parser.Close() will call msgDispatcher.RemoveAllQueues() to terminate the goroutine handling net-messages. It needs to be called by the user before discarding the Parser.
1 parent 239ae3e commit 2363e81

File tree

12 files changed

+53
-1
lines changed

12 files changed

+53
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func main() {
5252
defer f.Close()
5353

5454
p := dem.NewParser(f)
55+
defer p.Close()
5556

5657
// Register handler on kill events
5758
p.RegisterEventHandler(func(e events.Kill) {

examples/entities/entities.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func main() {
1818
defer f.Close()
1919

2020
p := dem.NewParser(f)
21+
defer p.Close()
2122

2223
p.RegisterEventHandler(func(events.DataTablesParsed) {
2324
p.ServerClasses().FindByName("CWeaponAWP").OnEntityCreated(func(ent *st.Entity) {

examples/heatmap/heatmap.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func main() {
3434
defer f.Close()
3535

3636
p := dem.NewParser(f)
37+
defer p.Close()
3738

3839
// Parse header (contains map-name etc.)
3940
header, err := p.ParseHeader()

examples/nade-trajectories/nade_trajectories.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func main() {
4646
defer f.Close()
4747

4848
p := dem.NewParser(f)
49+
defer p.Close()
4950

5051
header, err := p.ParseHeader()
5152
checkError(err)

examples/net-messages/netmessages.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func main() {
2626
}
2727

2828
p := dem.NewParserWithConfig(f, cfg)
29+
defer p.Close()
2930

3031
// Register handler for BSPDecal messages
3132
p.RegisterNetMessageHandler(func(m *msg.CSVCMsg_BSPDecal) {

examples/print-events/print_events.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import (
1313
// Run like this: go run print_events.go -demo /path/to/demo.dem
1414
func main() {
1515
f, err := os.Open(ex.DemoPathFromArgs())
16-
defer f.Close()
1716
checkError(err)
1817

18+
defer f.Close()
19+
1920
p := dem.NewParser(f)
21+
defer p.Close()
2022

2123
// Parse header
2224
header, err := p.ParseHeader()

examples_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,24 @@ func ExampleParser() {
1818
if err != nil {
1919
panic(err)
2020
}
21+
2122
defer f.Close()
2223

2324
p := dem.NewParser(f)
25+
defer p.Close()
2426

2527
// Register handler on kill events
2628
p.RegisterEventHandler(func(e events.Kill) {
2729
var hs string
2830
if e.IsHeadshot {
2931
hs = " (HS)"
3032
}
33+
3134
var wallBang string
3235
if e.PenetratedObjects > 0 {
3336
wallBang = " (WB)"
3437
}
38+
3539
fmt.Printf("%s <%v%s%s> %s\n", e.Killer, e.Weapon, hs, wallBang, e.Victim)
3640
})
3741

@@ -46,5 +50,6 @@ func TestExamplesWithoutOutput(t *testing.T) {
4650
if testing.Short() {
4751
t.Skip("skipping long running test")
4852
}
53+
4954
ExampleParser()
5055
}

fake/parser.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,9 @@ func max(numbers map[int][]interface{}) (maxNumber int) {
227227
func (p *Parser) Cancel() {
228228
p.Called()
229229
}
230+
231+
// Close is a mock-implementation of Parser.Close().
232+
// NOP implementation.
233+
func (p *Parser) Close() {
234+
p.Called()
235+
}

parser.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Example (without error handling):
3131
3232
f, _ := os.Open("/path/to/demo.dem")
3333
p := dem.NewParser(f)
34+
defer p.Close()
3435
header := p.ParseHeader()
3536
fmt.Println("Map:", header.MapName)
3637
p.RegisterEventHandler(func(e events.BombExplode) {
@@ -203,6 +204,12 @@ func (p *Parser) UnregisterNetMessageHandler(identifier dp.HandlerIdentifier) {
203204
p.msgDispatcher.UnregisterHandler(identifier)
204205
}
205206

207+
// Close closes any open resources used by the Parser (go routines, file handles).
208+
// This must be called before discarding the Parser to avoid memory leaks.
209+
func (p *Parser) Close() {
210+
p.msgDispatcher.RemoveAllQueues()
211+
}
212+
206213
func (p *Parser) error() (err error) {
207214
p.errLock.Lock()
208215
err = p.err

parser_interface.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
//
2525
// f, _ := os.Open("/path/to/demo.dem")
2626
// p := dem.NewParser(f)
27+
// defer p.Close()
2728
// header := p.ParseHeader()
2829
// fmt.Println("Map:", header.MapName)
2930
// p.RegisterEventHandler(func(e events.BombExplode) {
@@ -92,6 +93,9 @@ type IParser interface {
9293
//
9394
// The identifier is returned at registration by RegisterNetMessageHandler().
9495
UnregisterNetMessageHandler(identifier dp.HandlerIdentifier)
96+
// Close closes any open resources used by the Parser (go routines, file handles).
97+
// This must be called before discarding the Parser to avoid memory leaks.
98+
Close()
9599
// ParseHeader attempts to parse the header of the demo and returns it.
96100
// If not done manually this will be called by Parser.ParseNextFrame() or Parser.ParseToEnd().
97101
//

0 commit comments

Comments
 (0)