Skip to content
This repository has been archived by the owner on Sep 11, 2020. It is now read-only.

Made idxfile safe for concurrent access so that users can run blame concurrently #1270

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 13 additions & 25 deletions plumbing/format/idxfile/idxfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"io"
"sort"
"sync"

encbin "encoding/binary"

Expand Down Expand Up @@ -56,7 +57,7 @@ type MemoryIndex struct {
PackfileChecksum [20]byte
IdxChecksum [20]byte

offsetHash map[int64]plumbing.Hash
offsetHash sync.Map
offsetHashIsFull bool
}

Expand Down Expand Up @@ -127,10 +128,7 @@ func (idx *MemoryIndex) FindOffset(h plumbing.Hash) (int64, error) {

if !idx.offsetHashIsFull {
// Save the offset for reverse lookup
if idx.offsetHash == nil {
idx.offsetHash = make(map[int64]plumbing.Hash)
}
idx.offsetHash[int64(offset)] = h
idx.offsetHash.Store(int64(offset), h)
}

return int64(offset), nil
Expand Down Expand Up @@ -169,39 +167,29 @@ func (idx *MemoryIndex) getCRC32(firstLevel, secondLevel int) uint32 {

// FindHash implements the Index interface.
func (idx *MemoryIndex) FindHash(o int64) (plumbing.Hash, error) {
var hash plumbing.Hash
var ok bool

if idx.offsetHash != nil {
if hash, ok = idx.offsetHash[o]; ok {
return hash, nil
}
if hash, ok := idx.offsetHash.Load(o); ok {
return hash.(plumbing.Hash), nil
}

// Lazily generate the reverse offset/hash map if required.
if !idx.offsetHashIsFull || idx.offsetHash == nil {
if !idx.offsetHashIsFull {
if err := idx.genOffsetHash(); err != nil {
return plumbing.ZeroHash, err
}

hash, ok = idx.offsetHash[o]
}

if !ok {
return plumbing.ZeroHash, plumbing.ErrObjectNotFound
if hash, ok := idx.offsetHash.Load(o); !ok {
return plumbing.ZeroHash, plumbing.ErrObjectNotFound
} else {
return hash.(plumbing.Hash), nil
}
}

return hash, nil
return plumbing.Hash{}, nil
}

// genOffsetHash generates the offset/hash mapping for reverse search.
func (idx *MemoryIndex) genOffsetHash() error {
count, err := idx.Count()
if err != nil {
return err
}

idx.offsetHash = make(map[int64]plumbing.Hash, count)
idx.offsetHashIsFull = true

var hash plumbing.Hash
Expand All @@ -211,7 +199,7 @@ func (idx *MemoryIndex) genOffsetHash() error {
for secondLevel := uint32(0); i < fanoutValue; i++ {
copy(hash[:], idx.Names[mappedFirstLevel][secondLevel*objectIDLength:])
offset := int64(idx.getOffset(mappedFirstLevel, int(secondLevel)))
idx.offsetHash[offset] = hash
idx.offsetHash.Store(offset, hash)
secondLevel++
}
}
Expand Down