Skip to content

Commit

Permalink
Preserve panics in create
Browse files Browse the repository at this point in the history
In 24e43a4 we fixed deadlocks when the passed create func panics, but we also converted any panic into a returned err.

Preserving the panic is in many situations useful.
  • Loading branch information
bep committed Oct 27, 2024
1 parent 24e43a4 commit a41a38c
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 15 deletions.
12 changes: 6 additions & 6 deletions lazycache.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package lazycache

import (
"fmt"
"sync"

"github.com/hashicorp/golang-lru/v2/simplelru"
Expand Down Expand Up @@ -122,23 +121,24 @@ func (c *Cache[K, V]) GetOrCreate(key K, create func(key K) (V, error)) (V, bool
c.lru.Add(key, w)
c.mu.Unlock()

isPanic := true

v, err := func() (v V, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
w.err = err
w.value = v
w.found = err == nil
w.found = err == nil && !isPanic
close(w.ready)

if err != nil {
if err != nil || isPanic {
v = c.zerov
c.Delete(key)
}
}()

// Create the value with the lock released.
v, err = create(key)
isPanic = false

return
}()
Expand Down
36 changes: 27 additions & 9 deletions lazycache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,29 @@ func TestPanic(t *testing.T) {
c := qt.New(t)

cache := New(Options[int, any]{MaxEntries: 1000})
ep := &errorProducer{}

willPanic := func(i int) func() {
return func() {
cache.GetOrCreate(i, func(key int) (any, error) {
ep.Panic(i)
return nil, nil
})
}
}

for i := 0; i < 2; i++ {
_, _, err := cache.GetOrCreate(42, func(key int) (any, error) {
panic("failed1")
})
c.Assert(err, qt.IsNotNil)
c.Assert(err, qt.ErrorMatches, "panic: failed1")
_, _, err = cache.GetOrCreate(i, func(key int) (any, error) {
panic("failed2")
for j := 0; j < 2; j++ {
c.Assert(willPanic(i), qt.PanicMatches, fmt.Sprintf("failed-%d", i))
}
}

for i := 0; i < 2; i++ {
v, _, err := cache.GetOrCreate(i, func(key int) (any, error) {
return key + 2, nil
})
c.Assert(err, qt.IsNotNil)
c.Assert(err, qt.ErrorMatches, "panic: failed2")
c.Assert(err, qt.IsNil)
c.Assert(v, qt.Equals, i+2)
}
}

Expand Down Expand Up @@ -461,3 +473,9 @@ func BenchmarkCacheParallel(b *testing.B) {
})
})
}

type errorProducer struct{}

func (e *errorProducer) Panic(i int) {
panic(fmt.Sprintf("failed-%d", i))
}

0 comments on commit a41a38c

Please sign in to comment.