]> Sergey Matveev's repositories - btrtrc.git/blobdiff - fs/file_handle.go
fs: Use a new torrent file reader per handled request
[btrtrc.git] / fs / file_handle.go
index 11a5d74d2a10cb524fb08a2c567e45ca6162fc21..2a8fe2ccad5f6a96cefa24b5db687bec26a520e1 100644 (file)
@@ -2,42 +2,85 @@ package torrentfs
 
 import (
        "context"
-       "fmt"
+       "io"
 
-       "bazil.org/fuse"
-       "bazil.org/fuse/fs"
+       "github.com/anacrolix/fuse"
+       "github.com/anacrolix/fuse/fs"
+       "github.com/anacrolix/missinggo/v2"
+
+       "github.com/anacrolix/torrent"
 )
 
 type fileHandle struct {
        fn fileNode
+       tf *torrent.File
 }
 
-var _ fs.HandleReader = fileHandle{}
+var _ interface {
+       fs.HandleReader
+       fs.HandleReleaser
+} = fileHandle{}
 
 func (me fileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
        torrentfsReadRequests.Add(1)
        if req.Dir {
                panic("read on directory")
        }
-       size := req.Size
-       fileLeft := int64(me.fn.size) - req.Offset
-       if fileLeft < 0 {
-               fileLeft = 0
-       }
-       if fileLeft < int64(size) {
-               size = int(fileLeft)
-       }
-       resp.Data = resp.Data[:size]
-       if len(resp.Data) == 0 {
-               return nil
-       }
-       torrentOff := me.fn.TorrentOffset + req.Offset
-       n, err := readFull(ctx, me.fn.FS, me.fn.t, torrentOff, resp.Data)
+       r := me.tf.NewReader()
+       defer r.Close()
+       pos, err := r.Seek(req.Offset, io.SeekStart)
        if err != nil {
-               return err
+               panic(err)
        }
-       if n != size {
-               panic(fmt.Sprintf("%d < %d", n, size))
+       if pos != req.Offset {
+               panic("seek failed")
        }
+       resp.Data = resp.Data[:req.Size]
+       readDone := make(chan struct{})
+       ctx, cancel := context.WithCancel(ctx)
+       var readErr error
+       go func() {
+               defer close(readDone)
+               me.fn.FS.mu.Lock()
+               me.fn.FS.blockedReads++
+               me.fn.FS.event.Broadcast()
+               me.fn.FS.mu.Unlock()
+               var n int
+               r := missinggo.ContextedReader{r, ctx}
+               // log.Printf("reading %v bytes at %v", len(resp.Data), req.Offset)
+               if true {
+                       // A user reported on that on freebsd 12.2, the system requires that reads are
+                       // completely filled. Their system only asks for 64KiB at a time. I've seen systems that
+                       // can demand up to 16MiB at a time, so this gets tricky. For now, I'll restore the old
+                       // behaviour from before 2a7352a, which nobody reported problems with.
+                       n, readErr = io.ReadFull(r, resp.Data)
+               } else {
+                       n, readErr = r.Read(resp.Data)
+                       if readErr == io.EOF {
+                               readErr = nil
+                       }
+               }
+               resp.Data = resp.Data[:n]
+       }()
+       defer func() {
+               <-readDone
+               me.fn.FS.mu.Lock()
+               me.fn.FS.blockedReads--
+               me.fn.FS.event.Broadcast()
+               me.fn.FS.mu.Unlock()
+       }()
+       defer cancel()
+
+       select {
+       case <-readDone:
+               return readErr
+       case <-me.fn.FS.destroyed:
+               return fuse.EIO
+       case <-ctx.Done():
+               return fuse.EINTR
+       }
+}
+
+func (me fileHandle) Release(context.Context, *fuse.ReleaseRequest) error {
        return nil
 }