| @@ -0,0 +1,117 @@ | |||||
| package io2 | |||||
| import ( | |||||
| "io" | |||||
| "sync" | |||||
| ) | |||||
| type RingBuffer2 struct { | |||||
| buf []byte | |||||
| src io.ReadCloser | |||||
| err error | |||||
| isReading bool | |||||
| writePos int // 指向下一次写入的位置,应该是一个空位 | |||||
| readPos int // 执行下一次读取的位置,应该是有效数据 | |||||
| waitReading *sync.Cond | |||||
| waitComsuming *sync.Cond | |||||
| UpstreamName string | |||||
| DownstreamName string | |||||
| } | |||||
| func RingBuffer(src io.ReadCloser, size int) io.ReadCloser { | |||||
| lk := &sync.Mutex{} | |||||
| return &RingBuffer2{ | |||||
| buf: make([]byte, size), | |||||
| src: src, | |||||
| waitReading: sync.NewCond(lk), | |||||
| waitComsuming: sync.NewCond(lk), | |||||
| } | |||||
| } | |||||
| func (r *RingBuffer2) Read(p []byte) (n int, err error) { | |||||
| r.waitReading.L.Lock() | |||||
| if !r.isReading { | |||||
| go r.reading() | |||||
| r.isReading = true | |||||
| } | |||||
| for r.writePos == r.readPos { | |||||
| if r.err != nil { | |||||
| r.waitReading.L.Unlock() | |||||
| return 0, r.err | |||||
| } | |||||
| // startTime := time.Now() | |||||
| r.waitReading.Wait() | |||||
| // fmt.Printf("%s wait data for %v\n", r.DownstreamName, time.Since(startTime)) | |||||
| } | |||||
| writePos := r.writePos | |||||
| readPos := r.readPos | |||||
| r.waitReading.L.Unlock() | |||||
| if readPos < writePos { | |||||
| n = copy(p, r.buf[readPos:writePos]) | |||||
| } else { | |||||
| n = copy(p, r.buf[readPos:]) | |||||
| } | |||||
| r.waitComsuming.L.Lock() | |||||
| r.readPos = (r.readPos + n) % len(r.buf) | |||||
| r.waitComsuming.L.Unlock() | |||||
| r.waitComsuming.Broadcast() | |||||
| err = nil | |||||
| return | |||||
| } | |||||
| func (r *RingBuffer2) Close() error { | |||||
| r.src.Close() | |||||
| r.waitComsuming.Broadcast() | |||||
| r.waitReading.Broadcast() | |||||
| return nil | |||||
| } | |||||
| func (r *RingBuffer2) reading() { | |||||
| defer r.src.Close() | |||||
| for { | |||||
| r.waitComsuming.L.Lock() | |||||
| // writePos不能和readPos重合,因为无法区分缓冲区是已经满了,还是完全是空的 | |||||
| // 所以writePos最多能到readPos的前一格 | |||||
| for r.writePos+1 == r.readPos { | |||||
| r.waitComsuming.Wait() | |||||
| if r.err != nil { | |||||
| return | |||||
| } | |||||
| } | |||||
| writePos := r.writePos | |||||
| readPos := r.readPos | |||||
| r.waitComsuming.L.Unlock() | |||||
| var n int | |||||
| var err error | |||||
| if readPos <= writePos { | |||||
| // 同上理,写入数据的时候如果readPos为0,则它的前一格是底层缓冲区的最后一格 | |||||
| // 那就不能写入到这一格 | |||||
| if readPos == 0 { | |||||
| n, err = r.src.Read(r.buf[writePos : len(r.buf)-1]) | |||||
| } else { | |||||
| n, err = r.src.Read(r.buf[writePos:]) | |||||
| } | |||||
| } else if readPos > writePos { | |||||
| n, err = r.src.Read(r.buf[writePos:readPos]) | |||||
| } | |||||
| // 无论成功还是失败,都发送一下信号通知读取端 | |||||
| r.waitReading.L.Lock() | |||||
| r.err = err | |||||
| r.writePos = (r.writePos + n) % len(r.buf) | |||||
| r.waitReading.L.Unlock() | |||||
| r.waitReading.Broadcast() | |||||
| if err != nil { | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,110 @@ | |||||
| package io2 | |||||
| import ( | |||||
| "bytes" | |||||
| "io" | |||||
| "testing" | |||||
| . "github.com/smartystreets/goconvey/convey" | |||||
| "gitlink.org.cn/cloudream/common/utils/sync2" | |||||
| ) | |||||
| type syncReader struct { | |||||
| data [][]byte | |||||
| curDataIndex int | |||||
| nextData int | |||||
| counter *sync2.CounterCond | |||||
| } | |||||
| func (r *syncReader) Read(p []byte) (n int, err error) { | |||||
| if r.nextData >= len(r.data) { | |||||
| return 0, io.EOF | |||||
| } | |||||
| if r.data[r.nextData] == nil { | |||||
| r.counter.Wait() | |||||
| r.nextData++ | |||||
| } | |||||
| n = copy(p, r.data[r.nextData][r.curDataIndex:]) | |||||
| r.curDataIndex += n | |||||
| if r.curDataIndex == len(r.data[r.nextData]) { | |||||
| r.curDataIndex = 0 | |||||
| r.nextData++ | |||||
| } | |||||
| return n, nil | |||||
| } | |||||
| func (r *syncReader) Close() error { | |||||
| return nil | |||||
| } | |||||
| func Test_RingBuffer(t *testing.T) { | |||||
| Convey("写满读满", t, func() { | |||||
| b := RingBuffer(io.NopCloser(bytes.NewBuffer([]byte{1, 2, 3})), 4) | |||||
| ret := make([]byte, 3) | |||||
| n, err := b.Read(ret) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(n, ShouldEqual, 3) | |||||
| So(ret, ShouldResemble, []byte{1, 2, 3}) | |||||
| }) | |||||
| Convey("1+3+1", t, func() { | |||||
| sy := sync2.NewCounterCond(0) | |||||
| b := RingBuffer(&syncReader{ | |||||
| data: [][]byte{ | |||||
| {1}, | |||||
| nil, | |||||
| {2, 3, 4, 5}, | |||||
| }, | |||||
| counter: sy, | |||||
| }, 4) | |||||
| ret := make([]byte, 3) | |||||
| n, err := b.Read(ret) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(n, ShouldEqual, 1) | |||||
| So(ret[:n], ShouldResemble, []byte{1}) | |||||
| sy.Release() | |||||
| n, err = b.Read(ret) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(n, ShouldEqual, 3) | |||||
| So(ret[:n], ShouldResemble, []byte{2, 3, 4}) | |||||
| n, err = b.Read(ret) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(n, ShouldEqual, 1) | |||||
| So(ret[:n], ShouldResemble, []byte{5}) | |||||
| }) | |||||
| Convey("3+1+2", t, func() { | |||||
| sy := sync2.NewCounterCond(0) | |||||
| b := RingBuffer(&syncReader{ | |||||
| data: [][]byte{ | |||||
| {1, 2, 3, 4, 5, 6}, | |||||
| }, | |||||
| counter: sy, | |||||
| }, 4) | |||||
| ret := make([]byte, 3) | |||||
| n, err := b.Read(ret) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(n, ShouldEqual, 3) | |||||
| So(ret[:n], ShouldResemble, []byte{1, 2, 3}) | |||||
| n, err = b.Read(ret) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(n, ShouldEqual, 1) | |||||
| So(ret[:n], ShouldResemble, []byte{4}) | |||||
| n, err = b.Read(ret) | |||||
| So(err, ShouldEqual, nil) | |||||
| So(n, ShouldEqual, 2) | |||||
| So(ret[:n], ShouldResemble, []byte{5, 6}) | |||||
| }) | |||||
| } | |||||
| @@ -27,3 +27,27 @@ func ParallelDo[T any](args []T, fn func(val T, index int) error) error { | |||||
| return err | return err | ||||
| } | } | ||||
| func ParallelDoMap[K comparable, V any](args map[K]V, fn func(k K, v V) error) error { | |||||
| lock := sync.Mutex{} | |||||
| var err error | |||||
| var wg sync.WaitGroup | |||||
| wg.Add(len(args)) | |||||
| for k, v := range args { | |||||
| go func(k K, v V) { | |||||
| defer wg.Done() | |||||
| if e := fn(k, v); e != nil { | |||||
| lock.Lock() | |||||
| if err == nil { | |||||
| err = e | |||||
| } | |||||
| lock.Unlock() | |||||
| } | |||||
| }(k, v) | |||||
| } | |||||
| wg.Wait() | |||||
| return err | |||||
| } | |||||