mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
bitseq to provide atomic functions
- Also add validation for passed ordinal Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
ee31009744
commit
01d6585a31
3 changed files with 303 additions and 97 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/libnetwork/datastore"
|
"github.com/docker/libnetwork/datastore"
|
||||||
|
"github.com/docker/libnetwork/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// block sequence constants
|
// block sequence constants
|
||||||
|
@ -21,6 +22,10 @@ const (
|
||||||
invalidPos = blockMAX
|
invalidPos = blockMAX
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoBitAvailable = fmt.Errorf("no bit available")
|
||||||
|
)
|
||||||
|
|
||||||
// Handle contains the sequece representing the bitmask and its identifier
|
// Handle contains the sequece representing the bitmask and its identifier
|
||||||
type Handle struct {
|
type Handle struct {
|
||||||
bits uint32
|
bits uint32
|
||||||
|
@ -145,7 +150,7 @@ func (s *sequence) toByteArray() ([]byte, error) {
|
||||||
return bb, nil
|
return bb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromByteArray construct the sequence from the byte array
|
// fromByteArray construct the sequence from the byte array
|
||||||
func (s *sequence) fromByteArray(data []byte) error {
|
func (s *sequence) fromByteArray(data []byte) error {
|
||||||
l := len(data)
|
l := len(data)
|
||||||
if l%8 != 0 {
|
if l%8 != 0 {
|
||||||
|
@ -168,7 +173,21 @@ func (s *sequence) fromByteArray(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handle) getCopy() *Handle {
|
||||||
|
return &Handle{
|
||||||
|
bits: h.bits,
|
||||||
|
unselected: h.unselected,
|
||||||
|
head: h.head.getCopy(),
|
||||||
|
app: h.app,
|
||||||
|
id: h.id,
|
||||||
|
dbIndex: h.dbIndex,
|
||||||
|
dbExists: h.dbExists,
|
||||||
|
store: h.store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetFirstAvailable returns the byte and bit position of the first unset bit
|
// GetFirstAvailable returns the byte and bit position of the first unset bit
|
||||||
|
// @Deprecated Use SetAny() instead
|
||||||
func (h *Handle) GetFirstAvailable() (uint32, uint32, error) {
|
func (h *Handle) GetFirstAvailable() (uint32, uint32, error) {
|
||||||
h.Lock()
|
h.Lock()
|
||||||
defer h.Unlock()
|
defer h.Unlock()
|
||||||
|
@ -177,45 +196,157 @@ func (h *Handle) GetFirstAvailable() (uint32, uint32, error) {
|
||||||
|
|
||||||
// CheckIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
// CheckIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
||||||
// If the ordinal is beyond the sequence limits, a negative response is returned
|
// If the ordinal is beyond the sequence limits, a negative response is returned
|
||||||
|
// @Deprecated Use IsSet() instead
|
||||||
func (h *Handle) CheckIfAvailable(ordinal uint32) (uint32, uint32, error) {
|
func (h *Handle) CheckIfAvailable(ordinal uint32) (uint32, uint32, error) {
|
||||||
|
if err := h.validateOrdinal(ordinal); err != nil {
|
||||||
|
return invalidPos, invalidPos, err
|
||||||
|
}
|
||||||
h.Lock()
|
h.Lock()
|
||||||
defer h.Unlock()
|
defer h.Unlock()
|
||||||
return checkIfAvailable(h.head, ordinal)
|
return checkIfAvailable(h.head, ordinal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushReservation pushes the bit reservation inside the bitmask.
|
// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal
|
||||||
func (h *Handle) PushReservation(bytePos, bitPos uint32, release bool) error {
|
func (h *Handle) SetAny() (uint32, error) {
|
||||||
// Create a copy of the current handler
|
if h.Unselected() == 0 {
|
||||||
h.Lock()
|
return invalidPos, errNoBitAvailable
|
||||||
nh := &Handle{
|
|
||||||
app: h.app,
|
|
||||||
id: h.id,
|
|
||||||
store: h.store,
|
|
||||||
dbIndex: h.dbIndex,
|
|
||||||
head: h.head.GetCopy(),
|
|
||||||
dbExists: h.dbExists,
|
|
||||||
}
|
}
|
||||||
|
return h.set(0, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set atomically sets the corresponding bit in the sequence
|
||||||
|
func (h *Handle) Set(ordinal uint32) error {
|
||||||
|
if err := h.validateOrdinal(ordinal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := h.set(ordinal, false, false)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset atomically unsets the corresponding bit in the sequence
|
||||||
|
func (h *Handle) Unset(ordinal uint32) error {
|
||||||
|
if err := h.validateOrdinal(ordinal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := h.set(ordinal, false, true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSet atomically checks if the ordinal bit is set. In case ordinal
|
||||||
|
// is outside of the bit sequence limits, false is returned.
|
||||||
|
func (h *Handle) IsSet(ordinal uint32) bool {
|
||||||
|
if err := h.validateOrdinal(ordinal); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h.Lock()
|
||||||
|
_, _, err := checkIfAvailable(h.head, ordinal)
|
||||||
|
h.Unlock()
|
||||||
|
return err != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// set/reset the bit
|
||||||
|
func (h *Handle) set(ordinal uint32, any bool, release bool) (uint32, error) {
|
||||||
|
var (
|
||||||
|
bitPos uint32
|
||||||
|
bytePos uint32
|
||||||
|
ret uint32
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
h.Lock()
|
||||||
|
// Get position if available
|
||||||
|
if release {
|
||||||
|
bytePos, bitPos = ordinalToPos(ordinal)
|
||||||
|
} else {
|
||||||
|
if any {
|
||||||
|
bytePos, bitPos, err = getFirstAvailable(h.head)
|
||||||
|
ret = posToOrdinal(bytePos, bitPos)
|
||||||
|
} else {
|
||||||
|
bytePos, bitPos, err = checkIfAvailable(h.head, ordinal)
|
||||||
|
ret = ordinal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
h.Unlock()
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a private copy of h and work on it, also copy the current db index
|
||||||
|
nh := h.getCopy()
|
||||||
|
ci := h.dbIndex
|
||||||
|
h.Unlock()
|
||||||
|
|
||||||
|
nh.head = pushReservation(bytePos, bitPos, nh.head, release)
|
||||||
|
if release {
|
||||||
|
nh.unselected++
|
||||||
|
} else {
|
||||||
|
nh.unselected--
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to write private copy to store
|
||||||
|
if err := nh.writeToStore(); err != nil {
|
||||||
|
if _, ok := err.(types.RetryError); !ok {
|
||||||
|
return ret, fmt.Errorf("internal failure while setting the bit: %v", err)
|
||||||
|
}
|
||||||
|
// Retry
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unless unexpected error, save private copy to local copy
|
||||||
|
h.Lock()
|
||||||
|
defer h.Unlock()
|
||||||
|
if h.dbIndex != ci {
|
||||||
|
return ret, fmt.Errorf("unexected database index change")
|
||||||
|
}
|
||||||
|
h.unselected = nh.unselected
|
||||||
|
h.head = nh.head
|
||||||
|
h.dbExists = nh.dbExists
|
||||||
|
h.dbIndex = nh.dbIndex
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks is needed because to cover the case where the number of bits is not a multiple of blockLen
|
||||||
|
func (h *Handle) validateOrdinal(ordinal uint32) error {
|
||||||
|
if ordinal > h.bits {
|
||||||
|
return fmt.Errorf("bit does not belong to the sequence")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushReservation pushes the bit reservation inside the bitmask.
|
||||||
|
// @Deprecated Use Set() instead
|
||||||
|
func (h *Handle) PushReservation(bytePos, bitPos uint32, release bool) error {
|
||||||
|
// Create a private copy of h and work on it, also copy the current db index
|
||||||
|
h.Lock()
|
||||||
|
nh := h.getCopy()
|
||||||
|
ci := h.dbIndex
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
|
|
||||||
nh.head = pushReservation(bytePos, bitPos, nh.head, release)
|
nh.head = pushReservation(bytePos, bitPos, nh.head, release)
|
||||||
|
if release {
|
||||||
err := nh.writeToStore()
|
nh.unselected++
|
||||||
if err == nil {
|
} else {
|
||||||
// Commit went through, save locally
|
nh.unselected--
|
||||||
h.Lock()
|
|
||||||
h.head = nh.head
|
|
||||||
if release {
|
|
||||||
h.unselected++
|
|
||||||
} else {
|
|
||||||
h.unselected--
|
|
||||||
}
|
|
||||||
// Can't use SetIndex() since we're locked.
|
|
||||||
h.dbIndex = nh.Index()
|
|
||||||
h.dbExists = true
|
|
||||||
h.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
// Attempt to write private copy to store
|
||||||
|
if err := nh.writeToStore(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unless unexpected error, save private copy to local copy
|
||||||
|
h.Lock()
|
||||||
|
defer h.Unlock()
|
||||||
|
if h.dbIndex != ci {
|
||||||
|
return fmt.Errorf("unexected database index change")
|
||||||
|
}
|
||||||
|
h.unselected = nh.unselected
|
||||||
|
h.head = nh.head
|
||||||
|
h.dbExists = nh.dbExists
|
||||||
|
h.dbIndex = nh.dbIndex
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy removes from the datastore the data belonging to this handle
|
// Destroy removes from the datastore the data belonging to this handle
|
||||||
|
@ -285,7 +416,7 @@ func getFirstAvailable(head *sequence) (uint32, uint32, error) {
|
||||||
byteIndex += current.count * blockBytes
|
byteIndex += current.count * blockBytes
|
||||||
current = current.next
|
current = current.next
|
||||||
}
|
}
|
||||||
return invalidPos, invalidPos, fmt.Errorf("no bit available")
|
return invalidPos, invalidPos, errNoBitAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
||||||
|
@ -363,7 +494,7 @@ func pushReservation(bytePos, bitPos uint32, head *sequence, release bool) *sequ
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct updated block
|
// Construct updated block
|
||||||
bitSel := uint32(blockFirstBit >> uint(inBlockBytePos*8+bitPos))
|
bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
|
||||||
newBlock := current.block
|
newBlock := current.block
|
||||||
if release {
|
if release {
|
||||||
newBlock &^= bitSel
|
newBlock &^= bitSel
|
||||||
|
@ -447,3 +578,11 @@ func getNumBlocks(numBits uint32) uint32 {
|
||||||
}
|
}
|
||||||
return numBlocks
|
return numBlocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ordinalToPos(ordinal uint32) (uint32, uint32) {
|
||||||
|
return ordinal / 8, ordinal % 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func posToOrdinal(bytePos, bitPos uint32) uint32 {
|
||||||
|
return bytePos*8 + bitPos
|
||||||
|
}
|
||||||
|
|
|
@ -90,48 +90,7 @@ func TestSequenceEqual(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSequenceCopy(t *testing.T) {
|
func TestSequenceCopy(t *testing.T) {
|
||||||
s := &sequence{
|
s := getTestSequence()
|
||||||
block: 0x0,
|
|
||||||
count: 8,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 8,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 0,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 0,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 2,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 1,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 1,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 2,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x1,
|
|
||||||
count: 1,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0x0,
|
|
||||||
count: 2,
|
|
||||||
next: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
n := s.getCopy()
|
n := s.getCopy()
|
||||||
if !s.equal(n) {
|
if !s.equal(n) {
|
||||||
t.Fatalf("copy of s failed")
|
t.Fatalf("copy of s failed")
|
||||||
|
@ -388,30 +347,7 @@ func TestPushReservation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSerializeDeserialize(t *testing.T) {
|
func TestSerializeDeserialize(t *testing.T) {
|
||||||
s := &sequence{
|
s := getTestSequence()
|
||||||
block: 0xffffffff,
|
|
||||||
count: 1,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0xFF000000,
|
|
||||||
count: 1,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0xffffffff,
|
|
||||||
count: 6,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0xffffffff,
|
|
||||||
count: 1,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0xFF800000,
|
|
||||||
count: 1,
|
|
||||||
next: &sequence{
|
|
||||||
block: 0xffffffff,
|
|
||||||
count: 6,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := s.toByteArray()
|
data, err := s.toByteArray()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -428,3 +364,134 @@ func TestSerializeDeserialize(t *testing.T) {
|
||||||
t.Fatalf("Sequences are different: \n%v\n%v", s, r)
|
t.Fatalf("Sequences are different: \n%v\n%v", s, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTestSequence() *sequence {
|
||||||
|
// Returns a custom sequence of 1024 * 32 bits
|
||||||
|
return &sequence{
|
||||||
|
block: 0XFFFFFFFF,
|
||||||
|
count: 100,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0xFFFFFFFE,
|
||||||
|
count: 1,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0xFF000000,
|
||||||
|
count: 10,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0XFFFFFFFF,
|
||||||
|
count: 50,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0XFFFFFFFC,
|
||||||
|
count: 1,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0xFF800000,
|
||||||
|
count: 1,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0XFFFFFFFF,
|
||||||
|
count: 87,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0x0,
|
||||||
|
count: 150,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0XFFFFFFFF,
|
||||||
|
count: 200,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0x0000FFFF,
|
||||||
|
count: 1,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0x0,
|
||||||
|
count: 399,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0XFFFFFFFF,
|
||||||
|
count: 23,
|
||||||
|
next: &sequence{
|
||||||
|
block: 0x1,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet(t *testing.T) {
|
||||||
|
hnd, err := NewHandle("", nil, "", 1024*32)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
hnd.head = getTestSequence()
|
||||||
|
|
||||||
|
firstAv := uint32(32*100 + 31)
|
||||||
|
last := uint32(1024*32 - 1)
|
||||||
|
|
||||||
|
if hnd.IsSet(100000) {
|
||||||
|
t.Fatal("IsSet() returned wrong result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hnd.IsSet(0) {
|
||||||
|
t.Fatal("IsSet() returned wrong result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if hnd.IsSet(firstAv) {
|
||||||
|
t.Fatal("IsSet() returned wrong result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hnd.IsSet(last) {
|
||||||
|
t.Fatal("IsSet() returned wrong result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hnd.Set(0); err == nil {
|
||||||
|
t.Fatalf("Expected failure, but succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
os, err := hnd.SetAny()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected failure: %v", err)
|
||||||
|
}
|
||||||
|
if os != firstAv {
|
||||||
|
t.Fatalf("SetAny returned unexpected ordinal. Expected %d. Got %d.", firstAv, os)
|
||||||
|
}
|
||||||
|
if !hnd.IsSet(firstAv) {
|
||||||
|
t.Fatal("IsSet() returned wrong result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hnd.Unset(firstAv); err != nil {
|
||||||
|
t.Fatalf("Unexpected failure: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hnd.IsSet(firstAv) {
|
||||||
|
t.Fatal("IsSet() returned wrong result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hnd.Set(firstAv); err != nil {
|
||||||
|
t.Fatalf("Unexpected failure: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hnd.Set(last); err == nil {
|
||||||
|
t.Fatalf("Expected failure, but succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetUnset(t *testing.T) {
|
||||||
|
numBits := uint32(64 * 1024)
|
||||||
|
hnd, err := NewHandle("", nil, "", numBits)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// set and unset all one by one
|
||||||
|
for hnd.Unselected() > 0 {
|
||||||
|
hnd.SetAny()
|
||||||
|
}
|
||||||
|
i := uint32(0)
|
||||||
|
for hnd.Unselected() < numBits {
|
||||||
|
hnd.Unset(i)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (h *Handle) writeToStore() error {
|
||||||
}
|
}
|
||||||
err := store.PutObjectAtomic(h)
|
err := store.PutObjectAtomic(h)
|
||||||
if err == datastore.ErrKeyModified {
|
if err == datastore.ErrKeyModified {
|
||||||
return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
|
return types.RetryErrorf("failed to perform atomic write (%v). Retry might fix the error", err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue