moby/libnetwork/bitseq/sequence.go

214 lines
5.6 KiB
Go
Raw Normal View History

// 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 {
bitseq: fix race between CopyTo and set Race detector message: WARNING: DATA RACE Write by goroutine 269: github.com/docker/libnetwork/bitseq.(*Handle).CopyTo() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/store.go:85 +0x2f6 github.com/docker/libnetwork/datastore.(*cache).get() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/datastore/cache.go:135 +0x307 github.com/docker/libnetwork/datastore.(*datastore).GetObject() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/datastore/datastore.go:438 +0x121 github.com/docker/libnetwork/bitseq.(*Handle).set() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:254 +0x1a5 github.com/docker/libnetwork/bitseq.(*Handle).Unset() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:227 +0xb0 github.com/docker/libnetwork/ipam.(*Allocator).ReleaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/ipam/allocator.go:446 +0x10bc github.com/docker/libnetwork.(*endpoint).releaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:830 +0x731 github.com/docker/libnetwork.(*endpoint).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:624 +0x8d8 github.com/docker/libnetwork.(*sandbox).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/sandbox.go:191 +0x1047 github.com/docker/docker/daemon.(*Daemon).releaseNetwork() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/container_unix.go:1180 +0x676 github.com/docker/docker/daemon.(*Daemon).Cleanup() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:157 +0x5d github.com/docker/docker/daemon.(*containerMonitor).Close() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:111 +0xa4 github.com/docker/docker/daemon.(*containerMonitor).Start.func1() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:142 +0x14b github.com/docker/docker/daemon.(*containerMonitor).Start() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:223 +0x1159 github.com/docker/docker/daemon.(*containerMonitor).Start-fm() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:147 +0x3b github.com/docker/docker/pkg/promise.Go.func1() /home/moroz/project/workspace/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x2a Previous read by goroutine 340: github.com/docker/libnetwork/bitseq.(*Handle).set() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:254 +0x133 github.com/docker/libnetwork/bitseq.(*Handle).Unset() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:227 +0xb0 github.com/docker/libnetwork/ipam.(*Allocator).ReleaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/ipam/allocator.go:446 +0x10bc github.com/docker/libnetwork.(*endpoint).releaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:830 +0x731 github.com/docker/libnetwork.(*endpoint).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:624 +0x8d8 github.com/docker/libnetwork.(*sandbox).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/sandbox.go:191 +0x1047 github.com/docker/docker/daemon.(*Daemon).releaseNetwork() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/container_unix.go:1180 +0x676 github.com/docker/docker/daemon.(*Daemon).Cleanup() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:157 +0x5d github.com/docker/docker/daemon.(*containerMonitor).Close() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:111 +0xa4 github.com/docker/docker/daemon.(*containerMonitor).Start.func1() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:142 +0x14b github.com/docker/docker/daemon.(*containerMonitor).Start() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:223 +0x1159 github.com/docker/docker/daemon.(*containerMonitor).Start-fm() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:147 +0x3b github.com/docker/docker/pkg/promise.Go.func1() /home/moroz/project/workspace/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x2a Signed-off-by: Alexander Morozov <lk4d4@docker.com>
2015-11-30 21:18:52 +00:00
var store datastore.DataStore
h.mu.Lock()
bitseq: fix race between CopyTo and set Race detector message: WARNING: DATA RACE Write by goroutine 269: github.com/docker/libnetwork/bitseq.(*Handle).CopyTo() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/store.go:85 +0x2f6 github.com/docker/libnetwork/datastore.(*cache).get() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/datastore/cache.go:135 +0x307 github.com/docker/libnetwork/datastore.(*datastore).GetObject() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/datastore/datastore.go:438 +0x121 github.com/docker/libnetwork/bitseq.(*Handle).set() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:254 +0x1a5 github.com/docker/libnetwork/bitseq.(*Handle).Unset() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:227 +0xb0 github.com/docker/libnetwork/ipam.(*Allocator).ReleaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/ipam/allocator.go:446 +0x10bc github.com/docker/libnetwork.(*endpoint).releaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:830 +0x731 github.com/docker/libnetwork.(*endpoint).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:624 +0x8d8 github.com/docker/libnetwork.(*sandbox).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/sandbox.go:191 +0x1047 github.com/docker/docker/daemon.(*Daemon).releaseNetwork() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/container_unix.go:1180 +0x676 github.com/docker/docker/daemon.(*Daemon).Cleanup() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:157 +0x5d github.com/docker/docker/daemon.(*containerMonitor).Close() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:111 +0xa4 github.com/docker/docker/daemon.(*containerMonitor).Start.func1() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:142 +0x14b github.com/docker/docker/daemon.(*containerMonitor).Start() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:223 +0x1159 github.com/docker/docker/daemon.(*containerMonitor).Start-fm() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:147 +0x3b github.com/docker/docker/pkg/promise.Go.func1() /home/moroz/project/workspace/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x2a Previous read by goroutine 340: github.com/docker/libnetwork/bitseq.(*Handle).set() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:254 +0x133 github.com/docker/libnetwork/bitseq.(*Handle).Unset() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:227 +0xb0 github.com/docker/libnetwork/ipam.(*Allocator).ReleaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/ipam/allocator.go:446 +0x10bc github.com/docker/libnetwork.(*endpoint).releaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:830 +0x731 github.com/docker/libnetwork.(*endpoint).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:624 +0x8d8 github.com/docker/libnetwork.(*sandbox).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/sandbox.go:191 +0x1047 github.com/docker/docker/daemon.(*Daemon).releaseNetwork() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/container_unix.go:1180 +0x676 github.com/docker/docker/daemon.(*Daemon).Cleanup() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:157 +0x5d github.com/docker/docker/daemon.(*containerMonitor).Close() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:111 +0xa4 github.com/docker/docker/daemon.(*containerMonitor).Start.func1() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:142 +0x14b github.com/docker/docker/daemon.(*containerMonitor).Start() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:223 +0x1159 github.com/docker/docker/daemon.(*containerMonitor).Start-fm() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:147 +0x3b github.com/docker/docker/pkg/promise.Go.func1() /home/moroz/project/workspace/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x2a Signed-off-by: Alexander Morozov <lk4d4@docker.com>
2015-11-30 21:18:52 +00:00
store = h.store
if store != nil {
h.mu.Unlock() // The lock is acquired in the GetObject
bitseq: fix race between CopyTo and set Race detector message: WARNING: DATA RACE Write by goroutine 269: github.com/docker/libnetwork/bitseq.(*Handle).CopyTo() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/store.go:85 +0x2f6 github.com/docker/libnetwork/datastore.(*cache).get() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/datastore/cache.go:135 +0x307 github.com/docker/libnetwork/datastore.(*datastore).GetObject() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/datastore/datastore.go:438 +0x121 github.com/docker/libnetwork/bitseq.(*Handle).set() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:254 +0x1a5 github.com/docker/libnetwork/bitseq.(*Handle).Unset() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:227 +0xb0 github.com/docker/libnetwork/ipam.(*Allocator).ReleaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/ipam/allocator.go:446 +0x10bc github.com/docker/libnetwork.(*endpoint).releaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:830 +0x731 github.com/docker/libnetwork.(*endpoint).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:624 +0x8d8 github.com/docker/libnetwork.(*sandbox).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/sandbox.go:191 +0x1047 github.com/docker/docker/daemon.(*Daemon).releaseNetwork() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/container_unix.go:1180 +0x676 github.com/docker/docker/daemon.(*Daemon).Cleanup() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:157 +0x5d github.com/docker/docker/daemon.(*containerMonitor).Close() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:111 +0xa4 github.com/docker/docker/daemon.(*containerMonitor).Start.func1() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:142 +0x14b github.com/docker/docker/daemon.(*containerMonitor).Start() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:223 +0x1159 github.com/docker/docker/daemon.(*containerMonitor).Start-fm() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:147 +0x3b github.com/docker/docker/pkg/promise.Go.func1() /home/moroz/project/workspace/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x2a Previous read by goroutine 340: github.com/docker/libnetwork/bitseq.(*Handle).set() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:254 +0x133 github.com/docker/libnetwork/bitseq.(*Handle).Unset() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go:227 +0xb0 github.com/docker/libnetwork/ipam.(*Allocator).ReleaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/ipam/allocator.go:446 +0x10bc github.com/docker/libnetwork.(*endpoint).releaseAddress() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:830 +0x731 github.com/docker/libnetwork.(*endpoint).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/endpoint.go:624 +0x8d8 github.com/docker/libnetwork.(*sandbox).Delete() /home/moroz/project/workspace/src/github.com/docker/docker/vendor/src/github.com/docker/libnetwork/sandbox.go:191 +0x1047 github.com/docker/docker/daemon.(*Daemon).releaseNetwork() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/container_unix.go:1180 +0x676 github.com/docker/docker/daemon.(*Daemon).Cleanup() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:157 +0x5d github.com/docker/docker/daemon.(*containerMonitor).Close() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:111 +0xa4 github.com/docker/docker/daemon.(*containerMonitor).Start.func1() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:142 +0x14b github.com/docker/docker/daemon.(*containerMonitor).Start() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/monitor.go:223 +0x1159 github.com/docker/docker/daemon.(*containerMonitor).Start-fm() /home/moroz/project/workspace/src/github.com/docker/docker/daemon/start.go:147 +0x3b github.com/docker/docker/pkg/promise.Go.func1() /home/moroz/project/workspace/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x2a Signed-off-by: Alexander Morozov <lk4d4@docker.com>
2015-11-30 21:18:52 +00:00
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
}