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 for program crash when destructor is called before channel close with blocked readers/writers #8197

Merged
merged 8 commits into from
Feb 8, 2018

Conversation

abhinavarora
Copy link
Contributor

@abhinavarora abhinavarora commented Feb 6, 2018

Fix #8248

return ret;
}

template <typename T>
bool Buffered<T>::Receive(T* item) {
bool ret = false;
// Once the channel has been closed and all data has been consumed,
// just return false. Don't even try acquiring the mutex.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If a reader thread is allowed to acquire mutex here, then in a hypothetical infinite readers scenario, the destructor could be blocked forever. So after closing and when all the data in the channel is consumed, any call to Receive should immediately return without even trying to acquire the lock.

size_t i = data;
EXPECT_EQ(ch->Send(&i), false); // should panic because channel is closed
}};
recv_thread = std::thread{[&]() {

Choose a reason for hiding this comment

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

The receive doesn't really panic if the channel is closed.
It is just a matter of terminology here, since our panic behavior is just returning false instead of throwing an exception (see description in the PR: #8171) .
Ideally only the send to a closed channel should panic and receive on a closed channel first empties the channel buffer if any and receives a zero value post that (that we haven't implemented here) without getting blocked.

So maybe we should fix the comment for better understanding of readers ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you fixed that in the latest commit.

}};
recv_thread = std::thread{[&]() {
size_t i;
// should panic because channel is closed and queue is empty

Choose a reason for hiding this comment

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

Same as above.

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 in the latest commit

}};

send_thread.join();
recv_thread.join();

Choose a reason for hiding this comment

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

Also, I think the function ClosedUnBufferedChannelPanics and the function ClosedBufferedChannelPanics reuse a block of code as a part of the functionality.
I propose that we write the reused block inside another function and call that function with a buffered and unbuffered channel , check PR: #8162 for reference.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agreed with @kavyasrinet .

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 in the latest commit. Thank you


// This tests that destroying an unbuffered channel also unblocks
// unblocks any senders waiting for senders
TEST(Channel, UnbufferedChannelDestroyUnblocksSendersTest) {

Choose a reason for hiding this comment

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

Same comment as above, I think there is a lot of repetition of code inside the functions of Unbuffered and Buffered functions. We can write a helper function that implements the block and call it with different channels.

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 in the latest commit. Thank you.

// The destructor must wait for all readers and writers to complete their task
// The channel has been closed, so we will not accept new readers and writers
lock.lock();
destructor_cond_var_.wait(

Choose a reason for hiding this comment

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

Thanks for catching this!

// This tests that a channel must return false
// on send and receive performed after closing the channel.
// Receive will only return false after close when queue is empty
void ClosedChannelSendReceivePanics(Channel<size_t> *ch) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is SendReceiveWithACloseChannelShouldPanic a more accurate name for this test?

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. Thank you.

const size_t data = 5;
std::thread send_thread{[&]() {
size_t i = data;
EXPECT_EQ(ch->Send(&i), true); // should not block
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we remove the definition of the variable i and call ch-Send(&data) here?

// Receive will only return false after close when queue is empty
void ClosedChannelSendReceivePanics(Channel<size_t> *ch) {
const size_t data = 5;
std::thread send_thread{[&]() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we need a comment here:

// By creating separate threads for sending and receiving, we make this 
// function able to test both buffered and unbuffered channels.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@chengduoZH chengduoZH left a comment

Choose a reason for hiding this comment

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

LGTM

@abhinavarora abhinavarora merged commit a1fc570 into PaddlePaddle:develop Feb 8, 2018
@abhinavarora abhinavarora deleted the close_test branch February 8, 2018 07:45
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.

4 participants