Skip to content
Permalink
Browse files

feat: use contexts for the sync service (#456)

This patch also simplifies several parts of the sync service given that we can now just cancel contexts to, e.g., cancel subscriptions.
  • Loading branch information
Stebalien committed Feb 7, 2020
1 parent 929883f commit 88afb565548f9e1925b32d6424b33af65b4941f7
3 go.mod
@@ -25,9 +25,12 @@ require (
github.com/hashicorp/go-getter v1.4.0
github.com/hashicorp/go-multierror v1.0.0
github.com/imdario/mergo v0.3.8
github.com/ipfs/go-cid v0.0.4 // indirect
github.com/ipfs/testground/sdk/runtime v0.1.0
github.com/ipfs/testground/sdk/sync v0.1.0
github.com/libp2p/go-libp2p-core v0.3.0 // indirect
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.0
github.com/mitchellh/mapstructure v1.1.2
github.com/morikuni/aec v1.0.0 // indirect
23 go.sum
@@ -30,6 +30,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5njHPn0kLAsykn6mJGz7rnmW5W0=
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
@@ -111,6 +113,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -181,6 +185,8 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/ipfs/go-cid v0.0.3 h1:UIAh32wymBpStoe83YCzwVQQ5Oy/H0FdxvUS6DJDzms=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.4 h1:UlfXKrZx1DjZoBhQHmNHLC1fK1dUJDN20Y28A7s+gJ8=
github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=
github.com/ipfs/testground v0.1.0/go.mod h1:9obuj1h0ueQuJ5zV5ro69NwU3AHAIw7FXYT1DOj3Ygs=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10=
@@ -214,12 +220,18 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
github.com/libp2p/go-flow-metrics v0.0.1 h1:0gxuFd2GuK7IIP5pKljLwps6TvcuYgvG7Atqi3INF5s=
github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=
github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM=
github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
github.com/libp2p/go-libp2p-core v0.2.3 h1:zXikZ5pLfebtTMeIYfcwVQ2Pae77O0FIwDquwM6AGNM=
github.com/libp2p/go-libp2p-core v0.2.3/go.mod h1:GqhyQqyIAPsxFYXHMjfXgMv03lxsvM0mFzuYA9Ib42A=
github.com/libp2p/go-libp2p-core v0.3.0 h1:F7PqduvrztDtFsAa/bcheQ3azmNo+Nq7m8hQY5GiUW8=
github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw=
github.com/libp2p/go-msgio v0.0.4 h1:agEFehY3zWJFUHK6SEMR7UYmk2z6kC3oeCM7ybLhguA=
github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
github.com/libp2p/go-openssl v0.0.2 h1:9pP2d3Ubaxkv7ZisLjx9BFwgOGnQdQYnfcH29HNY3ls=
github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0=
github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg=
github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434 h1:im9kkmH0WWwxzegiv18gSUJbuXR9y028rXrWuPp6Jug=
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -236,6 +248,8 @@ github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKU
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
@@ -254,15 +268,23 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-multiaddr v0.1.1 h1:rVAztJYMhCQ7vEFr8FvxW3mS+HF2eY/oPbOMeS0ZDnE=
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90=
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA=
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg=
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
github.com/multiformats/go-multihash v0.0.10 h1:lMoNbh2Ssd9PUF74Nz008KGzGPlfeV6wH3rit5IIGCM=
github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
github.com/multiformats/go-varint v0.0.1 h1:TR/0rdQtnNxuN2IhiB639xC3tWM4IUi7DkTBVTdGW/M=
github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -479,6 +501,7 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnf
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -109,11 +109,9 @@ func (dm *Manager) Manage(
managers := make(map[string]workerHandle)

defer func() {
// cancel the remaining managers
for _, m := range managers {
m.cancel()
}
// wait for the running managers to exit
// They'll get canceled when we close the main context (deferred
// below).
for _, m := range managers {
<-m.done
}
@@ -276,7 +276,7 @@ func (d *DockerInstanceManager) manageContainer(ctx context.Context, container *
}
}
}
return NewInstance(runenv, info.Config.Hostname, network)
return NewInstance(ctx, runenv, info.Config.Hostname, network)
}

type dockerLink struct {
@@ -42,9 +42,9 @@ type Logs interface {
}

// NewInstance constructs a new test instance handle.
func NewInstance(runenv *runtime.RunEnv, hostname string, network Network) (*Instance, error) {
func NewInstance(ctx context.Context, runenv *runtime.RunEnv, hostname string, network Network) (*Instance, error) {
// Get a redis reader/writer.
watcher, writer, err := sync.WatcherWriter(runenv)
watcher, writer, err := sync.WatcherWriter(ctx, runenv)
if err != nil {
return nil, fmt.Errorf("during sync.WatcherWriter: %w", err)
}
@@ -86,7 +86,11 @@ func (d *K8sInstanceManager) Close() error {
func (d *K8sInstanceManager) manageContainer(ctx context.Context, container *dockermanager.Container) (inst *Instance, err error) {
// TODO: sidecar is racing to modify container network with CNI and pod getting ready
// we should probably adjust this function to be called when a pod is in `1/1 Ready` state, and not just listen on the docker socket
time.Sleep(20 * time.Second)
select {
case <-time.After(20 * time.Second):
case <-ctx.Done():
return nil, ctx.Err()
}

// Get the state/config of the cluster
info, err := container.Inspect(ctx)
@@ -245,7 +249,7 @@ func (d *K8sInstanceManager) manageContainer(ctx context.Context, container *doc
}
}

return NewInstance(runenv, info.Config.Hostname, network)
return NewInstance(ctx, runenv, info.Config.Hostname, network)
}

type k8sLink struct {
@@ -77,7 +77,7 @@ func Run(runnerName string) error {

// Wait for all the sidecars to enter the "network-initialized" state.
const netInitState = "network-initialized"
if _, err = instance.Writer.SignalEntry(netInitState); err != nil {
if _, err = instance.Writer.SignalEntry(ctx, netInitState); err != nil {
return fmt.Errorf("failed to signal network ready: %w", err)
}

@@ -96,22 +96,16 @@ func Run(runnerName string) error {
// Now let the test case tell us how to configure the network.
subtree := sync.NetworkSubtree(instance.Hostname)
networkChanges := make(chan *sync.NetworkConfig, 16)
closeSub, err := instance.Watcher.Subscribe(subtree, networkChanges)
if err != nil {
if err := instance.Watcher.Subscribe(ctx, subtree, networkChanges); err != nil {
return fmt.Errorf("failed to subscribe to network changes: %s", err)
}
defer func() {
if err := closeSub(); err != nil {
instance.S().Warnf("failed to close sub: %s", err)
}
}()
for cfg := range networkChanges {
instance.S().Infow("applying network change", "network", cfg)
if err := instance.Network.ConfigureNetwork(ctx, cfg); err != nil {
return fmt.Errorf("failed to update network %s: %w", cfg.Network, err)
}
if cfg.State != "" {
_, err := instance.Writer.SignalEntry(cfg.State)
_, err := instance.Writer.SignalEntry(ctx, cfg.State)
if err != nil {
return fmt.Errorf(
"failed to signal network state change %s: %w",
@@ -39,7 +39,7 @@ func Transfer(runenv *runtime.RunEnv) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

watcher, writer := sync.MustWatcherWriter(runenv)
watcher, writer := sync.MustWatcherWriter(ctx, runenv)

/// --- Tear down
defer func() {
@@ -66,7 +66,7 @@ func Transfer(runenv *runtime.RunEnv) error {
defer node.Close()

// Get sequence number of this host
seq, err := writer.Write(sync.PeerSubtree, host.InfoFromHost(node.Host))
seq, err := writer.Write(ctx, sync.PeerSubtree, host.InfoFromHost(node.Host))
if err != nil {
return err
}
@@ -95,42 +95,42 @@ func Transfer(runenv *runtime.RunEnv) error {
}

// Inform other nodes of the root CID
if _, err = writer.Write(RootCidSubtree, &rootCid); err != nil {
if _, err = writer.Write(ctx, RootCidSubtree, &rootCid); err != nil {
return fmt.Errorf("Failed to get Redis Sync RootCidSubtree %w", err)
}
} else if isLeech {
// Get the root CID from a seed
rootCidCh := make(chan *cid.Cid, 1)
cancelRootCidSub, err := watcher.Subscribe(RootCidSubtree, rootCidCh)
if err != nil {
sctx, cancelRootCidSub := context.WithCancel(ctx)
if err := watcher.Subscribe(sctx, RootCidSubtree, rootCidCh); err != nil {
cancelRootCidSub()
return fmt.Errorf("Failed to subscribe to RootCidSubtree %w", err)
}

// Note: only need to get the root CID from one seed - it should be the
// same on all seeds (seed data is generated from repeatable random
// sequence)
select {
case rootCidPtr := <-rootCidCh:
_ = cancelRootCidSub()
rootCid = *rootCidPtr
case <-time.After(timeout):
_ = cancelRootCidSub()
rootCidPtr, ok := <-rootCidCh
cancelRootCidSub()
if !ok {
return fmt.Errorf("no root cid in %d seconds", timeout/time.Second)
}
rootCid = *rootCidPtr
}

// Get addresses of all peers
peerCh := make(chan *peer.AddrInfo)
cancelSub, err := watcher.Subscribe(sync.PeerSubtree, peerCh)
if err != nil {
sctx, cancelSub := context.WithCancel(ctx)
if err := watcher.Subscribe(sctx, sync.PeerSubtree, peerCh); err != nil {
cancelSub()
return err
}
addrInfos, err := utils.AddrInfosFromChan(peerCh, runenv.TestInstanceCount, timeout)
addrInfos, err := utils.AddrInfosFromChan(peerCh, runenv.TestInstanceCount)
if err != nil {
_ = cancelSub()
return err
cancelSub()
return fmt.Errorf("no addrs in %d seconds", timeout/time.Second)
}
_ = cancelSub()
cancelSub()

// Dial all peers
dialed, err := utils.DialOtherPeers(ctx, node.Host, addrInfos)
@@ -30,7 +30,7 @@ func SetupNetwork(ctx context.Context, runenv *runtime.RunEnv, watcher *sync.Wat

latency := time.Duration(runenv.IntParam("latency_ms")) * time.Millisecond
bandwidth := runenv.IntParam("bandwidth_mb")
_, err = writer.Write(sync.NetworkSubtree(hostname), &sync.NetworkConfig{
_, err = writer.Write(ctx, sync.NetworkSubtree(hostname), &sync.NetworkConfig{
Network: "default",
Enable: true,
Default: sync.LinkShape{
@@ -4,22 +4,19 @@ import (
"bytes"
"context"
"fmt"
"time"

core "github.com/libp2p/go-libp2p-core"
"github.com/libp2p/go-libp2p-core/peer"
)

func AddrInfosFromChan(peerCh chan *peer.AddrInfo, count int, timeout time.Duration) ([]peer.AddrInfo, error) {
func AddrInfosFromChan(peerCh chan *peer.AddrInfo, count int) ([]peer.AddrInfo, error) {
var ais []peer.AddrInfo
for i := 1; i <= count; i++ {
select {
case ai := <-peerCh:
ais = append(ais, *ai)

case <-time.After(timeout):
return nil, fmt.Errorf("no new peers in %d seconds", timeout/time.Second)
ai, ok := <-peerCh
if !ok {
return ais, fmt.Errorf("subscription closed")
}
ais = append(ais, *ai)
}
return ais, nil
}
@@ -12,15 +12,11 @@ func SignalAndWaitForAll(ctx context.Context, instanceCount int, stateName strin
doneCh := watcher.Barrier(ctx, state, int64(instanceCount))

// Signal we've entered the state.
_, err := writer.SignalEntry(state)
_, err := writer.SignalEntry(ctx, state)
if err != nil {
return err
}

// Wait until all others have signalled.
if err = <-doneCh; err != nil {
return err
}

return nil
return <-doneCh
}

0 comments on commit 88afb56

Please sign in to comment.
You can’t perform that action at this time.