| // 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 | |
| } |