Skip to content

Commit

Permalink
Bug in Absorb because Object detected it was no longer valid
Browse files Browse the repository at this point in the history
  • Loading branch information
lkarlslund committed Oct 31, 2022
1 parent 42a40d9 commit 64e06f9
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 48 deletions.
105 changes: 58 additions & 47 deletions modules/engine/edgeconnplus.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import (
type EdgeConnectionsPlus struct {
backing unsafe.Pointer
mu sync.RWMutex
lastOp atomic.Uint32
growing atomic.Uint32
}

type Connections struct {
data []Connection
maxClean uint32 // Not atomic, this is always only being read (number of sorted items)
maxTotal atomic.Uint32 // Number of total items
deleted atomic.Uint32 // Number of deleted items
maxClean uint32 // Not atomic, this is always only being read (number of sorted items)
maxTotal uint32 // Number of total items
deleted uint32 // Number of deleted items
lookups uint32 // Number of lookups since created
}

type Connection struct {
Expand All @@ -42,7 +42,7 @@ func (e *EdgeConnectionsPlus) Range(af func(key *Object, value EdgeBitmap) bool)
if backing == nil {
return
}
last := int(backing.maxTotal.Load())
last := int(atomic.LoadUint32(&backing.maxTotal))
if last == 0 {
return
}
Expand All @@ -64,6 +64,8 @@ func (e *EdgeConnectionsPlus) getBacking() *Connections {
func (e *EdgeConnectionsPlus) search(wantedKey *Object) *Connection {
backing := e.getBacking()
if backing != nil {
atomic.AddUint32(&backing.lookups, 1)

uintWantedKey := uintptr(unsafe.Pointer(wantedKey))
n, found := sort.Find(int(backing.maxClean), func(i int) int {
foundKey := uintptr(unsafe.Pointer(backing.data[i].target))
Expand All @@ -72,7 +74,7 @@ func (e *EdgeConnectionsPlus) search(wantedKey *Object) *Connection {
if found {
return &backing.data[n]
}
max := backing.maxTotal.Load()
max := atomic.LoadUint32(&backing.maxTotal)
for i := backing.maxClean; i < max; i++ {
foundKey := backing.data[i].target
if foundKey == wantedKey {
Expand All @@ -87,46 +89,35 @@ func (e *EdgeConnectionsPlus) del(target *Object) {
e.rlock()
conn := e.search(target)
if conn != nil {
if conn.target != target {
panic("WRONG")
}
if atomic.CompareAndSwapUint32(&conn.alive, 1, 0) {
backing := e.getBacking()
backing.deleted.Add(1)

// if backing.deleted.Load() > backing.maxTotal.Load()/2 {
// e.runlock()
// e.lock()
// e.resize()
// e.unlock()
// return
// }
atomic.AddUint32(&backing.deleted, 1)
} else {
ui.Debug().Msg("Trying to delete edge that is already deleted")
backing := e.getBacking()
_ = backing
}
} else {
ui.Debug().Msg("Trying to delete edge that could not be found")
}
e.runlock()
e.autooptimize()
}

func (e *EdgeConnectionsPlus) Len() int {
backing := e.getBacking()
if backing == nil {
return 0
}
return int(backing.maxTotal.Load() - backing.deleted.Load())

return int(atomic.LoadUint32(&backing.maxTotal) - atomic.LoadUint32(&backing.deleted))
}

func (e *EdgeConnectionsPlus) PreciseLen() int {
func (e *EdgeConnectionsPlus) preciseLen() int {
backing := e.getBacking()
if backing == nil {
return 0
}
var length int
max := int(backing.maxTotal.Load())
max := int(atomic.LoadUint32(&backing.maxTotal))
for i := 0; i < max && i < len(backing.data); /* BCE */ i++ {
if atomic.LoadUint32(&backing.data[i].alive) == 1 {
length++
Expand Down Expand Up @@ -161,12 +152,12 @@ func (e *EdgeConnectionsPlus) modifyEdges(target *Object, mf func(edges *EdgeBit
if deleteIfBlank && connection.edges.IsBlank() {
if atomic.CompareAndSwapUint32(&connection.alive, 1, 0) {
backing := e.getBacking()
backing.deleted.Add(1)
atomic.AddUint32(&backing.deleted, 1)
}
} else {
if atomic.CompareAndSwapUint32(&connection.alive, 0, 1) {
backing := e.getBacking()
backing.deleted.Add(^uint32(0))
atomic.AddUint32(&backing.deleted, ^uint32(0))
}
}
e.runlock()
Expand All @@ -181,7 +172,7 @@ func (e *EdgeConnectionsPlus) modifyEdges(target *Object, mf func(edges *EdgeBit
oldBacking := e.getBacking()
var oldMax uint32
if oldBacking != nil {
oldMax = oldBacking.maxTotal.Load()
oldMax = atomic.LoadUint32(&oldBacking.maxTotal)
}

e.runlock()
Expand All @@ -192,7 +183,8 @@ func (e *EdgeConnectionsPlus) modifyEdges(target *Object, mf func(edges *EdgeBit
newBacking := e.getBacking()
if oldBacking == newBacking && newBacking != nil {
// Only a few was inserted, so just search those
newMax := newBacking.maxTotal.Load()
newMax := atomic.LoadUint32(&newBacking.maxTotal)

for i := oldMax; i < newMax; i++ {
if newBacking.data[i].target == target {
connection = &newBacking.data[i]
Expand All @@ -208,12 +200,12 @@ func (e *EdgeConnectionsPlus) modifyEdges(target *Object, mf func(edges *EdgeBit
if deleteIfBlank && connection.edges.IsBlank() {
if atomic.CompareAndSwapUint32(&connection.alive, 1, 0) {
backing := e.getBacking()
backing.deleted.Add(1)
atomic.AddUint32(&backing.deleted, 1)
}
} else {
if atomic.CompareAndSwapUint32(&connection.alive, 0, 1) {
backing := e.getBacking()
backing.deleted.Add(^uint32(0))
atomic.AddUint32(&backing.deleted, ^uint32(0))
}
}
e.unlock()
Expand All @@ -225,6 +217,7 @@ func (e *EdgeConnectionsPlus) modifyEdges(target *Object, mf func(edges *EdgeBit
e.insert(target, newedges)

e.unlock()
e.autooptimize()
}

func (e *EdgeConnectionsPlus) insert(target *Object, eb EdgeBitmap) {
Expand All @@ -236,18 +229,38 @@ func (e *EdgeConnectionsPlus) insert(target *Object, eb EdgeBitmap) {

backing := e.getBacking()

for backing == nil || int(backing.maxTotal.Load()) == len(backing.data) {
for backing == nil || int(atomic.LoadUint32(&backing.maxTotal)) == len(backing.data) {
e.maintainBacking(Grow)
backing = e.getBacking()
}

newMax := backing.maxTotal.Add(1)
newMax := atomic.AddUint32(&backing.maxTotal, 1)
backing.data[int(newMax-1)] = newConnection
}

func (e *EdgeConnectionsPlus) Minimize() {
func (e *EdgeConnectionsPlus) autooptimize() {
backing := e.getBacking()
if backing == nil {
return
}

dirtyCount := atomic.LoadUint32(&backing.maxTotal) - atomic.LoadUint32(&backing.maxClean)
lookups := atomic.LoadUint32(&backing.lookups)

if dirtyCount > 64 && lookups*dirtyCount > 1<<24 { // More than 1<<20 (16m) wasted loops
e.lock()
maybeNewbacking := e.getBacking()
if maybeNewbacking == backing {
// should we optimize?
e.maintainBacking(Same)
}
e.unlock()
}
}

func (e *EdgeConnectionsPlus) Optimize(requiredModification sizeModifierFlag) {
e.lock()
e.maintainBacking(Minimize)
e.maintainBacking(requiredModification)
e.unlock()
}

Expand All @@ -260,10 +273,17 @@ const (
)

func (e *EdgeConnectionsPlus) maintainBacking(requestedModification sizeModifierFlag) {
oldBacking := e.getBacking()

if requestedModification == Same && (oldBacking == nil || oldBacking.maxClean == oldBacking.maxTotal) {
// Already optimal
return
}

if !e.growing.CompareAndSwap(0, 1) {
panic("growing twice")
}
oldBacking := e.getBacking()

if oldBacking == nil {
// first time we're getting dirty around there
newBacking := Connections{
Expand All @@ -274,10 +294,9 @@ func (e *EdgeConnectionsPlus) maintainBacking(requestedModification sizeModifier
return
}

oldMax := int(oldBacking.maxTotal.Load())
oldMax := int(atomic.LoadUint32(&oldBacking.maxTotal))
oldClean := int(oldBacking.maxClean)
oldDeleted := int(oldBacking.deleted.Load())

oldDeleted := int(atomic.LoadUint32(&oldBacking.deleted))
var newLength int
switch requestedModification {
case Grow:
Expand Down Expand Up @@ -354,8 +373,8 @@ func (e *EdgeConnectionsPlus) maintainBacking(requestedModification sizeModifier
newBacking := Connections{
data: newData,
maxClean: uint32(insertEnd),
maxTotal: uint32(insertEnd),
}
newBacking.maxTotal.Store(newBacking.maxClean)

if !atomic.CompareAndSwapPointer(&e.backing, unsafe.Pointer(oldBacking), unsafe.Pointer(&newBacking)) {
panic("Backing was changed behind my back")
Expand All @@ -369,22 +388,14 @@ func (e *EdgeConnectionsPlus) maintainBacking(requestedModification sizeModifier
}

// Do a read lock
func (e *EdgeConnectionsPlus) rlock() uint32 {
func (e *EdgeConnectionsPlus) rlock() {
e.mu.RLock()
return e.lastOp.Load()
}

func (e *EdgeConnectionsPlus) runlock() {
e.mu.RUnlock()
}

// Upgrades lock and returns whether there was changes in the mean time
func (e *EdgeConnectionsPlus) upgradelock(lastOp uint32) bool {
e.mu.RUnlock()
e.mu.Lock()
return e.lastOp.Load() == lastOp
}

// Do a write lock
func (e *EdgeConnectionsPlus) lock() {
e.mu.Lock()
Expand Down
3 changes: 2 additions & 1 deletion modules/engine/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,6 @@ func (o *Object) OneAttrRendered(attr Attribute) string {

// Returns synthetic blank attribute value if it isn't set
func (o *Object) get(attr Attribute) (AttributeValues, bool) {
o.panicIfNotValid()
if attributenums[attr].onget != nil {
return attributenums[attr].onget(o, attr)
}
Expand All @@ -501,6 +500,7 @@ func (o *Object) get(attr Attribute) (AttributeValues, bool) {

// Auto locking version
func (o *Object) Get(attr Attribute) (AttributeValues, bool) {
o.panicIfNotValid()
return o.get(attr)
}

Expand All @@ -517,6 +517,7 @@ func (o *Object) attr(attr Attribute) AttributeValues {

// Returns synthetic blank attribute value if it isn't set
func (o *Object) Attr(attr Attribute) AttributeValues {
o.panicIfNotValid()
return o.attr(attr)
}

Expand Down

0 comments on commit 64e06f9

Please sign in to comment.