Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

w3test #19

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions internal/singleflight/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package singleflight

import "sync"

type Map[K comparable, V any] struct {
mux sync.Mutex
m map[K]*call[V]
}

func New[K comparable, V any]() *Map[K, V] {
return &Map[K, V]{
m: make(map[K]*call[V]),
}
}

type call[V any] struct {
once sync.Once

val V
err error
}

type GetFunc[V any] func() (V, error)

func (c *call[V]) Get(fn GetFunc[V]) (V, error) {
c.once.Do(func() {
c.val, c.err = fn()
})
return c.val, c.err
}

func (m *Map[K, V]) Do(key K, fn GetFunc[V]) (V, error) {
m.mux.Lock()
c, ok := m.m[key]
if !ok {
c = &call[V]{}
m.m[key] = c
}
m.mux.Unlock()

return c.Get(fn)
}

// Clear removes all entries from m, leaving it empty.
func (m *Map[K, V]) Clear() {
m.mux.Lock()
defer m.mux.Unlock()

for key := range m.m {
delete(m.m, key)
}
}
74 changes: 74 additions & 0 deletions internal/singleflight/map_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package singleflight

import (
"testing"
"time"
)

func TestMap(t *testing.T) {
t.Run("", func(t *testing.T) {
m := New[int, int]()

val, err := m.Do(1, fetch(1))
if err != nil || val != 1 {
t.Fatal()
}

val, err = m.Do(1, func() (int, error) {
t.Fatal("value should be cached")
return 1, nil
})
if err != nil || val != 1 {
t.Fatal()
}
})

t.Run("", func(t *testing.T) {
m := New[int, int]()

for i := 0; i < 10; i++ {
go func() {
val, err := m.Do(1, fetch(1))
if err != nil || val != 1 {
t.Fail()
}
}()
}
time.Sleep(10 * time.Millisecond)
val, err := m.Do(1, func() (int, error) {
t.Fatal("value should be cached")
return 1, nil
})
if err != nil || val != 1 {
t.Fatal()
}
})
}

func TestMapClear(t *testing.T) {
m := New[int, int]()

for i := 0; i < 10; i++ {
go func(i int) {
val, err := m.Do(i, fetch(1))
if err != nil || val != 1 {
t.Fail()
}
}(i)
}

time.Sleep(10 * time.Millisecond)
if len(m.m) != 10 {
t.Fatal()
}
m.Clear()
if len(m.m) != 0 {
t.Fatal()
}
}

func fetch(i int) func() (int, error) {
return func() (int, error) {
return i, nil
}
}
70 changes: 70 additions & 0 deletions w3test/multi_tracer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package w3test

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)

// multiEVMLogger is a wrapper around multiple EVMLogger's.
type multiEVMLogger []vm.EVMLogger

func newMultiEVMLogger(tracers []vm.EVMLogger) vm.EVMLogger {
switch len(tracers) {
case 0:
return nil
case 1:
return tracers[0]
default:
return multiEVMLogger(tracers)
}
}

func (m multiEVMLogger) CaptureTxStart(gasLimit uint64) {
for _, tracer := range m {
tracer.CaptureTxStart(gasLimit)
}
}

func (m multiEVMLogger) CaptureTxEnd(restGas uint64) {
for _, tracer := range m {
tracer.CaptureTxEnd(restGas)
}
}

func (m multiEVMLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
for _, tracer := range m {
tracer.CaptureStart(env, from, to, create, input, gas, value)
}
}

func (m multiEVMLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
for _, tracer := range m {
tracer.CaptureEnd(output, gasUsed, err)
}
}

func (m multiEVMLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
for _, tracer := range m {
tracer.CaptureEnter(typ, from, to, input, gas, value)
}
}

func (m multiEVMLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
for _, tracer := range m {
tracer.CaptureExit(output, gasUsed, err)
}
}

func (m multiEVMLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
for _, tracer := range m {
tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
}
}

func (m multiEVMLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
for _, tracer := range m {
tracer.CaptureFault(pc, op, gas, cost, scope, depth, err)
}
}