1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build unix 6 7package bio 8 9import ( 10 "runtime" 11 "sync/atomic" 12 "syscall" 13) 14 15// mmapLimit is the maximum number of mmaped regions to create before 16// falling back to reading into a heap-allocated slice. This exists 17// because some operating systems place a limit on the number of 18// distinct mapped regions per process. As of this writing: 19// 20// Darwin unlimited 21// DragonFly 1000000 (vm.max_proc_mmap) 22// FreeBSD unlimited 23// Linux 65530 (vm.max_map_count) // TODO: query /proc/sys/vm/max_map_count? 24// NetBSD unlimited 25// OpenBSD unlimited 26var mmapLimit int32 = 1<<31 - 1 27 28func init() { 29 // Linux is the only practically concerning OS. 30 if runtime.GOOS == "linux" { 31 mmapLimit = 30000 32 } 33} 34 35func (r *Reader) sliceOS(length uint64) ([]byte, bool) { 36 // For small slices, don't bother with the overhead of a 37 // mapping, especially since we have no way to unmap it. 38 const threshold = 16 << 10 39 if length < threshold { 40 return nil, false 41 } 42 43 // Have we reached the mmap limit? 44 if atomic.AddInt32(&mmapLimit, -1) < 0 { 45 atomic.AddInt32(&mmapLimit, 1) 46 return nil, false 47 } 48 49 // Page-align the offset. 50 off := r.Offset() 51 align := syscall.Getpagesize() 52 aoff := off &^ int64(align-1) 53 54 data, err := syscall.Mmap(int(r.f.Fd()), aoff, int(length+uint64(off-aoff)), syscall.PROT_READ, syscall.MAP_SHARED|syscall.MAP_FILE) 55 if err != nil { 56 return nil, false 57 } 58 59 data = data[off-aoff:] 60 r.MustSeek(int64(length), 1) 61 return data, true 62} 63