-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
basichost: don't wait for Identify #2551
base: master
Are you sure you want to change the base?
Changes from all commits
1e48734
c66780e
6817a9d
6fede0d
8811255
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import ( | |
"io" | ||
"net" | ||
"sync" | ||
"sync/atomic" | ||
"time" | ||
|
||
"github.com/libp2p/go-libp2p/core/connmgr" | ||
|
@@ -646,24 +647,32 @@ func (h *BasicHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.I | |
return nil, fmt.Errorf("failed to open stream: %w", err) | ||
} | ||
|
||
// Wait for any in-progress identifies on the connection to finish. This | ||
// is faster than negotiating. | ||
// | ||
// If the other side doesn't support identify, that's fine. This will | ||
// just be a no-op. | ||
select { | ||
case <-h.ids.IdentifyWait(s.Conn()): | ||
case <-ctx.Done(): | ||
_ = s.Reset() | ||
return nil, fmt.Errorf("identify failed to complete: %w", ctx.Err()) | ||
} | ||
// If pids contains only a single protocol, optimistically use that protocol (i.e. don't wait for | ||
// multistream negotiation). | ||
var pref protocol.ID | ||
if len(pids) == 1 { | ||
pref = pids[0] | ||
} else if len(pids) > 1 { | ||
// Wait for any in-progress identifies on the connection to finish. | ||
// This is faster than negotiating. | ||
// If the other side doesn't support identify, that's fine. This will just be a no-op. | ||
select { | ||
case <-h.ids.IdentifyWait(s.Conn()): | ||
case <-ctx.Done(): | ||
_ = s.Reset() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: Is just |
||
return nil, fmt.Errorf("identify failed to complete: %w", ctx.Err()) | ||
} | ||
|
||
pref, err := h.preferredProtocol(p, pids) | ||
if err != nil { | ||
_ = s.Reset() | ||
return nil, err | ||
// If Identify has finished, we know which protocols the peer supports. | ||
// We don't need to do a multistream negotiation. | ||
// Instead, we just pick the first supported protocol. | ||
var err error | ||
pref, err = h.preferredProtocol(p, pids) | ||
if err != nil { | ||
_ = s.Reset() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: Is just |
||
return nil, err | ||
} | ||
} | ||
|
||
if pref != "" { | ||
if err := s.SetProtocol(pref); err != nil { | ||
return nil, err | ||
|
@@ -736,22 +745,10 @@ func (h *BasicHost) Connect(ctx context.Context, pi peer.AddrInfo) error { | |
// the connection once it has been opened. | ||
func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error { | ||
log.Debugf("host %s dialing %s", h.ID(), p) | ||
c, err := h.Network().DialPeer(ctx, p) | ||
if err != nil { | ||
if _, err := h.Network().DialPeer(ctx, p); err != nil { | ||
return fmt.Errorf("failed to dial: %w", err) | ||
} | ||
|
||
// TODO: Consider removing this? On one hand, it's nice because we can | ||
// assume that things like the agent version are usually set when this | ||
// returns. On the other hand, we don't _really_ need to wait for this. | ||
// | ||
// This is mostly here to preserve existing behavior. | ||
select { | ||
case <-h.ids.IdentifyWait(c): | ||
case <-ctx.Done(): | ||
return fmt.Errorf("identify failed to complete: %w", ctx.Err()) | ||
} | ||
|
||
log.Debugf("host %s finished dialing %s", h.ID(), p) | ||
return nil | ||
} | ||
|
@@ -1049,14 +1046,26 @@ func (h *BasicHost) Close() error { | |
type streamWrapper struct { | ||
network.Stream | ||
rw io.ReadWriteCloser | ||
|
||
calledRead atomic.Bool | ||
Comment on lines
+1049
to
+1050
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we document why we need this? Maybe with a link to multiformats/go-multistream#20 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this |
||
} | ||
|
||
func (s *streamWrapper) Read(b []byte) (int, error) { | ||
return s.rw.Read(b) | ||
n, err := s.rw.Read(b) | ||
if s.calledRead.CompareAndSwap(false, true) { | ||
if errors.Is(err, network.ErrReset) { | ||
return n, msmux.ErrNotSupported[protocol.ID]{Protos: []protocol.ID{s.Protocol()}} | ||
} | ||
} | ||
return n, err | ||
} | ||
|
||
func (s *streamWrapper) Write(b []byte) (int, error) { | ||
return s.rw.Write(b) | ||
n, err := s.rw.Write(b) | ||
if s.calledRead.Load() && errors.Is(err, network.ErrReset) { | ||
return n, msmux.ErrNotSupported[protocol.ID]{Protos: []protocol.ID{s.Protocol()}} | ||
Comment on lines
+1065
to
+1066
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a race condition here, but I think it doesn't matter becuase I'm not sure if you can use streams in the way that is required to trigger this condition. The race is: then: now goroutine2 does the if s.calledRead.Load() && errors.Is(err, network.ErrReset) before goroutine1 could do CompareAndSwap. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see the race condition. I think this should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I now think this whole logic should be a part of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, I agree. |
||
} | ||
return n, err | ||
} | ||
|
||
func (s *streamWrapper) Close() error { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a summary of the algorithm to the documentation for the method
NewStream