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 }