From 462363c8e42f43a4b5eb40255950c517e87e7837 Mon Sep 17 00:00:00 2001 From: RelicOfTesla Date: Sun, 15 Jan 2023 01:35:44 +0800 Subject: [PATCH] RemoveListener support closure function; #9 --- listener.go | 5 +- listener_remove_test.go | 131 ++++++++++++++++++++++++++++++++++++++++ listener_utils.go | 54 +++++++++++++++++ listener_utils_test.go | 77 +++++++++++++++++++++++ 4 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 listener_remove_test.go create mode 100644 listener_utils.go create mode 100644 listener_utils_test.go diff --git a/listener.go b/listener.go index 6a28f32..cf1caca 100644 --- a/listener.go +++ b/listener.go @@ -1,7 +1,6 @@ package event import ( - "fmt" "sort" ) @@ -88,11 +87,11 @@ func (lq *ListenerQueue) Remove(listener Listener) { } // unsafe.Pointer(listener) - ptrVal := fmt.Sprintf("%p", listener) + ptrVal := getListenCompareKey(listener) var newItems []*ListenerItem for _, li := range lq.items { - liPtrVal := fmt.Sprintf("%p", li.Listener) + liPtrVal := getListenCompareKey(li.Listener) if liPtrVal == ptrVal { continue } diff --git a/listener_remove_test.go b/listener_remove_test.go new file mode 100644 index 0000000..e5b31d7 --- /dev/null +++ b/listener_remove_test.go @@ -0,0 +1,131 @@ +package event + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +type globalTestVal struct { + n int + sum int +} +type testListenerCalc struct { + bind int + owner *globalTestVal +} + +func (l testListenerCalc) Handle(e Event) error { + l.owner.n++ + l.owner.sum += l.bind + return nil +} + +func Test_RemoveListener(t *testing.T) { + t.Run("", func(t *testing.T) { + global := &globalTestVal{} + makeFn := func(a int) ListenerFunc { + return func(e Event) error { + global.n++ + global.sum += a + return nil + } + } + + evBus := NewManager("") + const evName = "ev1" + + f1 := makeFn(11) + f2 := makeFn(22) + f3 := &testListenerCalc{bind: 33, owner: global} + + evBus.On(evName, f1) + evBus.On(evName, f2) + evBus.On(evName, f3) + + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 3) + require.Equal(t, global.sum, 66) //11+22+33=66 + + evBus.RemoveListener(evName, f1) + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 5) + require.Equal(t, global.sum, 121) // 66+22+33=121 + + evBus.RemoveListener(evName, f3) + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 6) + require.Equal(t, global.sum, 143) // 121+22=143 + }) + t.Run("", func(t *testing.T) { + global := &globalTestVal{} + f1 := testListenerCalc{bind: 11, owner: global} + f2 := testListenerCalc{bind: 22, owner: global} + f2same := testListenerCalc{bind: 22, owner: global} + f2copy := f2 // testListenerCalc{bind: 22, owner: global} + + evBus := NewManager("") + const evName = "ev1" + evBus.On(evName, f1) + evBus.On(evName, f2) + evBus.On(evName, f2same) + evBus.On(evName, f2copy) + + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 4) + require.Equal(t, global.sum, 77) //11+22+22+22=77 + + evBus.RemoveListener(evName, f1) + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 7) + require.Equal(t, global.sum, 143) // 77+22+22+22=143 + + evBus.RemoveListener(evName, f2) + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 7) + require.Equal(t, global.sum, 143) // + }) + t.Run("", func(t *testing.T) { + global := &globalStatic + + f1 := ListenerFunc(testFuncCalc1) + f2 := ListenerFunc(testFuncCalc2) + f2same := ListenerFunc(testFuncCalc2) + f2copy := f2 + + evBus := NewManager("") + const evName = "ev1" + evBus.On(evName, f1) + evBus.On(evName, f2) + evBus.On(evName, f2same) + evBus.On(evName, f2copy) + + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 4) + require.Equal(t, global.sum, 77) //11+22+22+22=77 + + evBus.RemoveListener(evName, f1) + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 7) + require.Equal(t, global.sum, 143) // 77+22+22+22=143 + + evBus.RemoveListener(evName, f2) + evBus.MustFire(evName, nil) + require.Equal(t, global.n, 7) + require.Equal(t, global.sum, 143) // + }) +} + +var globalStatic = globalTestVal{} + +func testFuncCalc1(e Event) error { + globalStatic.n++ + globalStatic.sum += 11 + return nil +} +func testFuncCalc2(e Event) error { + globalStatic.n++ + globalStatic.sum += 22 + return nil +} + +/// diff --git a/listener_utils.go b/listener_utils.go new file mode 100644 index 0000000..25ecca7 --- /dev/null +++ b/listener_utils.go @@ -0,0 +1,54 @@ +package event + +import ( + "fmt" + "reflect" + "unsafe" +) + +func getReflectRawPointer[T any](src T) uintptr { + //if src == nil { + // return 0 + //} + v := reflect.ValueOf(src) + switch v.Kind() { + case reflect.Func: + return getNativePointer(v) + case reflect.Pointer, reflect.Chan, reflect.Map, reflect.UnsafePointer: + return v.Pointer() + default: + return 0 + } +} + +// only validate in reflect.Pointer, reflect.Chan, reflect.Map, reflect.UnsafePointer, reflect.Func +func getNativePointer[T any](src T) uintptr { + vv := (*emptyInterface)(unsafe.Pointer(&src)) + return uintptr(vv.word) +} + +// source code from reflect.ValueOf() +type emptyInterface struct { + typ *struct{} //*rtype + word unsafe.Pointer +} + +type anyRawValue struct { + strData string + ptrData uintptr +} + +func getAnyRawValue[T any](src T) anyRawValue { + ret := anyRawValue{ + ptrData: getReflectRawPointer(src), + } + if ret.ptrData == 0 { + //ret.strData = fmt.Sprintf("%v", src) + ret.strData = fmt.Sprintf("%p", any(src)) + } + return ret +} + +func getListenCompareKey(src Listener) anyRawValue { + return getAnyRawValue(src) +} diff --git a/listener_utils_test.go b/listener_utils_test.go new file mode 100644 index 0000000..df6be06 --- /dev/null +++ b/listener_utils_test.go @@ -0,0 +1,77 @@ +package event + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func Test_getListenCompareKey(t *testing.T) { + t.Run("ptr", func(t *testing.T) { + makeFn := func(a int) ListenerFunc { + return func(e Event) error { + _ = a + return nil + } + } + + f1 := makeFn(11) + f2 := makeFn(22) + f2same := makeFn(22) + f2copy := f2 + require.NotEqual(t, getListenCompareKey(f1), getListenCompareKey(f2)) + require.NotEqual(t, getListenCompareKey(f2), getListenCompareKey(f2same)) + require.NotEqual(t, getListenCompareKey(f2same), getListenCompareKey(f2copy)) + require.Equal(t, getListenCompareKey(f2copy), getListenCompareKey(f2)) + + f3ptr := &testListener{userData: "3"} + ptr4 := &testListener{userData: "4"} + ptr4Same := &testListener{userData: "4"} + ptr4Copy := ptr4 + + require.NotEqual(t, getListenCompareKey(f3ptr), getListenCompareKey(ptr4)) + require.NotEqual(t, getListenCompareKey(ptr4), getListenCompareKey(ptr4Same)) + require.NotEqual(t, getListenCompareKey(ptr4Same), getListenCompareKey(ptr4Copy)) + require.Equal(t, getListenCompareKey(ptr4Copy), getListenCompareKey(ptr4Copy)) + + }) + + t.Run("same value", func(t *testing.T) { + + f5struct := testListenerReadOnly{userData: "5"} + struct6 := testListenerReadOnly{userData: "6"} + struct6same := testListenerReadOnly{userData: "6"} + struct6copy := struct6 + + require.NotEqual(t, getListenCompareKey(f5struct), getListenCompareKey(struct6)) + require.Equal(t, getListenCompareKey(struct6), getListenCompareKey(struct6same)) // equal when struct data + require.Equal(t, getListenCompareKey(struct6same), getListenCompareKey(struct6copy)) // equal when struct data + require.Equal(t, getListenCompareKey(struct6copy), getListenCompareKey(struct6)) + + f7func := ListenerFunc(testEmptyFunc1) + func8 := ListenerFunc(testEmptyFunc2) + func8same := ListenerFunc(testEmptyFunc2) + func8copy := func8 + + require.NotEqual(t, getListenCompareKey(f7func), getListenCompareKey(func8)) + require.Equal(t, getListenCompareKey(func8), getListenCompareKey(func8same)) // equal when struct data + require.Equal(t, getListenCompareKey(func8same), getListenCompareKey(func8copy)) // equal when struct data + require.Equal(t, getListenCompareKey(func8copy), getListenCompareKey(func8)) + + }) + +} + +func testEmptyFunc1(e Event) error { + return nil +} +func testEmptyFunc2(e Event) error { + return nil +} + +type testListenerReadOnly struct { + userData string +} + +func (l testListenerReadOnly) Handle(e Event) error { + return nil +}