type piecePerResourceTorrentImpl struct {
piecePerResource
+ locks []sync.RWMutex
}
func (piecePerResourceTorrentImpl) Close() error {
}
func (s piecePerResource) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
- return piecePerResourceTorrentImpl{s}, nil
+ return piecePerResourceTorrentImpl{
+ s,
+ make([]sync.RWMutex, info.NumPieces()),
+ }, nil
}
-func (s piecePerResource) Piece(p metainfo.Piece) PieceImpl {
+func (s piecePerResourceTorrentImpl) Piece(p metainfo.Piece) PieceImpl {
return piecePerResourcePiece{
mp: p,
- piecePerResource: s,
+ piecePerResource: s.piecePerResource,
+ mu: &s.locks[p.Index()],
}
}
type piecePerResourcePiece struct {
mp metainfo.Piece
piecePerResource
+ // This protects operations that move complete/incomplete pieces around, which can trigger read
+ // errors that may cause callers to do more drastic things.
+ mu *sync.RWMutex
}
var _ io.WriterTo = piecePerResourcePiece{}
func (s piecePerResourcePiece) WriteTo(w io.Writer) (int64, error) {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
if s.mustIsComplete() {
r, err := s.completed().Get()
if err != nil {
}
func (s piecePerResourcePiece) Completion() Completion {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
fi, err := s.completed().Stat()
return Completion{
Complete: err == nil && fi.Size() == s.mp.Length(),
}
func (s piecePerResourcePiece) MarkComplete() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
incompleteChunks := s.getChunks()
r, err := func() (io.ReadCloser, error) {
if ccr, ok := s.rp.(ConsecutiveChunkReader); ok {
}
func (s piecePerResourcePiece) MarkNotComplete() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
return s.completed().Delete()
}
func (s piecePerResourcePiece) ReadAt(b []byte, off int64) (int, error) {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
if s.mustIsComplete() {
return s.completed().ReadAt(b, off)
}
}
func (s piecePerResourcePiece) WriteAt(b []byte, off int64) (n int, err error) {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), strconv.FormatInt(off, 10)))
if err != nil {
panic(err)