Commit 88aa51ec authored by Kirill Smelkov's avatar Kirill Smelkov

X goodbye xsync

There it was errgroup.Group adjusted with Gox for functions with
exceptions, but exceptions usage should be constrained to tests only and
it is easy to do with just Go(exc.Funcx(...)) explicitly or via local
gox function as syntax sugar.
parent b9bd0b73
...@@ -30,12 +30,13 @@ import ( ...@@ -30,12 +30,13 @@ import (
"testing" "testing"
"time" "time"
"lab.nexedi.com/kirr/neo/go/zodb" "golang.org/x/sync/errgroup"
"lab.nexedi.com/kirr/neo/go/xcommon/xsync"
"lab.nexedi.com/kirr/go123/exc" "lab.nexedi.com/kirr/go123/exc"
"lab.nexedi.com/kirr/go123/xerr" "lab.nexedi.com/kirr/go123/xerr"
"lab.nexedi.com/kirr/neo/go/zodb"
"github.com/kylelemons/godebug/pretty" "github.com/kylelemons/godebug/pretty"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
...@@ -78,6 +79,10 @@ func xhandshake(ctx context.Context, c net.Conn, version uint32) { ...@@ -78,6 +79,10 @@ func xhandshake(ctx context.Context, c net.Conn, version uint32) {
exc.Raiseif(err) exc.Raiseif(err)
} }
func gox(wg interface { Go(func() error) }, xf func()) {
wg.Go(exc.Funcx(xf))
}
// xlinkError verifies that err is *LinkError and returns err.Err // xlinkError verifies that err is *LinkError and returns err.Err
func xlinkError(err error) error { func xlinkError(err error) error {
le, ok := err.(*LinkError) le, ok := err.(*LinkError)
...@@ -175,8 +180,8 @@ func TestNodeLink(t *testing.T) { ...@@ -175,8 +180,8 @@ func TestNodeLink(t *testing.T) {
//println("000") //println("000")
// Close vs recvPkt // Close vs recvPkt
nl1, nl2 := _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend) nl1, nl2 := _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend)
wg := &xsync.WorkGroup{} wg := &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
xclose(nl1) xclose(nl1)
}) })
...@@ -189,8 +194,8 @@ func TestNodeLink(t *testing.T) { ...@@ -189,8 +194,8 @@ func TestNodeLink(t *testing.T) {
// Close vs sendPkt // Close vs sendPkt
nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend) nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
xclose(nl1) xclose(nl1)
}) })
...@@ -204,8 +209,8 @@ func TestNodeLink(t *testing.T) { ...@@ -204,8 +209,8 @@ func TestNodeLink(t *testing.T) {
// {Close,CloseAccept} vs Accept // {Close,CloseAccept} vs Accept
nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend) nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
xclose(nl2) xclose(nl2)
}) })
...@@ -213,7 +218,7 @@ func TestNodeLink(t *testing.T) { ...@@ -213,7 +218,7 @@ func TestNodeLink(t *testing.T) {
if !(c == nil && xlinkError(err) == ErrLinkClosed) { if !(c == nil && xlinkError(err) == ErrLinkClosed) {
t.Fatalf("NodeLink.Accept() after close: conn = %v, err = %v", c, err) t.Fatalf("NodeLink.Accept() after close: conn = %v, err = %v", c, err)
} }
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
nl1.CloseAccept() nl1.CloseAccept()
}) })
...@@ -233,8 +238,8 @@ func TestNodeLink(t *testing.T) { ...@@ -233,8 +238,8 @@ func TestNodeLink(t *testing.T) {
//println("111") //println("111")
// Close vs recvPkt on another side // Close vs recvPkt on another side
nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend) nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
xclose(nl2) xclose(nl2)
}) })
...@@ -247,8 +252,8 @@ func TestNodeLink(t *testing.T) { ...@@ -247,8 +252,8 @@ func TestNodeLink(t *testing.T) {
// Close vs sendPkt on another side // Close vs sendPkt on another side
nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend) nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
xclose(nl2) xclose(nl2)
}) })
...@@ -265,15 +270,15 @@ func TestNodeLink(t *testing.T) { ...@@ -265,15 +270,15 @@ func TestNodeLink(t *testing.T) {
// raw exchange // raw exchange
nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend) nl1, nl2 = _nodeLinkPipe(linkNoRecvSend, linkNoRecvSend)
wg, ctx := xsync.WorkGroupCtx(context.Background()) wg, ctx := errgroup.WithContext(context.Background())
wg.Gox(func() { gox(wg, func() {
// send ping; wait for pong // send ping; wait for pong
pkt := _mkpkt(1, 2, []byte("ping")) pkt := _mkpkt(1, 2, []byte("ping"))
xsendPkt(nl1, pkt) xsendPkt(nl1, pkt)
pkt = xrecvPkt(nl1) pkt = xrecvPkt(nl1)
xverifyPkt(pkt, 3, 4, []byte("pong")) xverifyPkt(pkt, 3, 4, []byte("pong"))
}) })
wg.Gox(func() { gox(wg, func() {
// wait for ping; send pong // wait for ping; send pong
pkt = xrecvPkt(nl2) pkt = xrecvPkt(nl2)
xverifyPkt(pkt, 1, 2, []byte("ping")) xverifyPkt(pkt, 1, 2, []byte("ping"))
...@@ -282,8 +287,8 @@ func TestNodeLink(t *testing.T) { ...@@ -282,8 +287,8 @@ func TestNodeLink(t *testing.T) {
}) })
// close nodelinks either when checks are done, or upon first error // close nodelinks either when checks are done, or upon first error
wgclose := &xsync.WorkGroup{} wgclose := &errgroup.Group{}
wgclose.Gox(func() { gox(wgclose, func() {
<-ctx.Done() <-ctx.Done()
xclose(nl1) xclose(nl1)
xclose(nl2) xclose(nl2)
...@@ -299,8 +304,8 @@ func TestNodeLink(t *testing.T) { ...@@ -299,8 +304,8 @@ func TestNodeLink(t *testing.T) {
// Close vs recvPkt // Close vs recvPkt
nl1, nl2 = _nodeLinkPipe(0, linkNoRecvSend) nl1, nl2 = _nodeLinkPipe(0, linkNoRecvSend)
c = xnewconn(nl1) c = xnewconn(nl1)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
xclose(c) xclose(c)
}) })
...@@ -317,8 +322,8 @@ func TestNodeLink(t *testing.T) { ...@@ -317,8 +322,8 @@ func TestNodeLink(t *testing.T) {
// Close vs sendPkt // Close vs sendPkt
nl1, nl2 = _nodeLinkPipe(0, linkNoRecvSend) nl1, nl2 = _nodeLinkPipe(0, linkNoRecvSend)
c = xnewconn(nl1) c = xnewconn(nl1)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
tdelay() tdelay()
xclose(c) xclose(c)
}) })
...@@ -334,14 +339,14 @@ func TestNodeLink(t *testing.T) { ...@@ -334,14 +339,14 @@ func TestNodeLink(t *testing.T) {
// NodeLink.Close vs Conn.sendPkt/recvPkt // NodeLink.Close vs Conn.sendPkt/recvPkt
c11 := xnewconn(nl1) c11 := xnewconn(nl1)
c12 := xnewconn(nl1) c12 := xnewconn(nl1)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
pkt, err := c11.recvPkt() pkt, err := c11.recvPkt()
if !(pkt == nil && xconnError(err) == ErrLinkClosed) { if !(pkt == nil && xconnError(err) == ErrLinkClosed) {
exc.Raisef("Conn.recvPkt() after NodeLink close: pkt = %v err = %v", pkt, err) exc.Raisef("Conn.recvPkt() after NodeLink close: pkt = %v err = %v", pkt, err)
} }
}) })
wg.Gox(func() { gox(wg, func() {
pkt := c12.mkpkt(0, []byte("data")) pkt := c12.mkpkt(0, []byte("data"))
err := c12.sendPkt(pkt) err := c12.sendPkt(pkt)
if xconnError(err) != ErrLinkClosed { if xconnError(err) != ErrLinkClosed {
...@@ -362,9 +367,9 @@ func TestNodeLink(t *testing.T) { ...@@ -362,9 +367,9 @@ func TestNodeLink(t *testing.T) {
c21 := xnewconn(nl2) c21 := xnewconn(nl2)
c22 := xnewconn(nl2) c22 := xnewconn(nl2)
c23 := xnewconn(nl2) c23 := xnewconn(nl2)
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
var errRecv error var errRecv error
wg.Gox(func() { gox(wg, func() {
pkt, err := c21.recvPkt() pkt, err := c21.recvPkt()
want1 := io.EOF // if recvPkt wakes up due to peer close want1 := io.EOF // if recvPkt wakes up due to peer close
want2 := io.ErrClosedPipe // if recvPkt wakes up due to sendPkt wakes up first and closes nl1 want2 := io.ErrClosedPipe // if recvPkt wakes up due to sendPkt wakes up first and closes nl1
...@@ -375,7 +380,7 @@ func TestNodeLink(t *testing.T) { ...@@ -375,7 +380,7 @@ func TestNodeLink(t *testing.T) {
errRecv = cerr errRecv = cerr
}) })
wg.Gox(func() { gox(wg, func() {
pkt := c22.mkpkt(0, []byte("data")) pkt := c22.mkpkt(0, []byte("data"))
err := c22.sendPkt(pkt) err := c22.sendPkt(pkt)
want := io.ErrClosedPipe // always this in both due to peer close or recvPkt waking up and closing nl2 want := io.ErrClosedPipe // always this in both due to peer close or recvPkt waking up and closing nl2
...@@ -384,7 +389,7 @@ func TestNodeLink(t *testing.T) { ...@@ -384,7 +389,7 @@ func TestNodeLink(t *testing.T) {
} }
}) })
wg.Gox(func() { gox(wg, func() {
conn, err := nl2.Accept() conn, err := nl2.Accept()
if !(conn == nil && xlinkError(err) == ErrLinkDown) { if !(conn == nil && xlinkError(err) == ErrLinkDown) {
exc.Raisef("Accept after peer NodeLink shutdown: conn = %v err = %v", conn, err) exc.Raisef("Accept after peer NodeLink shutdown: conn = %v err = %v", conn, err)
...@@ -484,9 +489,9 @@ func TestNodeLink(t *testing.T) { ...@@ -484,9 +489,9 @@ func TestNodeLink(t *testing.T) {
// Conn accept + exchange // Conn accept + exchange
nl1, nl2 = nodeLinkPipe() nl1, nl2 = nodeLinkPipe()
nl1.CloseAccept() nl1.CloseAccept()
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
closed := make(chan int) closed := make(chan int)
wg.Gox(func() { gox(wg, func() {
c := xaccept(nl2) c := xaccept(nl2)
pkt := xrecvPkt(c) pkt := xrecvPkt(c)
...@@ -604,7 +609,7 @@ func TestNodeLink(t *testing.T) { ...@@ -604,7 +609,7 @@ func TestNodeLink(t *testing.T) {
// test 2 channels with replies coming in reversed time order // test 2 channels with replies coming in reversed time order
nl1, nl2 = nodeLinkPipe() nl1, nl2 = nodeLinkPipe()
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
replyOrder := map[uint16]struct { // "order" in which to process requests replyOrder := map[uint16]struct { // "order" in which to process requests
start chan struct{} // processing starts when start chan is ready start chan struct{} // processing starts when start chan is ready
next uint16 // after processing this switch to next next uint16 // after processing this switch to next
...@@ -614,11 +619,11 @@ func TestNodeLink(t *testing.T) { ...@@ -614,11 +619,11 @@ func TestNodeLink(t *testing.T) {
} }
close(replyOrder[2].start) close(replyOrder[2].start)
wg.Gox(func() { gox(wg, func() {
for _ = range replyOrder { for _ = range replyOrder {
c := xaccept(nl2) c := xaccept(nl2)
wg.Gox(func() { gox(wg, func() {
pkt := xrecvPkt(c) pkt := xrecvPkt(c)
n := ntoh16(pkt.Header().MsgCode) n := ntoh16(pkt.Header().MsgCode)
x := replyOrder[n] x := replyOrder[n]
...@@ -662,11 +667,11 @@ func TestHandshake(t *testing.T) { ...@@ -662,11 +667,11 @@ func TestHandshake(t *testing.T) {
bg := context.Background() bg := context.Background()
// handshake ok // handshake ok
p1, p2 := net.Pipe() p1, p2 := net.Pipe()
wg := &xsync.WorkGroup{} wg := &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
xhandshake(bg, p1, 1) xhandshake(bg, p1, 1)
}) })
wg.Gox(func() { gox(wg, func() {
xhandshake(bg, p2, 1) xhandshake(bg, p2, 1)
}) })
xwait(wg) xwait(wg)
...@@ -676,11 +681,11 @@ func TestHandshake(t *testing.T) { ...@@ -676,11 +681,11 @@ func TestHandshake(t *testing.T) {
// version mismatch // version mismatch
p1, p2 = net.Pipe() p1, p2 = net.Pipe()
var err1, err2 error var err1, err2 error
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
err1 = handshake(bg, p1, 1) err1 = handshake(bg, p1, 1)
}) })
wg.Gox(func() { gox(wg, func() {
err2 = handshake(bg, p2, 2) err2 = handshake(bg, p2, 2)
}) })
xwait(wg) xwait(wg)
...@@ -700,11 +705,11 @@ func TestHandshake(t *testing.T) { ...@@ -700,11 +705,11 @@ func TestHandshake(t *testing.T) {
// tx & rx problem // tx & rx problem
p1, p2 = net.Pipe() p1, p2 = net.Pipe()
err1, err2 = nil, nil err1, err2 = nil, nil
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
err1 = handshake(bg, p1, 1) err1 = handshake(bg, p1, 1)
}) })
wg.Gox(func() { gox(wg, func() {
xclose(p2) xclose(p2)
}) })
xwait(wg) xwait(wg)
...@@ -719,7 +724,7 @@ func TestHandshake(t *testing.T) { ...@@ -719,7 +724,7 @@ func TestHandshake(t *testing.T) {
// ctx cancel // ctx cancel
p1, p2 = net.Pipe() p1, p2 = net.Pipe()
ctx, cancel := context.WithCancel(bg) ctx, cancel := context.WithCancel(bg)
wg.Gox(func() { gox(wg, func() {
err1 = handshake(ctx, p1, 1) err1 = handshake(ctx, p1, 1)
}) })
tdelay() tdelay()
...@@ -770,9 +775,9 @@ func TestRecv1Mode(t *testing.T) { ...@@ -770,9 +775,9 @@ func TestRecv1Mode(t *testing.T) {
//println("000") //println("000")
// Send1 // Send1
nl1, nl2 := nodeLinkPipe() nl1, nl2 := nodeLinkPipe()
wg := &xsync.WorkGroup{} wg := &errgroup.Group{}
sync := make(chan int) sync := make(chan int)
wg.Gox(func() { gox(wg, func() {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
panic(e) panic(e)
...@@ -822,8 +827,8 @@ func TestRecv1Mode(t *testing.T) { ...@@ -822,8 +827,8 @@ func TestRecv1Mode(t *testing.T) {
//println("111\n") //println("111\n")
// Recv1: further packets with same connid are rejected with "connection closed" // Recv1: further packets with same connid are rejected with "connection closed"
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
c := xnewconn(nl2) c := xnewconn(nl2)
//println("aaa") //println("aaa")
...@@ -852,11 +857,11 @@ func TestRecv1Mode(t *testing.T) { ...@@ -852,11 +857,11 @@ func TestRecv1Mode(t *testing.T) {
// bug triggers under -race // bug triggers under -race
func TestLightCloseVsLinkShutdown(t *testing.T) { func TestLightCloseVsLinkShutdown(t *testing.T) {
nl1, nl2 := nodeLinkPipe() nl1, nl2 := nodeLinkPipe()
wg := &xsync.WorkGroup{} wg := &errgroup.Group{}
c := xnewconn(nl1) c := xnewconn(nl1)
nl1.shutdown() nl1.shutdown()
wg.Gox(func() { gox(wg, func() {
c.lightClose() c.lightClose()
}) })
...@@ -1202,13 +1207,13 @@ func benchmarkLinkRTT(b *testing.B, l1, l2 *NodeLink) { ...@@ -1202,13 +1207,13 @@ func benchmarkLinkRTT(b *testing.B, l1, l2 *NodeLink) {
func xlinkPipe(c1, c2 net.Conn) (*NodeLink, *NodeLink) { func xlinkPipe(c1, c2 net.Conn) (*NodeLink, *NodeLink) {
var l1, l2 *NodeLink var l1, l2 *NodeLink
wg := &xsync.WorkGroup{} wg := &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
l, err := Handshake(context.Background(), c1, LinkClient) l, err := Handshake(context.Background(), c1, LinkClient)
exc.Raiseif(err) exc.Raiseif(err)
l1 = l l1 = l
}) })
wg.Gox(func() { gox(wg, func() {
l, err := Handshake(context.Background(), c2, LinkServer) l, err := Handshake(context.Background(), c2, LinkServer)
exc.Raiseif(err) exc.Raiseif(err)
l2 = l l2 = l
......
...@@ -43,7 +43,6 @@ import ( ...@@ -43,7 +43,6 @@ import (
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/zodb/storage/fs1" "lab.nexedi.com/kirr/neo/go/zodb/storage/fs1"
"lab.nexedi.com/kirr/neo/go/xcommon/xsync"
"lab.nexedi.com/kirr/neo/go/xcommon/xtesting" "lab.nexedi.com/kirr/neo/go/xcommon/xtesting"
"lab.nexedi.com/kirr/go123/exc" "lab.nexedi.com/kirr/go123/exc"
...@@ -62,6 +61,10 @@ func xwait(w interface { Wait() error }) { ...@@ -62,6 +61,10 @@ func xwait(w interface { Wait() error }) {
exc.Raiseif(err) exc.Raiseif(err)
} }
func gox(wg interface { Go(func() error) }, xf func()) {
wg.Go(exc.Funcx(xf))
}
func xfs1stor(path string) *fs1.FileStorage { func xfs1stor(path string) *fs1.FileStorage {
zstor, err := fs1.Open(bg, path) zstor, err := fs1.Open(bg, path)
exc.Raiseif(err) exc.Raiseif(err)
...@@ -227,14 +230,14 @@ func TestMasterStorage(t *testing.T) { ...@@ -227,14 +230,14 @@ func TestMasterStorage(t *testing.T) {
Shost := xnet.NetTrace(net.Host("s"), tracer) Shost := xnet.NetTrace(net.Host("s"), tracer)
Chost := xnet.NetTrace(net.Host("c"), tracer) Chost := xnet.NetTrace(net.Host("c"), tracer)
gwg := &xsync.WorkGroup{} gwg := &errgroup.Group{}
// start master // start master
Mclock := &vclock{} Mclock := &vclock{}
M := NewMaster("abc1", ":1", Mhost) M := NewMaster("abc1", ":1", Mhost)
M.monotime = Mclock.monotime M.monotime = Mclock.monotime
Mctx, Mcancel := context.WithCancel(bg) Mctx, Mcancel := context.WithCancel(bg)
gwg.Gox(func() { gox(gwg, func() {
err := M.Run(Mctx) err := M.Run(Mctx)
fmt.Println("M err: ", err) fmt.Println("M err: ", err)
exc.Raiseif(err) exc.Raiseif(err)
...@@ -251,7 +254,7 @@ func TestMasterStorage(t *testing.T) { ...@@ -251,7 +254,7 @@ func TestMasterStorage(t *testing.T) {
zstor := xfs1stor("../../zodb/storage/fs1/testdata/1.fs") zstor := xfs1stor("../../zodb/storage/fs1/testdata/1.fs")
S := NewStorage("abc1", "m:1", ":1", Shost, zstor) S := NewStorage("abc1", "m:1", ":1", Shost, zstor)
Sctx, Scancel := context.WithCancel(bg) Sctx, Scancel := context.WithCancel(bg)
gwg.Gox(func() { gox(gwg, func() {
err := S.Run(Sctx) err := S.Run(Sctx)
fmt.Println("S err: ", err) fmt.Println("S err: ", err)
exc.Raiseif(err) exc.Raiseif(err)
...@@ -301,8 +304,8 @@ func TestMasterStorage(t *testing.T) { ...@@ -301,8 +304,8 @@ func TestMasterStorage(t *testing.T) {
tc.Expect(masterStartReady(M, true)) tc.Expect(masterStartReady(M, true))
// M <- start cmd // M <- start cmd
wg := &xsync.WorkGroup{} wg := &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
err := M.Start() err := M.Start()
exc.Raiseif(err) exc.Raiseif(err)
}) })
...@@ -406,8 +409,8 @@ func TestMasterStorage(t *testing.T) { ...@@ -406,8 +409,8 @@ func TestMasterStorage(t *testing.T) {
// C asks M about last tid XXX better master sends it itself on new client connected // C asks M about last tid XXX better master sends it itself on new client connected
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
wg.Gox(func() { gox(wg, func() {
cLastTid, err := C.LastTid(bg) cLastTid, err := C.LastTid(bg)
exc.Raiseif(err) exc.Raiseif(err)
...@@ -424,11 +427,11 @@ func TestMasterStorage(t *testing.T) { ...@@ -424,11 +427,11 @@ func TestMasterStorage(t *testing.T) {
xwait(wg) xwait(wg)
// C starts loading first object ... // C starts loading first object ...
wg = &xsync.WorkGroup{} wg = &errgroup.Group{}
xid1 := zodb.Xid{Oid: 1, XTid: zodb.XTid{Tid: zodb.TidMax, TidBefore: true}} xid1 := zodb.Xid{Oid: 1, XTid: zodb.XTid{Tid: zodb.TidMax, TidBefore: true}}
buf1, serial1, err := zstor.Load(bg, xid1) buf1, serial1, err := zstor.Load(bg, xid1)
exc.Raiseif(err) exc.Raiseif(err)
wg.Gox(func() { gox(wg, func() {
buf, serial, err := C.Load(bg, xid1) buf, serial, err := C.Load(bg, xid1)
exc.Raiseif(err) exc.Raiseif(err)
......
...@@ -26,7 +26,8 @@ import ( ...@@ -26,7 +26,8 @@ import (
//"log" //"log"
) )
// SeqReaderAt implements buffering for a io.ReaderAt optimized for sequential access // SeqReaderAt implements buffering for a io.ReaderAt optimized for sequential access.
//
// Both forward, backward and interleaved forward/backward access patterns are supported // Both forward, backward and interleaved forward/backward access patterns are supported
// //
// NOTE SeqReaderAt is not safe to use from multiple goroutines concurrently. // NOTE SeqReaderAt is not safe to use from multiple goroutines concurrently.
......
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package xsync provides addons to packages "sync" and "golang.org/x/sync".
package xsync
import (
"context"
"golang.org/x/sync/errgroup"
"lab.nexedi.com/kirr/go123/exc"
)
// WorkGroup is like x/sync/errgroup.Group but also supports exceptions
type WorkGroup struct {
errgroup.Group
}
// Gox calls the given function in a new goroutine and handles exceptions
//
// it translates exception raised, if any, to as if it was regular error
// returned for a function under Go call.
//
// see errgroup.Group.Go documentation for details on how error from spawned
// goroutines are handled group-wise.
func (g *WorkGroup) Gox(xf func()) {
g.Go(func() error {
return exc.Runx(xf)
})
}
// WorkGroupCtx returns new WorkGroup and associated context derived from ctx
// see errgroup.WithContext for semantic description and details.
func WorkGroupCtx(ctx context.Context) (*WorkGroup, context.Context) {
g, ctx := errgroup.WithContext(ctx)
return &WorkGroup{*g}, ctx
}
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package xsync
import (
"context"
"testing"
"lab.nexedi.com/kirr/go123/exc"
"lab.nexedi.com/kirr/go123/my"
)
func TestWorkGroup(t *testing.T) {
g := WorkGroup{}
g.Gox(func() {
exc.Raise(1)
})
err := g.Wait()
e, ok := err.(*exc.Error)
want := my.FuncName() + ".func1: 1"
if !(ok && e.Error() == want) {
t.Fatalf("gox:\nhave: %v\nwant: %v", err, want)
}
g2, ctx := WorkGroupCtx(context.Background())
g2.Gox(func() {
exc.Raise(2)
})
<-ctx.Done()
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment