github.com/bhojpur/cache@v0.0.4/pkg/memory/storage_unix.go (about)

     1  //go:build !windows && !plan9 && !solaris
     2  // +build !windows,!plan9,!solaris
     3  
     4  package memory
     5  
     6  // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved.
     7  
     8  // Permission is hereby granted, free of charge, to any person obtaining a copy
     9  // of this software and associated documentation files (the "Software"), to deal
    10  // in the Software without restriction, including without limitation the rights
    11  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    12  // copies of the Software, and to permit persons to whom the Software is
    13  // furnished to do so, subject to the following conditions:
    14  
    15  // The above copyright notice and this permission notice shall be included in
    16  // all copies or substantial portions of the Software.
    17  
    18  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    19  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    20  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    21  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    22  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    23  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    24  // THE SOFTWARE.
    25  
    26  import (
    27  	"fmt"
    28  	"syscall"
    29  	"time"
    30  	"unsafe"
    31  
    32  	"golang.org/x/sys/unix"
    33  )
    34  
    35  // flock acquires an advisory lock on a file descriptor.
    36  func flock(db *DB, exclusive bool, timeout time.Duration) error {
    37  	var t time.Time
    38  	if timeout != 0 {
    39  		t = time.Now()
    40  	}
    41  	fd := db.file.Fd()
    42  	flag := syscall.LOCK_NB
    43  	if exclusive {
    44  		flag |= syscall.LOCK_EX
    45  	} else {
    46  		flag |= syscall.LOCK_SH
    47  	}
    48  	for {
    49  		// Attempt to obtain an exclusive lock.
    50  		err := syscall.Flock(int(fd), flag)
    51  		if err == nil {
    52  			return nil
    53  		} else if err != syscall.EWOULDBLOCK {
    54  			return err
    55  		}
    56  
    57  		// If we timed out then return an error.
    58  		if timeout != 0 && time.Since(t) > timeout-flockRetryTimeout {
    59  			return ErrTimeout
    60  		}
    61  
    62  		// Wait for a bit and try again.
    63  		time.Sleep(flockRetryTimeout)
    64  	}
    65  }
    66  
    67  // funlock releases an advisory lock on a file descriptor.
    68  func funlock(db *DB) error {
    69  	return syscall.Flock(int(db.file.Fd()), syscall.LOCK_UN)
    70  }
    71  
    72  // mmap memory maps a DB's data file.
    73  func mmap(db *DB, sz int) error {
    74  	// Map the data file to memory.
    75  	b, err := unix.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED|db.MmapFlags)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	// Advise the kernel that the mmap is accessed randomly.
    81  	err = unix.Madvise(b, syscall.MADV_RANDOM)
    82  	if err != nil && err != syscall.ENOSYS {
    83  		// Ignore not implemented error in kernel because it still works.
    84  		return fmt.Errorf("madvise: %s", err)
    85  	}
    86  
    87  	// Save the original byte slice and convert to a byte array pointer.
    88  	db.dataref = b
    89  	db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0]))
    90  	db.datasz = sz
    91  	return nil
    92  }
    93  
    94  // munmap unmaps a DB's data file from memory.
    95  func munmap(db *DB) error {
    96  	// Ignore the unmap if we have no mapped data.
    97  	if db.dataref == nil {
    98  		return nil
    99  	}
   100  
   101  	// Unmap using the original byte slice.
   102  	err := unix.Munmap(db.dataref)
   103  	db.dataref = nil
   104  	db.data = nil
   105  	db.datasz = 0
   106  	return err
   107  }