6f08fe20e9
That method was only referenced by ipam.Allocator, but as it no longer stores any state persistently there is no possibility for it to load an inconsistent bit-sequence from Docker 1.9.x. Signed-off-by: Cory Snider <csnider@mirantis.com>
213 lines
5.6 KiB
Go
213 lines
5.6 KiB
Go
// Package bitseq provides a structure and utilities for representing a long
|
|
// bitmask which is persisted in a datastore. It is backed by [bitmap.Bitmap]
|
|
// which operates directly on the encoded representation, without uncompressing.
|
|
package bitseq
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/docker/docker/libnetwork/bitmap"
|
|
"github.com/docker/docker/libnetwork/datastore"
|
|
"github.com/docker/docker/libnetwork/types"
|
|
)
|
|
|
|
var (
|
|
// ErrNoBitAvailable is returned when no more bits are available to set
|
|
ErrNoBitAvailable = bitmap.ErrNoBitAvailable
|
|
// ErrBitAllocated is returned when the specific bit requested is already set
|
|
ErrBitAllocated = bitmap.ErrBitAllocated
|
|
)
|
|
|
|
// Handle contains the sequence representing the bitmask and its identifier
|
|
type Handle struct {
|
|
app string
|
|
id string
|
|
dbIndex uint64
|
|
dbExists bool
|
|
store datastore.DataStore
|
|
bm *bitmap.Bitmap
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewHandle returns a thread-safe instance of the bitmask handler
|
|
func NewHandle(app string, ds datastore.DataStore, id string, numElements uint64) (*Handle, error) {
|
|
h := &Handle{
|
|
bm: bitmap.New(numElements),
|
|
app: app,
|
|
id: id,
|
|
store: ds,
|
|
}
|
|
|
|
if h.store == nil {
|
|
return h, nil
|
|
}
|
|
|
|
// Get the initial status from the ds if present.
|
|
if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
|
|
return nil, err
|
|
}
|
|
|
|
// If the handle is not in store, write it.
|
|
if !h.Exists() {
|
|
if err := h.writeToStore(); err != nil {
|
|
return nil, fmt.Errorf("failed to write bitsequence to store: %v", err)
|
|
}
|
|
}
|
|
|
|
return h, nil
|
|
}
|
|
|
|
func (h *Handle) getCopy() *Handle {
|
|
return &Handle{
|
|
bm: bitmap.Copy(h.bm),
|
|
app: h.app,
|
|
id: h.id,
|
|
dbIndex: h.dbIndex,
|
|
dbExists: h.dbExists,
|
|
store: h.store,
|
|
}
|
|
}
|
|
|
|
// SetAnyInRange atomically sets the first unset bit in the specified range in the sequence and returns the corresponding ordinal
|
|
func (h *Handle) SetAnyInRange(start, end uint64, serial bool) (uint64, error) {
|
|
return h.apply(func(b *bitmap.Bitmap) (uint64, error) { return b.SetAnyInRange(start, end, serial) })
|
|
}
|
|
|
|
// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal
|
|
func (h *Handle) SetAny(serial bool) (uint64, error) {
|
|
return h.apply(func(b *bitmap.Bitmap) (uint64, error) { return b.SetAny(serial) })
|
|
}
|
|
|
|
// Set atomically sets the corresponding bit in the sequence
|
|
func (h *Handle) Set(ordinal uint64) error {
|
|
_, err := h.apply(func(b *bitmap.Bitmap) (uint64, error) { return 0, b.Set(ordinal) })
|
|
return err
|
|
}
|
|
|
|
// Unset atomically unsets the corresponding bit in the sequence
|
|
func (h *Handle) Unset(ordinal uint64) error {
|
|
_, err := h.apply(func(b *bitmap.Bitmap) (uint64, error) { return 0, b.Unset(ordinal) })
|
|
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 uint64) bool {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
return h.bm.IsSet(ordinal)
|
|
}
|
|
|
|
// set/reset the bit
|
|
func (h *Handle) apply(op func(*bitmap.Bitmap) (uint64, error)) (uint64, error) {
|
|
for {
|
|
var store datastore.DataStore
|
|
h.mu.Lock()
|
|
store = h.store
|
|
if store != nil {
|
|
h.mu.Unlock() // The lock is acquired in the GetObject
|
|
if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
|
|
return 0, err
|
|
}
|
|
h.mu.Lock() // Acquire the lock back
|
|
}
|
|
|
|
// Create a private copy of h and work on it
|
|
nh := h.getCopy()
|
|
|
|
ret, err := op(nh.bm)
|
|
if err != nil {
|
|
h.mu.Unlock()
|
|
return ret, err
|
|
}
|
|
|
|
if h.store != nil {
|
|
h.mu.Unlock()
|
|
// 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
|
|
}
|
|
h.mu.Lock()
|
|
}
|
|
|
|
// Previous atomic push was successful. Save private copy to local copy
|
|
h.bm = nh.bm
|
|
h.dbExists = nh.dbExists
|
|
h.dbIndex = nh.dbIndex
|
|
h.mu.Unlock()
|
|
return ret, nil
|
|
}
|
|
}
|
|
|
|
// Destroy removes from the datastore the data belonging to this handle
|
|
func (h *Handle) Destroy() error {
|
|
for {
|
|
if err := h.deleteFromStore(); err != nil {
|
|
if _, ok := err.(types.RetryError); !ok {
|
|
return fmt.Errorf("internal failure while destroying the sequence: %v", err)
|
|
}
|
|
// Fetch latest
|
|
if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil {
|
|
if err == datastore.ErrKeyNotFound { // already removed
|
|
return nil
|
|
}
|
|
return fmt.Errorf("failed to fetch from store when destroying the sequence: %v", err)
|
|
}
|
|
continue
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Bits returns the length of the bit sequence
|
|
func (h *Handle) Bits() uint64 {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
return h.bm.Bits()
|
|
}
|
|
|
|
// Unselected returns the number of bits which are not selected
|
|
func (h *Handle) Unselected() uint64 {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
return h.bm.Unselected()
|
|
}
|
|
|
|
func (h *Handle) String() string {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, %s",
|
|
h.app, h.id, h.dbIndex, h.bm)
|
|
}
|
|
|
|
type jsonMessage struct {
|
|
ID string `json:"id"`
|
|
Sequence *bitmap.Bitmap `json:"sequence"`
|
|
}
|
|
|
|
// MarshalJSON encodes h into a JSON message.
|
|
func (h *Handle) MarshalJSON() ([]byte, error) {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
m := jsonMessage{ID: h.id, Sequence: h.bm}
|
|
return json.Marshal(m)
|
|
}
|
|
|
|
// UnmarshalJSON decodes a JSON message into h.
|
|
func (h *Handle) UnmarshalJSON(data []byte) error {
|
|
var m jsonMessage
|
|
if err := json.Unmarshal(data, &m); err != nil {
|
|
return err
|
|
}
|
|
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
h.id, h.bm = m.ID, m.Sequence
|
|
return nil
|
|
}
|