go-aptproxy/cache/livereader.go

103 lines
2.3 KiB
Go
Raw Normal View History

2016-05-01 06:00:43 +02:00
package cache
import (
"github.com/fsnotify/fsnotify"
"io"
"os"
)
// liveReader reads a file from disk, synchronizing reads with a downloader.
type liveReader struct {
2016-05-01 07:32:35 +02:00
downloader *downloader
dataFilename string
file *os.File
entry *Entry
done chan error
err error
eof bool
2016-05-01 06:00:43 +02:00
}
// newLiveReader creates a reader from the provided downloader and data
// file. fsnotify is used to watch for writes to the file to avoid using a
// spinloop. Invoking this function assumes the existence of the data file.
func newLiveReader(d *downloader, dataFilename string) (*liveReader, error) {
l := &liveReader{
2016-05-01 07:32:35 +02:00
downloader: d,
dataFilename: dataFilename,
done: make(chan error),
2016-05-01 06:00:43 +02:00
}
go func() {
defer close(l.done)
l.done <- d.WaitForDone()
}()
2016-05-01 07:32:35 +02:00
return l, nil
2016-05-01 06:00:43 +02:00
}
// Read attempts to read as much data as possible into the provided buffer.
// Since data is being downloaded as data is being read, fsnotify is used to
// monitor writes to the file. This function blocks until the requested amount
// of data is read, an error occurs, or EOF is encountered.
func (l *liveReader) Read(p []byte) (int, error) {
if l.err != nil {
return 0, l.err
}
2016-05-01 07:32:35 +02:00
if l.file == nil {
f, err := os.Open(l.dataFilename)
if err != nil {
l.err = err
return 0, l.err
}
l.file = f
}
2016-05-01 06:00:43 +02:00
bytesRead := 0
2016-05-01 07:32:35 +02:00
watcher, err := fsnotify.NewWatcher()
if err != nil {
l.err = err
return 0, l.err
}
defer watcher.Close()
if err := watcher.Add(l.dataFilename); err != nil {
l.err = err
return 0, l.err
}
2016-05-01 06:00:43 +02:00
loop:
for bytesRead < len(p) {
n, err := l.file.Read(p[bytesRead:])
bytesRead += n
if err != nil {
if err != io.EOF || l.eof {
l.err = err
break loop
}
for {
select {
2016-05-01 07:32:35 +02:00
case e := <-watcher.Events:
2016-05-01 06:00:43 +02:00
if e.Op&fsnotify.Write != fsnotify.Write {
continue
}
case err = <-l.done:
l.err = err
l.eof = true
}
continue loop
}
}
}
return bytesRead, l.err
}
// Close attempts to close the data file (if opened).
func (l *liveReader) Close() error {
2016-05-01 07:32:35 +02:00
if l.file != nil {
return l.file.Close()
}
return nil
2016-05-01 06:00:43 +02:00
}
// GetEntry returns the Entry associated with the file, blocking until either
// the data is available or an error occurs.
func (l *liveReader) GetEntry() (*Entry, error) {
return l.downloader.GetEntry()
}