@@ -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
123124var metadataStartMarker = []byte ("\xAB \xCD \xEF MaxMind.com" )
124125
126+ // mmapCleanup holds the data needed to safely cleanup memory-mapped files.
127+ type mmapCleanup struct {
128+ hasMapped * atomic.Bool
129+ data []byte
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.
282297func (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
405418func (r * Reader ) setIPv4Start () error {
0 commit comments