Skip to content

Commit

Permalink
把erx改为err以保持简洁
Browse files Browse the repository at this point in the history
  • Loading branch information
yangyile committed Dec 30, 2024
1 parent 97597cd commit dd4348a
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 52 deletions.
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,24 @@ func main() {

---

## Contributing
## License

Welcome to submit issues or requests for improvements!
`mutexmap` is open-source and released under the MIT License. See the [LICENSE](LICENSE) file for more information.

---
---

## Support

Welcome to contribute to this project by submitting pull requests or reporting issues.

If you find this package helpful, give it a star on GitHub!

**Thank you for your support!**

**Happy Coding with `mutexmap`!** 🎉

Give me stars. Thank you!!!

## Starring

If you find this package valuable, give it a ⭐ on GitHub! Thank you for your support!!!
[![starring](https://starchart.cc/yyle88/mutexmap.svg?variant=adaptive)](https://starchart.cc/yyle88/mutexmap)
14 changes: 12 additions & 2 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,18 @@ func main() {

---

## 许可

`mutexmap` 是一个开源项目,发布于 MIT 许可证下。有关更多信息,请参阅 [LICENSE](LICENSE) 文件。

## 贡献与支持

非常欢迎提交问题、建议或功能请求!!!
欢迎通过提交 pull request 或报告问题来贡献此项目。

如果你觉得这个包对你有帮助,请在 GitHub 上给个 ⭐,感谢支持!!!

**感谢你的支持!**

**祝编程愉快!** 🎉

如果觉得本库对您有帮助,请在 GitHub 上给个 ⭐,感谢支持!!!
Give me stars. Thank you!!!
40 changes: 20 additions & 20 deletions cachemap/cache_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type valueBottle[V any] struct {
mutex *sync.RWMutex
done bool //这里的done仅仅表示已经执行过,而不表示结果成功,有可能结果是失败的,但也标记为"done"
res V
erx error //即使是计算错误,也保存下来,再次遇到时直接返回错误
err error //即使是计算错误,也保存下来,再次遇到时直接返回错误
}

func NewMap[K comparable, V any](size int) *Map[K, V] {
Expand All @@ -26,7 +26,7 @@ func NewMap[K comparable, V any](size int) *Map[K, V] {
}
}

func (a *Map[K, V]) Get(k K) (res V, erx error, done bool) {
func (a *Map[K, V]) Get(k K) (res V, err error, done bool) {
a.mutex.RLock() //首先用读锁去试探性的读数据
defer a.mutex.RUnlock()
vx, ok := a.mp[k]
Expand All @@ -36,29 +36,29 @@ func (a *Map[K, V]) Get(k K) (res V, erx error, done bool) {
if !vx.done {
return res, errors.New("not acted"), false
}
return vx.res, vx.erx, true
return vx.res, vx.err, true
}

func (a *Map[K, V]) Set(k K, v V) {
a.SetKve(k, v, nil)
a.SetVe(k, v, nil)
}

func (a *Map[K, V]) SetKv(k K, v V) {
a.SetKve(k, v, nil)
a.SetVe(k, v, nil)
}

func (a *Map[K, V]) SetKe(k K, erx error) {
a.SetKve(k, mutexmap.Zero[V](), erx)
func (a *Map[K, V]) SetKe(k K, err error) {
a.SetVe(k, mutexmap.Zero[V](), err)
}

func (a *Map[K, V]) SetKve(k K, v V, erx error) {
func (a *Map[K, V]) SetVe(k K, v V, err error) {
a.mutex.Lock()
defer a.mutex.Unlock()
a.mp[k] = &valueBottle[V]{
mutex: &sync.RWMutex{}, //其实当设置done以后就不会再用锁啦,但没有关系的,依然写在这里避免疑惑和出错
done: true,
res: v,
erx: erx,
err: err,
}
}

Expand All @@ -68,8 +68,8 @@ func (a *Map[K, V]) SetKve(k K, v V, erx error) {
// so when already exist do not change the value, return the old value.
// Orz means "or" but "or" is too ugly, more ugly than ugly. So I use Orz instead of it.
func (a *Map[K, V]) Getset(k K, newValue func() (V, error)) (V, error) {
if res, erx, done := a.Get(k); done {
return res, erx
if res, err, done := a.Get(k); done {
return res, err
}
//在写锁内执行-因此在占用前有可能会执行别的操作-比如设置新值/删除值-设计时要考虑时序问题
vb, mu := a.setBottle(k)
Expand All @@ -81,24 +81,24 @@ func (a *Map[K, V]) Getset(k K, newValue func() (V, error)) (V, error) {
defer func() {
if ove := recover(); ove != nil {
if erp, ok := ove.(error); ok {
vb.erx = erp
vb.err = erp
} else {
vb.erx = errors.Errorf("panic occurred. reason: %v", ove)
vb.err = errors.Errorf("panic occurred. reason: %v", ove)
}
}
}()
vb.res, vb.erx = newValue() //接着去计算数据,最终释放壳子的写锁,确保一个资源只被计算一次
vb.res, vb.err = newValue() //接着去计算数据,最终释放壳子的写锁,确保一个资源只被计算一次
}()
vb.done = true
return vb.res, vb.erx
return vb.res, vb.err
} else {
if vb.done {
return vb.res, vb.erx
return vb.res, vb.err
} else {
vb.mutex.RLock() //抢占壳子的读锁,当能抢到的时候说明壳子已经有数据,抢不到说明还没计算完
defer vb.mutex.RUnlock()
//当抢到读锁时,说明前面的执行已经完成(done),结果已经被赋值到壳子里,就可以使用和返回
return vb.res, vb.erx
return vb.res, vb.err
}
}
}
Expand All @@ -113,7 +113,7 @@ func (a *Map[K, V]) setBottle(k K) (*valueBottle[V], *sync.RWMutex) {
mutex: &sync.RWMutex{},
done: false,
res: mutexmap.Zero[V](), // no need to set zero value
erx: nil, // no need to set none value
err: nil, // no need to set none value
}
a.mp[k] = vx
vx.mutex.Lock() //把这个壳给锁住,再开始计算数据,这时map的锁即可释放,而元素的锁将生效以确保需要同样元素的请求都得等待后面运算结束,即等待元素的释放锁
Expand All @@ -135,11 +135,11 @@ func (a *Map[K, V]) Len() int {
return len(a.mp)
}

func (a *Map[K, V]) Range(run func(k K, v V, erx error, done bool) bool) {
func (a *Map[K, V]) Range(run func(k K, v V, err error, done bool) bool) {
a.mutex.RLock()
defer a.mutex.RUnlock()
for k, v := range a.mp {
if ok := run(k, v.res, v.erx, v.done); !ok {
if ok := run(k, v.res, v.err, v.done); !ok {
return
}
}
Expand Down
52 changes: 26 additions & 26 deletions cachemap/cache_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ func TestNewMap(t *testing.T) {
for idx := 0; idx < 10; idx++ {
t.Log("(", idx, ")")
const constK = "abc"
res1, erx1 := cache.Getset(constK, calc)
t.Log(res1, erx1)
res2, erx2 := cache.Getset(constK, calc)
t.Log(res2, erx2)
res1, err1 := cache.Getset(constK, calc)
t.Log(res1, err1)
res2, err2 := cache.Getset(constK, calc)
t.Log(res2, err2)

if erx1 != nil || erx2 != nil {
require.ErrorIs(t, erx1, erx2)
if err1 != nil || err2 != nil {
require.ErrorIs(t, err1, err2)
}
require.Equal(t, res1, res2)

res3, erx3, done := cache.Get(constK)
res3, err3, done := cache.Get(constK)
require.True(t, done)
if erx2 != nil || erx3 != nil {
require.ErrorIs(t, erx2, erx3)
if err2 != nil || err3 != nil {
require.ErrorIs(t, err2, err3)
}
require.Equal(t, res2, res3)

Expand All @@ -44,7 +44,7 @@ func TestNewMap(t *testing.T) {
}
}

func TestMap_SetKve(t *testing.T) {
func TestMap_SetVe(t *testing.T) {
cache := NewMap[string, int](100)

calc := func() (int, error) {
Expand All @@ -58,35 +58,35 @@ func TestMap_SetKve(t *testing.T) {
t.Log("(", idx, ")")
const constK = "abc"
var res0 = rand.IntN(100)
var erx0 error
var err0 error
if res0 < 50 {
res0 = -1
erx0 = errors.New("wrong")
err0 = errors.New("wrong")
}
cache.SetKve(constK, res0, erx0)
cache.SetVe(constK, res0, err0)

res1, erx1 := cache.Getset(constK, calc)
t.Log(res1, erx1)
res1, err1 := cache.Getset(constK, calc)
t.Log(res1, err1)

if erx0 != nil || erx1 != nil {
t.Log(erx0)
t.Log(erx1)
require.ErrorIs(t, erx0, erx1)
if err0 != nil || err1 != nil {
t.Log(err0)
t.Log(err1)
require.ErrorIs(t, err0, err1)
}
require.Equal(t, res0, res1)

res2, erx2 := cache.Getset(constK, calc)
t.Log(res2, erx2)
res2, err2 := cache.Getset(constK, calc)
t.Log(res2, err2)

if erx1 != nil || erx2 != nil {
require.ErrorIs(t, erx1, erx2)
if err1 != nil || err2 != nil {
require.ErrorIs(t, err1, err2)
}
require.Equal(t, res1, res2)

res3, erx3, done := cache.Get(constK)
res3, err3, done := cache.Get(constK)
require.True(t, done)
if erx2 != nil || erx3 != nil {
require.ErrorIs(t, erx2, erx3)
if err2 != nil || err3 != nil {
require.ErrorIs(t, err2, err3)
}
require.Equal(t, res2, res3)

Expand Down

0 comments on commit dd4348a

Please sign in to comment.