swarm/storage, swarm/network: Fix race condition

There was a race condition in writing/reading chunk.SData between
delivery.processReceivedChunks and dpa.retrieveWorker when the chunk was
still fetching
This commit is contained in:
Balint Gabor 2018-01-22 15:23:41 +01:00
parent 111b53d8bf
commit 295512f5a8
9 changed files with 42 additions and 25 deletions

View file

@ -18,6 +18,7 @@ package stream
import (
"errors"
"fmt"
"time"
"github.com/ethereum/go-ethereum/log"
@ -167,12 +168,11 @@ R:
for req := range d.receiveC {
// this should be has locally
chunk, err := d.db.Get(req.Key)
if err != nil {
log.Error("not in db? ", "key", req.Key, "chunk", chunk)
if err == nil {
continue R
}
if chunk.ReqC == nil {
continue R
if err != storage.ErrFetching {
panic(fmt.Sprintf("not in db? key %v chunk %v", req.Key, chunk))
}
select {
case <-chunk.ReqC:

View file

@ -20,16 +20,28 @@ import (
"bytes"
"crypto/rand"
"encoding/binary"
"flag"
"fmt"
"hash"
"io"
"os"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/log"
)
var (
loglevel = flag.Int("loglevel", 2, "verbosity of logs")
)
func init() {
flag.Parse()
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
}
type brokenLimitedReader struct {
lr io.Reader
errAt int

View file

@ -699,7 +699,7 @@ func (s *DbStore) get(key Key) (chunk *Chunk, err error) {
decodeData(data, chunk)
} else {
err = notFound
err = ErrNotFound
}
return
@ -712,8 +712,8 @@ func newMockGetDataFunc(mockStore *mock.NodeStore) func(key Key) (data []byte, e
return func(key Key) (data []byte, err error) {
data, err = mockStore.Get(key)
if err == mock.ErrNotFound {
// preserve notFound error
err = notFound
// preserve ErrNotFound error
err = ErrNotFound
}
return data, err
}

View file

@ -142,8 +142,8 @@ func testDbStoreNotFound(t *testing.T, mock bool) {
defer db.close()
_, err = db.Get(ZeroKey)
if err != notFound {
t.Errorf("Expected notFound, got %v", err)
if err != ErrNotFound {
t.Errorf("Expected ErrNotFound, got %v", err)
}
}

View file

@ -48,8 +48,8 @@ const (
)
var (
notFound = errors.New("not found")
ErrNotFound = errors.New("not found")
ErrFetching = errors.New("chunk still fetching")
// timeout interval before retrieval is timed out
searchTimeout = 3 * time.Second
)

View file

@ -106,7 +106,15 @@ func (self *LocalStore) Put(chunk *Chunk) {
// ChunkStores are remote and can have long latency
func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) {
chunk, err = self.memStore.Get(key)
if err == nil {
if chunk.ReqC != nil {
select {
case <-chunk.ReqC:
default:
return chunk, ErrFetching
}
}
return
}
chunk, err = self.DbStore.Get(key)
@ -123,12 +131,11 @@ func (self *LocalStore) GetOrCreateRequest(key Key) (chunk *Chunk, created bool)
var err error
chunk, err = self.Get(key)
if err == nil {
if chunk.ReqC == nil {
log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v found locally", key))
} else {
log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v hit on an existing request", key))
// no need to launch again
return chunk, false
}
if err == ErrFetching {
log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v hit on an existing request %v", key, chunk.ReqC))
return chunk, false
}
// no data and no request status

View file

@ -214,7 +214,7 @@ func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) {
l := hash.bits(bitpos, node.bits)
st := node.subtree[l]
if st == nil {
return nil, notFound
return nil, ErrNotFound
}
bitpos += node.bits
node = st
@ -232,7 +232,7 @@ func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) {
}
}
} else {
err = notFound
err = ErrNotFound
}
return

View file

@ -63,8 +63,8 @@ func TestMemStoreNotFound(t *testing.T) {
defer m.Close()
_, err := m.Get(ZeroKey)
if err != notFound {
t.Errorf("Expected notFound, got %v", err)
if err != ErrNotFound {
t.Errorf("Expected ErrNotFound, got %v", err)
}
}

View file

@ -17,7 +17,6 @@
package storage
import (
"encoding/binary"
"time"
)
@ -40,7 +39,7 @@ func (self *NetStore) Get(key Key) (chunk *Chunk, err error) {
var created bool
chunk, created = self.localStore.GetOrCreateRequest(key)
if chunk.ReqC == nil {
return
return chunk, nil
}
if created {
@ -53,10 +52,9 @@ func (self *NetStore) Get(key Key) (chunk *Chunk, err error) {
select {
case <-t.C:
return nil, notFound
return nil, ErrNotFound
case <-chunk.ReqC:
}
chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
return chunk, nil
}