重构代码
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
package runtimex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStackClosed = errors.New("stack closed")
|
||||
ErrStackFull = errors.New("stack full")
|
||||
)
|
||||
|
||||
type ChanStack struct {
|
||||
data chan interface{}
|
||||
cap uint64
|
||||
current uint64
|
||||
isClose atomic.Value
|
||||
}
|
||||
|
||||
func NewChanStack(cap uint64) *ChanStack {
|
||||
rtnBuffer := new(ChanStack)
|
||||
rtnBuffer.cap = cap
|
||||
rtnBuffer.isClose.Store(false)
|
||||
rtnBuffer.data = make(chan interface{}, cap)
|
||||
return rtnBuffer
|
||||
}
|
||||
|
||||
func (s *ChanStack) init() {
|
||||
s.cap = 1024
|
||||
s.data = make(chan interface{}, s.cap)
|
||||
s.isClose.Store(false)
|
||||
}
|
||||
|
||||
func (s *ChanStack) Free() uint64 {
|
||||
return s.cap - atomic.LoadUint64(&s.current)
|
||||
}
|
||||
|
||||
func (s *ChanStack) Cap() uint64 {
|
||||
return s.cap
|
||||
}
|
||||
|
||||
func (s *ChanStack) Len() uint64 {
|
||||
return atomic.LoadUint64(&s.current)
|
||||
}
|
||||
|
||||
func (s *ChanStack) Pop() (interface{}, error) {
|
||||
if s.isClose.Load() == nil {
|
||||
s.init()
|
||||
}
|
||||
if s.isClose.Load().(bool) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
data, ok := <-s.data
|
||||
if !ok {
|
||||
s.isClose.Store(true)
|
||||
return nil, io.EOF
|
||||
}
|
||||
for {
|
||||
current := atomic.LoadUint64(&s.current)
|
||||
if current == 0 {
|
||||
break
|
||||
}
|
||||
if atomic.CompareAndSwapUint64(&s.current, current, current-1) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *ChanStack) Push(data interface{}) error {
|
||||
if s.isClose.Load() == nil {
|
||||
s.init()
|
||||
}
|
||||
if s.isClose.Load().(bool) {
|
||||
return io.EOF
|
||||
}
|
||||
if err := func() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = io.EOF
|
||||
}
|
||||
}()
|
||||
s.data <- data
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
current := atomic.LoadUint64(&s.current)
|
||||
if atomic.CompareAndSwapUint64(&s.current, current, current+1) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ChanStack) TryPush(data interface{}) error {
|
||||
if s.isClose.Load() == nil {
|
||||
s.init()
|
||||
}
|
||||
if s.isClose.Load().(bool) {
|
||||
return io.EOF
|
||||
}
|
||||
if err := func() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = io.EOF
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case s.data <- data:
|
||||
return nil
|
||||
default:
|
||||
return ErrStackFull
|
||||
}
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
current := atomic.LoadUint64(&s.current)
|
||||
if atomic.CompareAndSwapUint64(&s.current, current, current+1) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ChanStack) Close() error {
|
||||
if s.isClose.Load() == nil {
|
||||
s.init()
|
||||
}
|
||||
if s.isClose.Load().(bool) {
|
||||
return ErrStackClosed
|
||||
}
|
||||
s.isClose.Store(true)
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
close(s.data)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package runtimex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChanStackPushPop(t *testing.T) {
|
||||
stack := NewChanStack(2)
|
||||
if err := stack.Push("a"); err != nil {
|
||||
t.Fatalf("Push failed: %v", err)
|
||||
}
|
||||
if err := stack.Push("b"); err != nil {
|
||||
t.Fatalf("Push failed: %v", err)
|
||||
}
|
||||
if stack.Len() != 2 {
|
||||
t.Fatalf("expected len=2, got %d", stack.Len())
|
||||
}
|
||||
if stack.Free() != 0 {
|
||||
t.Fatalf("expected free=0, got %d", stack.Free())
|
||||
}
|
||||
|
||||
first, err := stack.Pop()
|
||||
if err != nil {
|
||||
t.Fatalf("Pop failed: %v", err)
|
||||
}
|
||||
if first.(string) != "a" {
|
||||
t.Fatalf("unexpected first value: %v", first)
|
||||
}
|
||||
second, err := stack.Pop()
|
||||
if err != nil {
|
||||
t.Fatalf("Pop failed: %v", err)
|
||||
}
|
||||
if second.(string) != "b" {
|
||||
t.Fatalf("unexpected second value: %v", second)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChanStackTryPushFull(t *testing.T) {
|
||||
stack := NewChanStack(1)
|
||||
if err := stack.TryPush("a"); err != nil {
|
||||
t.Fatalf("TryPush should succeed on empty stack: %v", err)
|
||||
}
|
||||
if err := stack.TryPush("b"); !errors.Is(err, ErrStackFull) {
|
||||
t.Fatalf("TryPush should return ErrStackFull, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChanStackCloseBehavior(t *testing.T) {
|
||||
stack := NewChanStack(1)
|
||||
if err := stack.Close(); err != nil {
|
||||
t.Fatalf("Close should succeed first time: %v", err)
|
||||
}
|
||||
if err := stack.Close(); !errors.Is(err, ErrStackClosed) {
|
||||
t.Fatalf("Close should return ErrStackClosed on second call, got %v", err)
|
||||
}
|
||||
if err := stack.Push("x"); !errors.Is(err, io.EOF) {
|
||||
t.Fatalf("Push after close should return io.EOF, got %v", err)
|
||||
}
|
||||
if _, err := stack.Pop(); !errors.Is(err, io.EOF) {
|
||||
t.Fatalf("Pop after close should return io.EOF, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package runtimex
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type MapKV struct {
|
||||
kvMap map[interface{}]interface{}
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMapKV() MapKV {
|
||||
var mp MapKV
|
||||
mp.kvMap = make(map[interface{}]interface{})
|
||||
return mp
|
||||
}
|
||||
|
||||
func (m *MapKV) Get(key interface{}) (interface{}, error) {
|
||||
var err error
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data, ok := m.kvMap[key]
|
||||
if !ok {
|
||||
err = os.ErrNotExist
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (m *MapKV) MustGet(key interface{}) interface{} {
|
||||
result, _ := m.Get(key)
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *MapKV) Store(key interface{}, value interface{}) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.kvMap[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MapKV) Exists(key interface{}) bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
_, ok := m.kvMap[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *MapKV) Delete(key interface{}) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.kvMap, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MapKV) Range(run func(k interface{}, v interface{}) bool) error {
|
||||
for k, v := range m.kvMap {
|
||||
if !run(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user