github.com/fengyoulin/shm@v0.0.0-20200305015033-287e184bdf0a/database/file.go (about)

     1  package database
     2  
     3  import (
     4  	"errors"
     5  	"github.com/fengyoulin/shm/mapping"
     6  	"os"
     7  	"time"
     8  )
     9  
    10  // ErrTimeout when waiting for database init
    11  var ErrTimeout = errors.New("timeout when waiting for database init")
    12  
    13  // Open a database file, return a mapping
    14  func Open(path string, size int, wait time.Duration) (m *mapping.Mapping, unlock func() error, err error) {
    15  	var lock *os.File
    16  	name := path + ".lock"
    17  	for i := 0; i < int(wait/time.Millisecond/10); i++ {
    18  		lock, err = os.OpenFile(name, os.O_CREATE|os.O_EXCL, 0664)
    19  		if err == nil {
    20  			break
    21  		}
    22  		if !os.IsExist(err) {
    23  			return
    24  		}
    25  		time.Sleep(10 * time.Millisecond)
    26  	}
    27  	if err != nil {
    28  		err = ErrTimeout
    29  		return
    30  	}
    31  	uf := func() (er error) {
    32  		er = lock.Close()
    33  		if e := os.Remove(name); er == nil {
    34  			er = e
    35  		}
    36  		return
    37  	}
    38  	defer func() {
    39  		if uf == nil {
    40  			return
    41  		}
    42  		if e := uf(); err == nil {
    43  			err = e
    44  		}
    45  	}()
    46  	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0664)
    47  	if err != nil {
    48  		return
    49  	}
    50  	defer func() {
    51  		if e := f.Close(); err == nil {
    52  			err = e
    53  		}
    54  	}()
    55  	info, err := f.Stat()
    56  	if err != nil {
    57  		return
    58  	}
    59  	// created new file
    60  	if info.Size() == 0 {
    61  		var buf [4096]byte
    62  		for i := 0; i < size/4096; i++ {
    63  			_, err = f.Write(buf[:])
    64  			if err != nil {
    65  				return
    66  			}
    67  		}
    68  		if s := size % 4096; s > 0 {
    69  			_, err = f.Write(buf[:s])
    70  			if err != nil {
    71  				return
    72  			}
    73  		}
    74  	}
    75  	m, err = mapping.Create(f)
    76  	if err != nil {
    77  		return
    78  	}
    79  	unlock, uf = uf, nil
    80  	return
    81  }