github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/soliton/bitmap/concurrent.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package bitmap 15 16 import ( 17 "sync/atomic" 18 "unsafe" 19 ) 20 21 const ( 22 segmentWidth = 32 23 segmentWidthPower = 5 24 bitMask uint32 = 0x80000000 25 ) 26 27 var bytesConcurrentBitmap = int64(int(unsafe.Sizeof(ConcurrentBitmap{}))) 28 29 // ConcurrentBitmap is a static-length bitmap which is thread-safe on setting. 30 // It is implemented using CAS, as atomic bitwise operation is not supported by 31 // golang yet. (See https://github.com/golang/go/issues/24244) 32 // CAS operation is narrowed down to uint32 instead of longer types like uint64, 33 // to reduce probability of racing. 34 type ConcurrentBitmap struct { 35 segments []uint32 36 bitLen int 37 } 38 39 // NewConcurrentBitmap initializes a ConcurrentBitmap which can causetstore 40 // bitLen of bits. 41 func NewConcurrentBitmap(bitLen int) *ConcurrentBitmap { 42 segmentLen := (bitLen + segmentWidth - 1) >> segmentWidthPower 43 return &ConcurrentBitmap{ 44 segments: make([]uint32, segmentLen), 45 bitLen: bitLen, 46 } 47 } 48 49 // BytesConsumed returns size of this bitmap in bytes. 50 func (cb *ConcurrentBitmap) BytesConsumed() int64 { 51 return bytesConcurrentBitmap + int64(segmentWidth/8*cap(cb.segments)) 52 } 53 54 // Set sets the bit on bitIndex to be 1 (bitIndex starts from 0). 55 // isSetter indicates whether the function call this time triggers the bit from 0 to 1. 56 // bitIndex bigger than bitLen initialized will be ignored. 57 func (cb *ConcurrentBitmap) Set(bitIndex int) (isSetter bool) { 58 if bitIndex < 0 || bitIndex >= cb.bitLen { 59 return 60 } 61 62 var oldValue, newValue uint32 63 segmentPointer := &(cb.segments[bitIndex>>segmentWidthPower]) 64 mask := bitMask >> uint32(bitIndex%segmentWidth) 65 // Repeatedly observe whether bit is already set, and try to set 66 // it based on observation. 67 for { 68 // Observe. 69 oldValue = atomic.LoadUint32(segmentPointer) 70 if (oldValue & mask) != 0 { 71 return 72 } 73 74 // Set. 75 newValue = oldValue | mask 76 isSetter = atomic.CompareAndSwapUint32(segmentPointer, oldValue, newValue) 77 if isSetter { 78 return 79 } 80 } 81 } 82 83 // UnsafeIsSet returns if a bit on bitIndex is set (bitIndex starts from 0). 84 // bitIndex bigger than bitLen initialized will return false. 85 // This method is not thread-safe as it does not use atomic load. 86 func (cb *ConcurrentBitmap) UnsafeIsSet(bitIndex int) (isSet bool) { 87 if bitIndex < 0 || bitIndex >= cb.bitLen { 88 return 89 } 90 91 mask := bitMask >> uint32(bitIndex%segmentWidth) 92 isSet = ((cb.segments[bitIndex>>segmentWidthPower] & mask) != 0) 93 return 94 }