@@ -14,19 +14,23 @@ import (
1414 "github.com/cloudstruct/go-ouroboros-network/protocol/txsubmission"
1515 "io"
1616 "net"
17+ "sync"
1718)
1819
1920type Ouroboros struct {
20- conn net.Conn
21- networkMagic uint32
22- server bool
23- useNodeToNodeProto bool
24- muxer * muxer.Muxer
25- ErrorChan chan error
26- protoErrorChan chan error
27- sendKeepAlives bool
28- delayMuxerStart bool
29- fullDuplex bool
21+ conn net.Conn
22+ networkMagic uint32
23+ server bool
24+ useNodeToNodeProto bool
25+ muxer * muxer.Muxer
26+ ErrorChan chan error
27+ protoErrorChan chan error
28+ handshakeFinishedChan chan interface {}
29+ doneChan chan interface {}
30+ closeMutex sync.Mutex
31+ sendKeepAlives bool
32+ delayMuxerStart bool
33+ fullDuplex bool
3034 // Mini-protocols
3135 Handshake * handshake.Handshake
3236 ChainSync * chainsync.ChainSync
@@ -45,7 +49,9 @@ type Ouroboros struct {
4549
4650func New (options ... OuroborosOptionFunc ) (* Ouroboros , error ) {
4751 o := & Ouroboros {
48- protoErrorChan : make (chan error , 10 ),
52+ protoErrorChan : make (chan error , 10 ),
53+ handshakeFinishedChan : make (chan interface {}),
54+ doneChan : make (chan interface {}),
4955 }
5056 // Apply provided options functions
5157 for _ , option := range options {
@@ -81,6 +87,18 @@ func (o *Ouroboros) Dial(proto string, address string) error {
8187}
8288
8389func (o * Ouroboros ) Close () error {
90+ // We use a mutex to prevent this function from being called multiple times
91+ // concurrently, which would cause a race condition
92+ o .closeMutex .Lock ()
93+ defer o .closeMutex .Unlock ()
94+ // Immediately return if we're already shutting down
95+ select {
96+ case <- o .doneChan :
97+ return nil
98+ default :
99+ }
100+ // Close doneChan to signify that we're shutting down
101+ close (o .doneChan )
84102 // Gracefully stop the muxer
85103 if o .muxer != nil {
86104 o .muxer .Stop ()
@@ -91,6 +109,22 @@ func (o *Ouroboros) Close() error {
91109 return err
92110 }
93111 }
112+ // Close channels
113+ close (o .ErrorChan )
114+ close (o .protoErrorChan )
115+ // We can only close a channel once, so we have to jump through a few hoops
116+ select {
117+ // The channel is either closed or has an item pending
118+ case _ , ok := <- o .handshakeFinishedChan :
119+ // We successfully retrieved an item
120+ // This will probably never happen, but it doesn't hurt to cover this case
121+ if ok {
122+ close (o .handshakeFinishedChan )
123+ }
124+ // The channel is open and has no pending items
125+ default :
126+ close (o .handshakeFinishedChan )
127+ }
94128 return nil
95129}
96130
@@ -131,7 +165,6 @@ func (o *Ouroboros) setupConnection() error {
131165 protoOptions .Role = protocol .ProtocolRoleClient
132166 }
133167 // Perform handshake
134- handshakeFinishedChan := make (chan interface {})
135168 var handshakeVersion uint16
136169 var handshakeFullDuplex bool
137170 handshakeConfig := & handshake.Config {
@@ -141,7 +174,7 @@ func (o *Ouroboros) setupConnection() error {
141174 FinishedFunc : func (version uint16 , fullDuplex bool ) error {
142175 handshakeVersion = version
143176 handshakeFullDuplex = fullDuplex
144- close (handshakeFinishedChan )
177+ close (o . handshakeFinishedChan )
145178 return nil
146179 },
147180 }
@@ -153,9 +186,13 @@ func (o *Ouroboros) setupConnection() error {
153186 }
154187 // Wait for handshake completion or error
155188 select {
189+ case <- o .doneChan :
190+ // Return an error if we're shutting down
191+ return io .EOF
156192 case err := <- o .protoErrorChan :
157193 return err
158- case <- handshakeFinishedChan :
194+ case <- o .handshakeFinishedChan :
195+ // This is purposely empty, but we need this case to break out when this channel is closed
159196 }
160197 // Provide the negotiated protocol version to the various mini-protocols
161198 protoOptions .Version = handshakeVersion
@@ -165,7 +202,11 @@ func (o *Ouroboros) setupConnection() error {
165202 }
166203 // Start Goroutine to pass along errors from the mini-protocols
167204 go func () {
168- err := <- o .protoErrorChan
205+ err , ok := <- o .protoErrorChan
206+ // The channel is closed, which means we're already shutting down
207+ if ! ok {
208+ return
209+ }
169210 o .ErrorChan <- fmt .Errorf ("protocol error: %s" , err )
170211 // Close connection on mini-protocol errors
171212 o .Close ()
0 commit comments