]> Sergey Matveev's repositories - btrtrc.git/blobdiff - storage/mmap.go
go1.19 compat
[btrtrc.git] / storage / mmap.go
index 1b1423f7a9e73904e4a7e9fcd44cb462ef9f2878..1851c3238d2618cdc1c43789dff6189013ae2908 100644 (file)
@@ -41,7 +41,7 @@ func (s *mmapClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash
                span:     span,
                pc:       s.pc,
        }
-       return TorrentImpl{Piece: t.Piece, Close: t.Close}, err
+       return TorrentImpl{Piece: t.Piece, Close: t.Close, Flush: t.Flush}, err
 }
 
 func (s *mmapClientImpl) Close() error {
@@ -72,6 +72,14 @@ func (ts *mmapTorrentStorage) Close() error {
        return nil
 }
 
+func (ts *mmapTorrentStorage) Flush() error {
+       errs := ts.span.Flush()
+       if len(errs) > 0 {
+               return errs[0]
+       }
+       return nil
+}
+
 type mmapStoragePiece struct {
        pc PieceCompletionGetSetter
        p  metainfo.Piece
@@ -116,33 +124,35 @@ func mMapTorrent(md *metainfo.Info, location string) (mms *mmap_span.MMapSpan, e
                        return
                }
                fileName := filepath.Join(location, safeName)
-               var mm mmap.MMap
+               var mm FileMapping
                mm, err = mmapFile(fileName, miFile.Length)
                if err != nil {
                        err = fmt.Errorf("file %q: %s", miFile.DisplayPath(md), err)
                        return
                }
-               if mm != nil {
-                       mms.Append(mm)
-               }
+               mms.Append(mm)
        }
        mms.InitIndex()
        return
 }
 
-func mmapFile(name string, size int64) (ret mmap.MMap, err error) {
+func mmapFile(name string, size int64) (_ FileMapping, err error) {
        dir := filepath.Dir(name)
-       err = os.MkdirAll(dir, 0750)
+       err = os.MkdirAll(dir, 0o750)
        if err != nil {
                err = fmt.Errorf("making directory %q: %s", dir, err)
                return
        }
        var file *os.File
-       file, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0666)
+       file, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0o666)
        if err != nil {
                return
        }
-       defer file.Close()
+       defer func() {
+               if err != nil {
+                       file.Close()
+               }
+       }()
        var fi os.FileInfo
        fi, err = file.Stat()
        if err != nil {
@@ -156,22 +166,65 @@ func mmapFile(name string, size int64) (ret mmap.MMap, err error) {
                        return
                }
        }
-       if size == 0 {
-               // Can't mmap() regions with length 0.
-               return
-       }
-       intLen := int(size)
-       if int64(intLen) != size {
-               err = errors.New("size too large for system")
+       return func() (ret mmapWithFile, err error) {
+               ret.f = file
+               if size == 0 {
+                       // Can't mmap() regions with length 0.
+                       return
+               }
+               intLen := int(size)
+               if int64(intLen) != size {
+                       err = errors.New("size too large for system")
+                       return
+               }
+               ret.mmap, err = mmap.MapRegion(file, intLen, mmap.RDWR, 0, 0)
+               if err != nil {
+                       err = fmt.Errorf("error mapping region: %s", err)
+                       return
+               }
+               if int64(len(ret.mmap)) != size {
+                       panic(len(ret.mmap))
+               }
                return
+       }()
+}
+
+// Combines a mmapped region and file into a storage Mmap abstraction, which handles closing the
+// mmap file handle.
+func WrapFileMapping(region mmap.MMap, file *os.File) FileMapping {
+       return mmapWithFile{
+               f:    file,
+               mmap: region,
        }
-       ret, err = mmap.MapRegion(file, intLen, mmap.RDWR, 0, 0)
-       if err != nil {
-               err = fmt.Errorf("error mapping region: %s", err)
-               return
+}
+
+type FileMapping = mmap_span.Mmap
+
+// Handles closing the mmap's file handle (needed for Windows). Could be implemented differently by
+// OS.
+type mmapWithFile struct {
+       f    *os.File
+       mmap mmap.MMap
+}
+
+func (m mmapWithFile) Flush() error {
+       return m.mmap.Flush()
+}
+
+func (m mmapWithFile) Unmap() (err error) {
+       if m.mmap != nil {
+               err = m.mmap.Unmap()
        }
-       if int64(len(ret)) != size {
-               panic(len(ret))
+       fileErr := m.f.Close()
+       if err == nil {
+               err = fileErr
        }
        return
 }
+
+func (m mmapWithFile) Bytes() []byte {
+       if m.mmap == nil {
+               return nil
+       }
+       return m.mmap
+}