geo icon indicating copy to clipboard operation
geo copied to clipboard

Deadlock during call to ShapeIndex.Iterator()

Open gaston-haro opened this issue 4 years ago • 8 comments

This is the call chain for (s *ShapeIndex)

Call to s.Iterator
--> s.maybeApplyUpdates()
  --> Acquires s.mu.Lock()
    --> s.applyUpdatesInternal()
      --> s.updateFaceEdges()
        --> s.skipCellRange()
          --> s.updateEdges()
            --> s.Iterator()
              --> s.maybeApplyUpdates()
                --> Tries to acquire s.mu.Lock() --> DEADLOCK

I'm using version v0.0.0-20200319012246-673a6f80352d

gaston-haro avatar Jul 09 '20 20:07 gaston-haro

Can you see if 050ea44 fixes this? If not, can you provide a reproduction case?

dsymonds avatar Jul 30 '20 02:07 dsymonds

I can check

gaston-haro avatar Jul 30 '20 11:07 gaston-haro

Problem still persists. To reproduce it is pretty simple:

index := s2.NewShapeIndex()
loop := s2.LoopFromPoints(points)
index.Add(loop)
index.Build()
other_loop := s2.LoopFromPoints(other_points)
index.Add(other_loop)
index.Build() // Deadlock

I'll provide full code later, I'm at work now. (I'm not sure if this is dependant on the shapes themselves yet)

PS: I really don't think any of the recent changes fix this because the call chain that I described originally remains a valid execution path.

gaston-haro avatar Jul 30 '20 13:07 gaston-haro

You are adding the same shape a second time to the index?

On Thu, Jul 30, 2020 at 6:31 AM Gastón Haro [email protected] wrote:

Problem still persists. To reproduce it is pretty simple:

index := s2.NewShapeIndex() loop := s2.LoopFromPoints(points) index.Add(loop) index.Build() other_loop := s2.LoopFromPoints(other_points) index.Add(other_loop) index.Add(loop) index.Build() // Deadlock

I'll provide full code later, I'm at work now. (I'm not sure if this is dependant on the shapes themselves yet)

— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/golang/geo/issues/67#issuecomment-666365976, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACBHGBBVS24MBPWVBHOHE3TR6FY2FANCNFSM4OV6WWUA .

rsned avatar Jul 30 '20 14:07 rsned

You are adding the same shape a second time to the index?

No, new (and different) shapes. PS: Sorry, I updated my previous comment there was an extra line. I see what you meant now.

gaston-haro avatar Jul 30 '20 15:07 gaston-haro

I'm running into this issue as well:

github.com/golang/geo/s2.(*ShapeIndex).maybeApplyUpdates(0xc0001903f0) /go/pkg/mod/github.com/golang/[email protected]/s2/shapeindex.go:796 +0x75 github.com/golang/geo/s2.(*ShapeIndex).Iterator(0xc0001903f0, 0x70) /go/pkg/mod/github.com/golang/[email protected]/s2/shapeindex.go:635 +0x3d github.com/golang/geo/s2.(*ShapeIndex).shrinkToFit(0xc0001903f0, 0xc003413ab0, 0xbfed3612dc5bff5d, 0xbfea84424eb463f1, 0xbfd3858dd3322fb9, 0xbfcea0866809b516, 0xbfcea9de6340bcac) /go/pkg/mod/github.com/golang/[email protected]/s2/shapeindex.go:947 +0xfb github.com/golang/geo/s2.(*ShapeIndex).updateFaceEdges(0xc0001903f0, 0x4, 0xc000594000, 0x4b, 0x92, 0xc002c87200) /go/pkg/mod/github.com/golang/[email protected]/s2/shapeindex.go:922 +0x612 github.com/golang/geo/s2.(*ShapeIndex).applyUpdatesInternal(0xc0001903f0) /go/pkg/mod/github.com/golang/[email protected]/s2/shapeindex.go:824 +0x1f6 github.com/golang/geo/s2.(*ShapeIndex).maybeApplyUpdates(0xc0001903f0) /go/pkg/mod/github.com/golang/[email protected]/s2/shapeindex.go:797 +0x83 github.com/golang/geo/s2.(*ShapeIndex).Iterator(0xc0001903f0, 0xc0011107f3349c2c) /go/pkg/mod/github.com/golang/[email protected]/s2/shapeindex.go:635 +0x3d

Even after patching the library to return an iterator without locking in the maybeApplyUpdates code path, Begin()-shapeindex.go:650 calls maybeApplyUpdates which will also cause a deadlock.

I'm open to submitting a fix, but it's not clear to me the best path forward. There's a TODO for replacing the mutex, so perhaps there's a planned update that will address this issue.

mdavis333 avatar Aug 27 '20 21:08 mdavis333

Sorry I haven't found time to submit a minimal working example to reproduce the bug, but the way to go is pretty clear. Even by doing code inspection you realize the possibility of deadlock.

gaston-haro avatar Aug 27 '20 21:08 gaston-haro

After quickly looking at this issue because I got hit by it myself, I found out that solving this is a bit more effort than just avoiding the deadlock: Subsequent calls to Build (or anything that triggers an update) may trigger a call to absorbIndexCell, which some steps down the call stack depends on the tracker's lowerBound. That's not yet implemented:

https://github.com/golang/geo/blob/740aa86cb551d6388f5cf4a8f39568d52fac6ed7/s2/shapeindex.go#L537-L540

But looking at the original C++ source code, it shouldn't be too hard to port it (?):

https://github.com/google/s2geometry/blob/20ea540d81f4575a3fc0aea585aac611bcd03ede/src/s2/mutable_s2shape_index.cc#L432-L440

hypirion avatar Jul 25 '21 16:07 hypirion