1
0
Fork 0
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:
Alessandro Boch 2015-06-24 12:10:54 -07:00
parent ee31009744
commit 01d6585a31
3 changed files with 303 additions and 97 deletions

View file

@ -9,6 +9,7 @@ import (
"sync"
"github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/types"
)
// block sequence constants
@ -21,6 +22,10 @@ const (
invalidPos = blockMAX
)
var (
errNoBitAvailable = fmt.Errorf("no bit available")
)
// Handle contains the sequece representing the bitmask and its identifier
type Handle struct {
bits uint32
@ -145,7 +150,7 @@ func (s *sequence) toByteArray() ([]byte, error) {
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 {
l := len(data)
if l%8 != 0 {
@ -168,7 +173,21 @@ func (s *sequence) fromByteArray(data []byte) error {
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
// @Deprecated Use SetAny() instead
func (h *Handle) GetFirstAvailable() (uint32, uint32, error) {
h.Lock()
defer h.Unlock()
@ -177,47 +196,159 @@ func (h *Handle) GetFirstAvailable() (uint32, uint32, error) {
// 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
// @Deprecated Use IsSet() instead
func (h *Handle) CheckIfAvailable(ordinal uint32) (uint32, uint32, error) {
if err := h.validateOrdinal(ordinal); err != nil {
return invalidPos, invalidPos, err
}
h.Lock()
defer h.Unlock()
return checkIfAvailable(h.head, ordinal)
}
// PushReservation pushes the bit reservation inside the bitmask.
func (h *Handle) PushReservation(bytePos, bitPos uint32, release bool) error {
// Create a copy of the current handler
h.Lock()
nh := &Handle{
app: h.app,
id: h.id,
store: h.store,
dbIndex: h.dbIndex,
head: h.head.GetCopy(),
dbExists: h.dbExists,
// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal
func (h *Handle) SetAny() (uint32, error) {
if h.Unselected() == 0 {
return invalidPos, errNoBitAvailable
}
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)
err := nh.writeToStore()
if err == nil {
// Commit went through, save locally
h.Lock()
h.head = nh.head
if release {
h.unselected++
nh.unselected++
} else {
h.unselected--
}
// Can't use SetIndex() since we're locked.
h.dbIndex = nh.Index()
h.dbExists = true
h.Unlock()
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()
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 {
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
func (h *Handle) Destroy() {
h.deleteFromStore()
@ -285,7 +416,7 @@ func getFirstAvailable(head *sequence) (uint32, uint32, error) {
byteIndex += current.count * blockBytes
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
@ -363,7 +494,7 @@ func pushReservation(bytePos, bitPos uint32, head *sequence, release bool) *sequ
}
// Construct updated block
bitSel := uint32(blockFirstBit >> uint(inBlockBytePos*8+bitPos))
bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
newBlock := current.block
if release {
newBlock &^= bitSel
@ -447,3 +578,11 @@ func getNumBlocks(numBits uint32) uint32 {
}
return numBlocks
}
func ordinalToPos(ordinal uint32) (uint32, uint32) {
return ordinal / 8, ordinal % 8
}
func posToOrdinal(bytePos, bitPos uint32) uint32 {
return bytePos*8 + bitPos
}

View file

@ -90,48 +90,7 @@ func TestSequenceEqual(t *testing.T) {
}
func TestSequenceCopy(t *testing.T) {
s := &sequence{
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,
},
},
},
},
},
},
},
},
},
}
s := getTestSequence()
n := s.getCopy()
if !s.equal(n) {
t.Fatalf("copy of s failed")
@ -388,30 +347,7 @@ func TestPushReservation(t *testing.T) {
}
func TestSerializeDeserialize(t *testing.T) {
s := &sequence{
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,
},
},
},
},
},
}
s := getTestSequence()
data, err := s.toByteArray()
if err != nil {
@ -428,3 +364,134 @@ func TestSerializeDeserialize(t *testing.T) {
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++
}
}

View file

@ -122,7 +122,7 @@ func (h *Handle) writeToStore() error {
}
err := store.PutObjectAtomic(h)
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
}