github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/internal/ackseq/ackseq.go (about) 1 // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package ackseq 6 7 import ( 8 "fmt" 9 "sync" 10 "sync/atomic" 11 ) 12 13 const ( 14 // The window size constants. These values specify a window that can hold ~1m 15 // pending unacknowledged sequence numbers using 128 KB of memory. 16 windowSize = 1 << 20 17 windowMask = windowSize - 1 18 windowBytes = (windowSize + 7) / 8 19 ) 20 21 // S keeps track of the largest sequence number such that all sequence numbers 22 // in the range [0,v) have been acknowledged. 23 type S struct { 24 next uint64 25 mu struct { 26 sync.Mutex 27 base uint64 28 window [windowBytes]uint8 29 } 30 } 31 32 // New creates a new acknowledged sequence tracker with the specified base 33 // sequence number. All of the sequence numbers in the range [0,base) are 34 // considered acknowledged. Next() will return base upon first call. 35 func New(base uint64) *S { 36 s := &S{ 37 next: base, 38 } 39 s.mu.base = base 40 return s 41 } 42 43 // Next returns the next sequence number to use. 44 func (s *S) Next() uint64 { 45 return atomic.AddUint64(&s.next, 1) - 1 46 } 47 48 // Ack acknowledges the specified seqNum, adjusting base as necessary, 49 // returning the number of newly acknowledged sequence numbers. 50 func (s *S) Ack(seqNum uint64) (int, error) { 51 s.mu.Lock() 52 if s.getLocked(seqNum) { 53 defer s.mu.Unlock() 54 return 0, fmt.Errorf( 55 "pending acks exceeds window size: %d has been acked, but %d has not", 56 seqNum, s.mu.base) 57 } 58 59 var count int 60 s.setLocked(seqNum) 61 for s.getLocked(s.mu.base) { 62 s.clearLocked(s.mu.base) 63 s.mu.base++ 64 count++ 65 } 66 s.mu.Unlock() 67 return count, nil 68 } 69 70 func (s *S) getLocked(seqNum uint64) bool { 71 bit := seqNum & windowMask 72 return (s.mu.window[bit/8] & (1 << (bit % 8))) != 0 73 } 74 75 func (s *S) setLocked(seqNum uint64) { 76 bit := seqNum & windowMask 77 s.mu.window[bit/8] |= (1 << (bit % 8)) 78 } 79 80 func (s *S) clearLocked(seqNum uint64) { 81 bit := seqNum & windowMask 82 s.mu.window[bit/8] &^= (1 << (bit % 8)) 83 }