Skip to content
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

fix: networking: call Stream.CloseWrite right after writing #9892

Merged
merged 4 commits into from
Jan 4, 2023

Conversation

hanabi1224
Copy link
Contributor

@hanabi1224 hanabi1224 commented Dec 16, 2022

Related Issues

It fixes potential deadlock in Hello and ChainExchange p2p protocols. Below example is a minimal repro, although currently in the actual lotus code the cbor decoder will either succeed or fail fast before deadlock happens, IMHO it's a nice improvement to have.

CloseWrite sends a FIN header over yamux which notifies the reader side that stream writing has finished.

If the stream.CloseWrite() line in the below example is commented out, it will deadlock.

to run the program

  1. paste it in main.go
  2. run go mod tidy
  3. run go mod vendor
  4. run go run .
package main

import (
	"context"
	"fmt"
	"io"
	"time"

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

const (
	ChainExchangeProtocolID = "/fil/chain/xchg/0.0.1"
)

func main() {
	ctx := context.Background()

	h1, err := libp2p.New()
	if err != nil {
		panic(err)
	}
	defer h1.Close()
	h1.SetStreamHandler(ChainExchangeProtocolID, handleStream)

	h2, err := libp2p.New()
	if err != nil {
		panic(err)
	}
	defer h2.Close()

	ttl, _ := time.ParseDuration("24h")
	h2.Peerstore().AddAddrs(h1.ID(), h1.Addrs(), ttl)
	stream, _ := h2.NewStream(ctx, h1.ID(), ChainExchangeProtocolID)
	defer stream.Close()
	stream.Write([]byte("ping"))

	// CloseWrite sends FIN header over yamux, without it
	// the example program will deadlock
	stream.CloseWrite()

	bytes, _ := io.ReadAll(stream)
	fmt.Printf("[h2] Done reading: %s\n", string(bytes))
}

func handleStream(stream network.Stream) {
	defer stream.Close()
	fmt.Println("[h1] Start reading")
	bytes, _ := io.ReadAll(stream)
	fmt.Printf("[h1] Done reading: %s\n", string(bytes))
	stream.Write([]byte("pong"))
}

Output with CloseWrite:

[h1] Start reading
[h1] Done reading: ping
[h2] Done reading: pong

Output without CloseWrite:

[h1] Start reading

Proposed Changes

Additional Info

Checklist

Before you mark the PR ready for review, please make sure that:

  • Commits have a clear commit message.
  • PR title is in the form of of <PR type>: <area>: <change being made>
    • example: fix: mempool: Introduce a cache for valid signatures
    • PR type: fix, feat, build, chore, ci, docs, perf, refactor, revert, style, test
    • area, e.g. api, chain, state, market, mempool, multisig, networking, paych, proving, sealing, wallet, deps
  • New features have usage guidelines and / or documentation updates in
  • Tests exist for new functionality or change in behavior
  • CI is green

@hanabi1224 hanabi1224 marked this pull request as ready for review December 16, 2022 09:58
@hanabi1224 hanabi1224 requested a review from a team as a code owner December 16, 2022 09:58
@geoff-vball
Copy link
Contributor

@hanabi1224 Thanks for the contribution! @magik6k has a FIXME here, so I'm going to let him review this. He's on vacation, but we should get this in after xmas :)

@geoff-vball geoff-vball requested a review from magik6k December 16, 2022 15:31
Copy link
Contributor

@magik6k magik6k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

This seems to be more about the client side being nice / more correct. Reading cborrpc isn't like ReadAll, it will stop after the cbor object is fully read.

Change looks good, other than missing error check.

@@ -430,6 +430,7 @@ func (c *client) sendRequestToPeer(ctx context.Context, peer peer.ID, req *Reque
}
_ = stream.SetWriteDeadline(time.Time{}) // clear deadline // FIXME: Needs
// its own API (https://github.com/libp2p/go-libp2p/core/issues/162).
stream.CloseWrite()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lint fails due to missing error check

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@magik6k magik6k merged commit 472eaaa into filecoin-project:master Jan 4, 2023
@hanabi1224 hanabi1224 deleted the close-write branch January 4, 2023 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants