Add a ForEach() to bitmap allocator
This commit is contained in:
		@@ -26,5 +26,5 @@ go_test(
 | 
				
			|||||||
    ],
 | 
					    ],
 | 
				
			||||||
    library = "go_default_library",
 | 
					    library = "go_default_library",
 | 
				
			||||||
    tags = ["automanaged"],
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
    deps = [],
 | 
					    deps = ["//vendor:k8s.io/client-go/pkg/util/sets"],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,6 +124,33 @@ func (r *AllocationBitmap) Release(offset int) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// Find the size of a big.Word in bytes.
 | 
				
			||||||
 | 
						notZero   = uint64(^big.Word(0))
 | 
				
			||||||
 | 
						wordPower = (notZero>>8)&1 + (notZero>>16)&1 + (notZero>>32)&1
 | 
				
			||||||
 | 
						wordSize  = 1 << wordPower
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ForEach calls the provided function for each allocated bit.  The
 | 
				
			||||||
 | 
					// AllocationBitmap may not be modified while this loop is running.
 | 
				
			||||||
 | 
					func (r *AllocationBitmap) ForEach(fn func(int)) {
 | 
				
			||||||
 | 
						r.lock.Lock()
 | 
				
			||||||
 | 
						defer r.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						words := r.allocated.Bits()
 | 
				
			||||||
 | 
						for wordIdx, word := range words {
 | 
				
			||||||
 | 
							bit := 0
 | 
				
			||||||
 | 
							for word > 0 {
 | 
				
			||||||
 | 
								if (word & 1) != 0 {
 | 
				
			||||||
 | 
									fn((wordIdx * wordSize * 8) + bit)
 | 
				
			||||||
 | 
									word = word &^ 1
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								bit++
 | 
				
			||||||
 | 
								word = word >> 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Has returns true if the provided item is already allocated and a call
 | 
					// Has returns true if the provided item is already allocated and a call
 | 
				
			||||||
// to Allocate(offset) would fail.
 | 
					// to Allocate(offset) would fail.
 | 
				
			||||||
func (r *AllocationBitmap) Has(offset int) bool {
 | 
					func (r *AllocationBitmap) Has(offset int) bool {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,8 @@ package allocator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/client-go/pkg/util/sets"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAllocate(t *testing.T) {
 | 
					func TestAllocate(t *testing.T) {
 | 
				
			||||||
@@ -82,6 +84,37 @@ func TestRelease(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestForEach(t *testing.T) {
 | 
				
			||||||
 | 
						testCases := []sets.Int{
 | 
				
			||||||
 | 
							sets.NewInt(),
 | 
				
			||||||
 | 
							sets.NewInt(0),
 | 
				
			||||||
 | 
							sets.NewInt(0, 2, 5, 9),
 | 
				
			||||||
 | 
							sets.NewInt(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, tc := range testCases {
 | 
				
			||||||
 | 
							m := NewAllocationMap(10, "test")
 | 
				
			||||||
 | 
							for offset := range tc {
 | 
				
			||||||
 | 
								if ok, _ := m.Allocate(offset); !ok {
 | 
				
			||||||
 | 
									t.Errorf("[%d] error allocate offset %v", i, offset)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if !m.Has(offset) {
 | 
				
			||||||
 | 
									t.Errorf("[%d] expect offset %v allocated", i, offset)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							calls := sets.NewInt()
 | 
				
			||||||
 | 
							m.ForEach(func(i int) {
 | 
				
			||||||
 | 
								calls.Insert(i)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if len(calls) != len(tc) {
 | 
				
			||||||
 | 
								t.Errorf("[%d] expected %d calls, got %d", i, len(tc), len(calls))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !calls.Equal(tc) {
 | 
				
			||||||
 | 
								t.Errorf("[%d] expected calls to equal testcase: %v vs %v", i, calls.List(), tc.List())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestSnapshotAndRestore(t *testing.T) {
 | 
					func TestSnapshotAndRestore(t *testing.T) {
 | 
				
			||||||
	offset := 3
 | 
						offset := 3
 | 
				
			||||||
	m := NewAllocationMap(10, "test")
 | 
						m := NewAllocationMap(10, "test")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,6 +144,12 @@ func (e *Etcd) Release(item int) error {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *Etcd) ForEach(fn func(int)) {
 | 
				
			||||||
 | 
						e.lock.Lock()
 | 
				
			||||||
 | 
						defer e.lock.Unlock()
 | 
				
			||||||
 | 
						e.alloc.ForEach(fn)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// tryUpdate performs a read-update to persist the latest snapshot state of allocation.
 | 
					// tryUpdate performs a read-update to persist the latest snapshot state of allocation.
 | 
				
			||||||
func (e *Etcd) tryUpdate(fn func() error) error {
 | 
					func (e *Etcd) tryUpdate(fn func() error) error {
 | 
				
			||||||
	err := e.storage.GuaranteedUpdate(context.TODO(), e.baseKey, &api.RangeAllocation{}, true, nil,
 | 
						err := e.storage.GuaranteedUpdate(context.TODO(), e.baseKey, &api.RangeAllocation{}, true, nil,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ type Interface interface {
 | 
				
			|||||||
	Allocate(int) (bool, error)
 | 
						Allocate(int) (bool, error)
 | 
				
			||||||
	AllocateNext() (int, bool, error)
 | 
						AllocateNext() (int, bool, error)
 | 
				
			||||||
	Release(int) error
 | 
						Release(int) error
 | 
				
			||||||
 | 
						ForEach(func(int))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// For testing
 | 
						// For testing
 | 
				
			||||||
	Has(int) bool
 | 
						Has(int) bool
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,6 @@ go_library(
 | 
				
			|||||||
        "//pkg/apis/storage/v1beta1/util:go_default_library",
 | 
					        "//pkg/apis/storage/v1beta1/util:go_default_library",
 | 
				
			||||||
        "//pkg/client/clientset_generated/clientset:go_default_library",
 | 
					        "//pkg/client/clientset_generated/clientset:go_default_library",
 | 
				
			||||||
        "//pkg/labels:go_default_library",
 | 
					        "//pkg/labels:go_default_library",
 | 
				
			||||||
        "//pkg/registry/core/service/allocator:go_default_library",
 | 
					 | 
				
			||||||
        "//pkg/types:go_default_library",
 | 
					        "//pkg/types:go_default_library",
 | 
				
			||||||
        "//pkg/util/exec:go_default_library",
 | 
					        "//pkg/util/exec:go_default_library",
 | 
				
			||||||
        "//pkg/util/mount:go_default_library",
 | 
					        "//pkg/util/mount:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,8 +25,6 @@ package glusterfs
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/registry/core/service/allocator"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -51,7 +49,11 @@ var _ Rangeable = &MinMaxAllocator{}
 | 
				
			|||||||
// Rangeable is an Interface that can adjust its min/max range.
 | 
					// Rangeable is an Interface that can adjust its min/max range.
 | 
				
			||||||
// Rangeable should be threadsafe
 | 
					// Rangeable should be threadsafe
 | 
				
			||||||
type Rangeable interface {
 | 
					type Rangeable interface {
 | 
				
			||||||
	allocator.Interface
 | 
						Allocate(int) (bool, error)
 | 
				
			||||||
 | 
						AllocateNext() (int, bool, error)
 | 
				
			||||||
 | 
						Release(int) error
 | 
				
			||||||
 | 
						Has(int) bool
 | 
				
			||||||
 | 
						Free() int
 | 
				
			||||||
	SetRange(min, max int) error
 | 
						SetRange(min, max int) error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user