Skip to content

Commit be9d365

Browse files
committed
Replace SetFinalizer with AddCleanup for Go 1.24+
Switch to runtime.AddCleanup for more reliable resource cleanup. This provides better finalization behavior compared to SetFinalizer. Uses atomic operations to prevent race conditions during cleanup.
1 parent 476083a commit be9d365

File tree

4 files changed

+31
-14
lines changed

4 files changed

+31
-14
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changes
22

3+
## 2.0.0-beta.10
4+
5+
- Replaced `runtime.SetFinalizer` with `runtime.AddCleanup` for resource
6+
cleanup in Go 1.24+. This provides more reliable finalization behavior and
7+
better garbage collection performance.
8+
39
## 2.0.0-beta.9 - 2025-08-23
410

511
- **SECURITY**: Fixed integer overflow vulnerability in search tree size

reader.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ import (
112112
"net/netip"
113113
"os"
114114
"runtime"
115+
"sync/atomic"
115116
"time"
116117

117118
"github.com/oschwald/maxminddb-golang/v2/internal/decoder"
@@ -122,6 +123,12 @@ const dataSectionSeparatorSize = 16
122123

123124
var metadataStartMarker = []byte("\xAB\xCD\xEFMaxMind.com")
124125

126+
// mmapCleanup holds the data needed to safely cleanup memory-mapped files
127+
type mmapCleanup struct {
128+
data []byte
129+
hasMapped *atomic.Bool
130+
}
131+
125132
// Reader holds the data corresponding to the MaxMind DB file. Its only public
126133
// field is Metadata, which contains the metadata from the MaxMind DB file.
127134
//
@@ -134,7 +141,7 @@ type Reader struct {
134141
ipv4Start uint
135142
ipv4StartBitDepth int
136143
nodeOffsetMult uint
137-
hasMappedFile bool
144+
hasMappedFile atomic.Bool
138145
}
139146

140147
// Metadata holds the metadata decoded from the MaxMind DB file.
@@ -252,8 +259,16 @@ func Open(file string, options ...ReaderOption) (*Reader, error) {
252259
return nil, err
253260
}
254261

255-
reader.hasMappedFile = true
256-
runtime.SetFinalizer(reader, (*Reader).Close)
262+
reader.hasMappedFile.Store(true)
263+
cleanup := &mmapCleanup{
264+
data: data,
265+
hasMapped: &reader.hasMappedFile,
266+
}
267+
runtime.AddCleanup(reader, func(mc *mmapCleanup) {
268+
if mc.hasMapped.CompareAndSwap(true, false) {
269+
_ = munmap(mc.data)
270+
}
271+
}, cleanup)
257272
return reader, nil
258273
}
259274

@@ -281,9 +296,7 @@ func openFallback(f *os.File, size int) (data []byte, err error) {
281296
// Close returns the resources used by the database to the system.
282297
func (r *Reader) Close() error {
283298
var err error
284-
if r.hasMappedFile {
285-
runtime.SetFinalizer(r, nil)
286-
r.hasMappedFile = false
299+
if r.hasMappedFile.CompareAndSwap(true, false) {
287300
err = munmap(r.buffer)
288301
}
289302
r.buffer = nil
@@ -384,7 +397,7 @@ func (r *Reader) Lookup(ip netip.Addr) Result {
384397
}
385398
offset, err := r.resolveDataPointer(pointer)
386399
return Result{
387-
decoder: r.decoder,
400+
reader: r,
388401
ip: ip,
389402
offset: uint(offset),
390403
prefixLen: uint8(prefixLen),
@@ -399,7 +412,7 @@ func (r *Reader) LookupOffset(offset uintptr) Result {
399412
return Result{err: errors.New("cannot call LookupOffset on a closed database")}
400413
}
401414

402-
return Result{decoder: r.decoder, offset: uint(offset)}
415+
return Result{reader: r, offset: uint(offset)}
403416
}
404417

405418
func (r *Reader) setIPv4Start() error {

result.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package maxminddb
33
import (
44
"math"
55
"net/netip"
6-
7-
"github.com/oschwald/maxminddb-golang/v2/internal/decoder"
86
)
97

108
const notFound uint = math.MaxUint
@@ -13,7 +11,7 @@ const notFound uint = math.MaxUint
1311
type Result struct {
1412
ip netip.Addr
1513
err error
16-
decoder decoder.ReflectionDecoder
14+
reader *Reader
1715
offset uint
1816
prefixLen uint8
1917
}
@@ -37,7 +35,7 @@ func (r Result) Decode(v any) error {
3735
return nil
3836
}
3937

40-
return r.decoder.Decode(r.offset, v)
38+
return r.reader.decoder.Decode(r.offset, v)
4139
}
4240

4341
// DecodePath unmarshals a value from data section into v, following the
@@ -94,7 +92,7 @@ func (r Result) DecodePath(v any, path ...any) error {
9492
if r.offset == notFound {
9593
return nil
9694
}
97-
return r.decoder.DecodePath(r.offset, path, v)
95+
return r.reader.decoder.DecodePath(r.offset, path, v)
9896
}
9997

10098
// Err provides a way to check whether there was an error during the lookup

traverse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func (r *Reader) NetworksWithin(prefix netip.Prefix, options ...NetworksOption)
192192
}
193193

194194
ok := yield(Result{
195-
decoder: r.decoder,
195+
reader: r,
196196
ip: mappedIP(node.ip),
197197
offset: uint(offset),
198198
prefixLen: uint8(node.bit),

0 commit comments

Comments
 (0)