浏览代码

libnetwork/bitseq: refactor in terms of bitmap

Signed-off-by: Cory Snider <csnider@mirantis.com>
Cory Snider 2 年之前
父节点
当前提交
94ef26428b
共有 3 个文件被更改,包括 79 次插入1575 次删除
  1. 55 517
      libnetwork/bitseq/sequence.go
  2. 21 1053
      libnetwork/bitseq/sequence_test.go
  3. 3 5
      libnetwork/bitseq/store.go

+ 55 - 517
libnetwork/bitseq/sequence.go

@@ -1,63 +1,44 @@
-// Package bitseq provides a structure and utilities for representing long bitmask
-// as sequence of run-length encoded blocks. It operates directly on the encoded
-// representation, it does not decode/encode.
+// 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/binary"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"sync"
 
+	"github.com/docker/docker/libnetwork/bitmap"
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/sirupsen/logrus"
 )
 
-// block sequence constants
-// If needed we can think of making these configurable
-const (
-	blockLen      = uint32(32)
-	blockBytes    = uint64(blockLen / 8)
-	blockMAX      = uint32(1<<blockLen - 1)
-	blockFirstBit = uint32(1) << (blockLen - 1)
-	invalidPos    = uint64(0xFFFFFFFFFFFFFFFF)
-)
-
 var (
 	// ErrNoBitAvailable is returned when no more bits are available to set
-	ErrNoBitAvailable = errors.New("no bit available")
+	ErrNoBitAvailable = bitmap.ErrNoBitAvailable
 	// ErrBitAllocated is returned when the specific bit requested is already set
-	ErrBitAllocated = errors.New("requested bit is already allocated")
+	ErrBitAllocated = bitmap.ErrBitAllocated
 )
 
 // Handle contains the sequence representing the bitmask and its identifier
 type Handle struct {
-	bits       uint64
-	unselected uint64
-	head       *sequence
-	app        string
-	id         string
-	dbIndex    uint64
-	dbExists   bool
-	curr       uint64
-	store      datastore.DataStore
+	app      string
+	id       string
+	dbIndex  uint64
+	dbExists bool
+	store    datastore.DataStore
+	bm       *bitmap.Bitmap
 	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{
-		app:        app,
-		id:         id,
-		store:      ds,
-		bits:       numElements,
-		unselected: numElements,
-		head: &sequence{
-			block: 0x0,
-			count: getNumBlocks(numElements),
-		},
+		bm:    bitmap.New(numElements),
+		app:   app,
+		id:    id,
+		store: ds,
 	}
 
 	if h.store == nil {
@@ -79,187 +60,45 @@ func NewHandle(app string, ds datastore.DataStore, id string, numElements uint64
 	return h, nil
 }
 
-// sequence represents a recurring sequence of 32 bits long bitmasks
-type sequence struct {
-	block uint32    // block is a symbol representing 4 byte long allocation bitmask
-	count uint64    // number of consecutive blocks (symbols)
-	next  *sequence // next sequence
-}
-
-// String returns a string representation of the block sequence starting from this block
-func (s *sequence) toString() string {
-	var nextBlock string
-	if s.next == nil {
-		nextBlock = "end"
-	} else {
-		nextBlock = s.next.toString()
-	}
-	return fmt.Sprintf("(0x%x, %d)->%s", s.block, s.count, nextBlock)
-}
-
-// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence
-func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) {
-	if s.block == blockMAX || s.count == 0 {
-		return invalidPos, invalidPos, ErrNoBitAvailable
-	}
-	bits := from
-	bitSel := blockFirstBit >> from
-	for bitSel > 0 && s.block&bitSel != 0 {
-		bitSel >>= 1
-		bits++
-	}
-	// Check if the loop exited because it could not
-	// find any available bit int block  starting from
-	// "from". Return invalid pos in that case.
-	if bitSel == 0 {
-		return invalidPos, invalidPos, ErrNoBitAvailable
-	}
-	return bits / 8, bits % 8, nil
-}
-
-// GetCopy returns a copy of the linked list rooted at this node
-func (s *sequence) getCopy() *sequence {
-	n := &sequence{block: s.block, count: s.count}
-	pn := n
-	ps := s.next
-	for ps != nil {
-		pn.next = &sequence{block: ps.block, count: ps.count}
-		pn = pn.next
-		ps = ps.next
-	}
-	return n
-}
-
-// Equal checks if this sequence is equal to the passed one
-func (s *sequence) equal(o *sequence) bool {
-	this := s
-	other := o
-	for this != nil {
-		if other == nil {
-			return false
-		}
-		if this.block != other.block || this.count != other.count {
-			return false
-		}
-		this = this.next
-		other = other.next
-	}
-	return other == nil
-}
-
-// ToByteArray converts the sequence into a byte array
-func (s *sequence) toByteArray() ([]byte, error) {
-	var bb []byte
-
-	p := s
-	for p != nil {
-		b := make([]byte, 12)
-		binary.BigEndian.PutUint32(b[0:], p.block)
-		binary.BigEndian.PutUint64(b[4:], p.count)
-		bb = append(bb, b...)
-		p = p.next
-	}
-
-	return bb, nil
-}
-
-// fromByteArray construct the sequence from the byte array
-func (s *sequence) fromByteArray(data []byte) error {
-	l := len(data)
-	if l%12 != 0 {
-		return fmt.Errorf("cannot deserialize byte sequence of length %d (%v)", l, data)
-	}
-
-	p := s
-	i := 0
-	for {
-		p.block = binary.BigEndian.Uint32(data[i : i+4])
-		p.count = binary.BigEndian.Uint64(data[i+4 : i+12])
-		i += 12
-		if i == l {
-			break
-		}
-		p.next = &sequence{}
-		p = p.next
-	}
-
-	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,
-		curr:       h.curr,
+		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) {
-	if end < start || end >= h.bits {
-		return invalidPos, fmt.Errorf("invalid bit range [%d, %d]", start, end)
-	}
-	if h.Unselected() == 0 {
-		return invalidPos, ErrNoBitAvailable
-	}
-	return h.set(0, start, end, true, false, serial)
+	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) {
-	if h.Unselected() == 0 {
-		return invalidPos, ErrNoBitAvailable
-	}
-	return h.set(0, 0, h.bits-1, true, false, serial)
+	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 {
-	if err := h.validateOrdinal(ordinal); err != nil {
-		return err
-	}
-	_, err := h.set(ordinal, 0, 0, false, false, false)
+	_, 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 {
-	if err := h.validateOrdinal(ordinal); err != nil {
-		return err
-	}
-	_, err := h.set(ordinal, 0, 0, false, true, false)
+	_, 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 {
-	if err := h.validateOrdinal(ordinal); err != nil {
-		return false
-	}
 	h.Lock()
-	_, _, err := checkIfAvailable(h.head, ordinal)
-	h.Unlock()
-	return err != nil
-}
-
-func (h *Handle) runConsistencyCheck() bool {
-	corrupted := false
-	for p, c := h.head, h.head.next; c != nil; c = c.next {
-		if c.count == 0 {
-			corrupted = true
-			p.next = c.next
-			continue // keep same p
-		}
-		p = c
-	}
-	return corrupted
+	defer h.Unlock()
+	return h.bm.IsSet(ordinal)
 }
 
 // CheckConsistency checks if the bit sequence is in an inconsistent state and attempts to fix it.
@@ -280,7 +119,7 @@ func (h *Handle) CheckConsistency() error {
 		nh := h.getCopy()
 		h.Unlock()
 
-		if !nh.runConsistencyCheck() {
+		if !nh.bm.CheckConsistency() {
 			return nil
 		}
 
@@ -294,7 +133,7 @@ func (h *Handle) CheckConsistency() error {
 		logrus.Infof("Fixed inconsistent bit sequence in datastore:\n%s\n%s", h, nh)
 
 		h.Lock()
-		h.head = nh.head
+		h.bm = nh.bm
 		h.Unlock()
 
 		return nil
@@ -302,57 +141,26 @@ func (h *Handle) CheckConsistency() error {
 }
 
 // set/reset the bit
-func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial bool) (uint64, error) {
-	var (
-		bitPos  uint64
-		bytePos uint64
-		ret     uint64
-		err     error
-	)
-
+func (h *Handle) apply(op func(*bitmap.Bitmap) (uint64, error)) (uint64, error) {
 	for {
 		var store datastore.DataStore
-		curr := uint64(0)
 		h.Lock()
 		store = h.store
 		if store != nil {
 			h.Unlock() // The lock is acquired in the GetObject
 			if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
-				return ret, err
+				return 0, err
 			}
 			h.Lock() // Acquire the lock back
 		}
-		if serial {
-			curr = h.curr
-		}
-		// Get position if available
-		if release {
-			bytePos, bitPos = ordinalToPos(ordinal)
-		} else {
-			if any {
-				bytePos, bitPos, err = getAvailableFromCurrent(h.head, start, curr, end)
-				ret = posToOrdinal(bytePos, bitPos)
-				if err == nil {
-					h.curr = ret + 1
-				}
-			} 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
 		nh := h.getCopy()
 
-		nh.head = pushReservation(bytePos, bitPos, nh.head, release)
-		if release {
-			nh.unselected++
-		} else {
-			nh.unselected--
+		ret, err := op(nh.bm)
+		if err != nil {
+			h.Unlock()
+			return ret, err
 		}
 
 		if h.store != nil {
@@ -369,8 +177,7 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial
 		}
 
 		// Previous atomic push was successful. Save private copy to local copy
-		h.unselected = nh.unselected
-		h.head = nh.head
+		h.bm = nh.bm
 		h.dbExists = nh.dbExists
 		h.dbIndex = nh.dbIndex
 		h.Unlock()
@@ -378,16 +185,6 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial
 	}
 }
 
-// checks is needed because to cover the case where the number of bits is not a multiple of blockLen
-func (h *Handle) validateOrdinal(ordinal uint64) error {
-	h.Lock()
-	defer h.Unlock()
-	if ordinal >= h.bits {
-		return errors.New("bit does not belong to the sequence")
-	}
-	return nil
-}
-
 // Destroy removes from the datastore the data belonging to this handle
 func (h *Handle) Destroy() error {
 	for {
@@ -408,69 +205,38 @@ func (h *Handle) Destroy() error {
 	}
 }
 
-// ToByteArray converts this handle's data into a byte array
-func (h *Handle) ToByteArray() ([]byte, error) {
-	h.Lock()
-	defer h.Unlock()
-	ba := make([]byte, 16)
-	binary.BigEndian.PutUint64(ba[0:], h.bits)
-	binary.BigEndian.PutUint64(ba[8:], h.unselected)
-	bm, err := h.head.toByteArray()
-	if err != nil {
-		return nil, fmt.Errorf("failed to serialize head: %s", err.Error())
-	}
-	ba = append(ba, bm...)
-
-	return ba, nil
-}
-
-// FromByteArray reads his handle's data from a byte array
-func (h *Handle) FromByteArray(ba []byte) error {
-	if ba == nil {
-		return errors.New("nil byte array")
-	}
-
-	nh := &sequence{}
-	err := nh.fromByteArray(ba[16:])
-	if err != nil {
-		return fmt.Errorf("failed to deserialize head: %s", err.Error())
-	}
-
-	h.Lock()
-	h.head = nh
-	h.bits = binary.BigEndian.Uint64(ba[0:8])
-	h.unselected = binary.BigEndian.Uint64(ba[8:16])
-	h.Unlock()
-
-	return nil
-}
-
 // Bits returns the length of the bit sequence
 func (h *Handle) Bits() uint64 {
-	return h.bits
+	h.Lock()
+	defer h.Unlock()
+	return h.bm.Bits()
 }
 
 // Unselected returns the number of bits which are not selected
 func (h *Handle) Unselected() uint64 {
 	h.Lock()
 	defer h.Unlock()
-	return h.unselected
+	return h.bm.Unselected()
 }
 
 func (h *Handle) String() string {
 	h.Lock()
 	defer h.Unlock()
-	return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, Bits: %d, Unselected: %d, Sequence: %s Curr:%d",
-		h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString(), h.curr)
+	return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, %s",
+		h.app, h.id, h.dbIndex, h.bm)
 }
 
-// MarshalJSON encodes Handle into json message
+// MarshalJSON encodes h into a JSON message.
 func (h *Handle) MarshalJSON() ([]byte, error) {
 	m := map[string]interface{}{
 		"id": h.id,
 	}
 
-	b, err := h.ToByteArray()
+	b, err := func() ([]byte, error) {
+		h.Lock()
+		defer h.Unlock()
+		return h.bm.MarshalBinary()
+	}()
 	if err != nil {
 		return nil, err
 	}
@@ -478,7 +244,7 @@ func (h *Handle) MarshalJSON() ([]byte, error) {
 	return json.Marshal(m)
 }
 
-// UnmarshalJSON decodes json message into Handle
+// UnmarshalJSON decodes a JSON message into h.
 func (h *Handle) UnmarshalJSON(data []byte) error {
 	var (
 		m   map[string]interface{}
@@ -493,239 +259,11 @@ func (h *Handle) UnmarshalJSON(data []byte) error {
 	if err := json.Unmarshal(bi, &b); err != nil {
 		return err
 	}
-	return h.FromByteArray(b)
-}
-
-// getFirstAvailable looks for the first unset bit in passed mask starting from start
-func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
-	// Find sequence which contains the start bit
-	byteStart, bitStart := ordinalToPos(start)
-	current, _, precBlocks, inBlockBytePos := findSequence(head, byteStart)
-	// Derive the this sequence offsets
-	byteOffset := byteStart - inBlockBytePos
-	bitOffset := inBlockBytePos*8 + bitStart
-	for current != nil {
-		if current.block != blockMAX {
-			// If the current block is not full, check if there is any bit
-			// from the current bit in the current block. If not, before proceeding to the
-			// next block node, make sure we check for available bit in the next
-			// instance of the same block. Due to RLE same block signature will be
-			// compressed.
-		retry:
-			bytePos, bitPos, err := current.getAvailableBit(bitOffset)
-			if err != nil && precBlocks == current.count-1 {
-				// This is the last instance in the same block node,
-				// so move to the next block.
-				goto next
-			}
-			if err != nil {
-				// There are some more instances of the same block, so add the offset
-				// and be optimistic that you will find the available bit in the next
-				// instance of the same block.
-				bitOffset = 0
-				byteOffset += blockBytes
-				precBlocks++
-				goto retry
-			}
-			return byteOffset + bytePos, bitPos, err
-		}
-		// Moving to next block: Reset bit offset.
-	next:
-		bitOffset = 0
-		byteOffset += (current.count * blockBytes) - (precBlocks * blockBytes)
-		precBlocks = 0
-		current = current.next
-	}
-	return invalidPos, invalidPos, ErrNoBitAvailable
-}
-
-// getAvailableFromCurrent will look for available ordinal from the current ordinal.
-// If none found then it will loop back to the start to check of the available bit.
-// This can be further optimized to check from start till curr in case of a rollover
-func getAvailableFromCurrent(head *sequence, start, curr, end uint64) (uint64, uint64, error) {
-	var bytePos, bitPos uint64
-	var err error
-	if curr != 0 && curr > start {
-		bytePos, bitPos, err = getFirstAvailable(head, curr)
-		ret := posToOrdinal(bytePos, bitPos)
-		if end < ret || err != nil {
-			goto begin
-		}
-		return bytePos, bitPos, nil
-	}
-
-begin:
-	bytePos, bitPos, err = getFirstAvailable(head, start)
-	ret := posToOrdinal(bytePos, bitPos)
-	if end < ret || err != nil {
-		return invalidPos, invalidPos, ErrNoBitAvailable
-	}
-	return bytePos, bitPos, nil
-}
-
-// 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
-func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) {
-	bytePos, bitPos := ordinalToPos(ordinal)
-
-	// Find the sequence containing this byte
-	current, _, _, inBlockBytePos := findSequence(head, bytePos)
-	if current != nil {
-		// Check whether the bit corresponding to the ordinal address is unset
-		bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
-		if current.block&bitSel == 0 {
-			return bytePos, bitPos, nil
-		}
-	}
-
-	return invalidPos, invalidPos, ErrBitAllocated
-}
-
-// Given the byte position and the sequences list head, return the pointer to the
-// sequence containing the byte (current), the pointer to the previous sequence,
-// the number of blocks preceding the block containing the byte inside the current sequence.
-// If bytePos is outside of the list, function will return (nil, nil, 0, invalidPos)
-func findSequence(head *sequence, bytePos uint64) (*sequence, *sequence, uint64, uint64) {
-	// Find the sequence containing this byte
-	previous := head
-	current := head
-	n := bytePos
-	for current.next != nil && n >= (current.count*blockBytes) { // Nil check for less than 32 addresses masks
-		n -= (current.count * blockBytes)
-		previous = current
-		current = current.next
-	}
 
-	// If byte is outside of the list, let caller know
-	if n >= (current.count * blockBytes) {
-		return nil, nil, 0, invalidPos
-	}
-
-	// Find the byte position inside the block and the number of blocks
-	// preceding the block containing the byte inside this sequence
-	precBlocks := n / blockBytes
-	inBlockBytePos := bytePos % blockBytes
-
-	return current, previous, precBlocks, inBlockBytePos
-}
-
-// PushReservation pushes the bit reservation inside the bitmask.
-// Given byte and bit positions, identify the sequence (current) which holds the block containing the affected bit.
-// Create a new block with the modified bit according to the operation (allocate/release).
-// Create a new sequence containing the new block and insert it in the proper position.
-// Remove current sequence if empty.
-// Check if new sequence can be merged with neighbour (previous/next) sequences.
-//
-// Identify "current" sequence containing block:
-//
-//	[prev seq] [current seq] [next seq]
-//
-// Based on block position, resulting list of sequences can be any of three forms:
-//
-// block position                        Resulting list of sequences
-//
-// A) block is first in current:         [prev seq] [new] [modified current seq] [next seq]
-// B) block is last in current:          [prev seq] [modified current seq] [new] [next seq]
-// C) block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [next seq]
-func pushReservation(bytePos, bitPos uint64, head *sequence, release bool) *sequence {
-	// Store list's head
-	newHead := head
-
-	// Find the sequence containing this byte
-	current, previous, precBlocks, inBlockBytePos := findSequence(head, bytePos)
-	if current == nil {
-		return newHead
-	}
-
-	// Construct updated block
-	bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
-	newBlock := current.block
-	if release {
-		newBlock &^= bitSel
-	} else {
-		newBlock |= bitSel
-	}
-
-	// Quit if it was a redundant request
-	if current.block == newBlock {
-		return newHead
-	}
-
-	// Current sequence inevitably looses one block, upadate count
-	current.count--
-
-	// Create new sequence
-	newSequence := &sequence{block: newBlock, count: 1}
-
-	// Insert the new sequence in the list based on block position
-	if precBlocks == 0 { // First in sequence (A)
-		newSequence.next = current
-		if current == head {
-			newHead = newSequence
-			previous = newHead
-		} else {
-			previous.next = newSequence
-		}
-		removeCurrentIfEmpty(&newHead, newSequence, current)
-		mergeSequences(previous)
-	} else if precBlocks == current.count { // Last in sequence (B)
-		newSequence.next = current.next
-		current.next = newSequence
-		mergeSequences(current)
-	} else { // In between the sequence (C)
-		currPre := &sequence{block: current.block, count: precBlocks, next: newSequence}
-		currPost := current
-		currPost.count -= precBlocks
-		newSequence.next = currPost
-		if currPost == head {
-			newHead = currPre
-		} else {
-			previous.next = currPre
-		}
-		// No merging or empty current possible here
-	}
-
-	return newHead
-}
-
-// Removes the current sequence from the list if empty, adjusting the head pointer if needed
-func removeCurrentIfEmpty(head **sequence, previous, current *sequence) {
-	if current.count == 0 {
-		if current == *head {
-			*head = current.next
-		} else {
-			previous.next = current.next
-		}
-	}
-}
-
-// Given a pointer to a sequence, it checks if it can be merged with any following sequences
-// It stops when no more merging is possible.
-// TODO: Optimization: only attempt merge from start to end sequence, no need to scan till the end of the list
-func mergeSequences(seq *sequence) {
-	if seq != nil {
-		// Merge all what possible from seq
-		for seq.next != nil && seq.block == seq.next.block {
-			seq.count += seq.next.count
-			seq.next = seq.next.next
-		}
-		// Move to next
-		mergeSequences(seq.next)
-	}
-}
-
-func getNumBlocks(numBits uint64) uint64 {
-	numBlocks := numBits / uint64(blockLen)
-	if numBits%uint64(blockLen) != 0 {
-		numBlocks++
+	h.Lock()
+	defer h.Unlock()
+	if err := h.bm.UnmarshalBinary(b); err != nil {
+		return err
 	}
-	return numBlocks
-}
-
-func ordinalToPos(ordinal uint64) (uint64, uint64) {
-	return ordinal / 8, ordinal % 8
-}
-
-func posToOrdinal(bytePos, bitPos uint64) uint64 {
-	return bytePos*8 + bitPos
+	return nil
 }

文件差异内容过多而无法显示
+ 21 - 1053
libnetwork/bitseq/sequence_test.go


+ 3 - 5
libnetwork/bitseq/store.go

@@ -3,6 +3,7 @@ package bitseq
 import (
 	"encoding/json"
 
+	"github.com/docker/docker/libnetwork/bitmap"
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/types"
 )
@@ -78,16 +79,13 @@ func (h *Handle) CopyTo(o datastore.KVObject) error {
 		return nil
 	}
 	dstH.Lock()
-	dstH.bits = h.bits
-	dstH.unselected = h.unselected
-	dstH.head = h.head.getCopy()
+	defer dstH.Unlock()
+	dstH.bm = bitmap.Copy(h.bm)
 	dstH.app = h.app
 	dstH.id = h.id
 	dstH.dbIndex = h.dbIndex
 	dstH.dbExists = h.dbExists
 	dstH.store = h.store
-	dstH.curr = h.curr
-	dstH.Unlock()
 
 	return nil
 }

部分文件因为文件数量过多而无法显示