@ -22,6 +22,8 @@ limitations under the License. */
using paddle : : framework : : Channel ;
using paddle : : framework : : Channel ;
using paddle : : framework : : MakeChannel ;
using paddle : : framework : : MakeChannel ;
using paddle : : framework : : CloseChannel ;
using paddle : : framework : : CloseChannel ;
using paddle : : framework : : details : : Buffered ;
using paddle : : framework : : details : : UnBuffered ;
TEST ( Channel , MakeAndClose ) {
TEST ( Channel , MakeAndClose ) {
using paddle : : framework : : details : : Buffered ;
using paddle : : framework : : details : : Buffered ;
@ -60,13 +62,54 @@ TEST(Channel, SufficientBufferSizeDoesntBlock) {
delete ch ;
delete ch ;
}
}
TEST ( Channel , SendOnClosedChannelPanics ) {
// This tests that a channel must return false
const size_t buffer_size = 10 ;
// on send and receive performed after closing the channel.
auto ch = MakeChannel < size_t > ( buffer_size ) ;
// Receive will only return false after close when queue is empty.
size_t i = 5 ;
// By creating separate threads for sending and receiving, we make this
EXPECT_EQ ( ch - > Send ( & i ) , true ) ; // should not block or panic
// function able to test both buffered and unbuffered channels.
void SendReceiveWithACloseChannelShouldPanic ( Channel < size_t > * ch ) {
const size_t data = 5 ;
std : : thread send_thread { [ & ] ( ) {
size_t i = data ;
EXPECT_EQ ( ch - > Send ( & i ) , true ) ; // should not block
} } ;
std : : thread recv_thread { [ & ] ( ) {
size_t i ;
EXPECT_EQ ( ch - > Receive ( & i ) , true ) ; // should not block
EXPECT_EQ ( i , data ) ;
} } ;
send_thread . join ( ) ;
recv_thread . join ( ) ;
// After closing send should return false. Receive should
// also return false as there is no data in queue.
CloseChannel ( ch ) ;
CloseChannel ( ch ) ;
EXPECT_EQ ( ch - > Send ( & i ) , false ) ; // should panic
send_thread = std : : thread { [ & ] ( ) {
size_t i = data ;
EXPECT_EQ ( ch - > Send ( & i ) , false ) ; // should return false
} } ;
recv_thread = std : : thread { [ & ] ( ) {
size_t i ;
// should return false because channel is closed and queue is empty
EXPECT_EQ ( ch - > Receive ( & i ) , false ) ;
} } ;
send_thread . join ( ) ;
recv_thread . join ( ) ;
}
TEST ( Channel , SendReceiveClosedBufferedChannelPanics ) {
size_t buffer_size = 10 ;
auto ch = MakeChannel < size_t > ( buffer_size ) ;
SendReceiveWithACloseChannelShouldPanic ( ch ) ;
delete ch ;
}
TEST ( Channel , SendReceiveClosedUnBufferedChannelPanics ) {
auto ch = MakeChannel < size_t > ( 0 ) ;
SendReceiveWithACloseChannelShouldPanic ( ch ) ;
delete ch ;
delete ch ;
}
}
@ -381,3 +424,129 @@ TEST(Channel, UnbufferedMoreReceiveLessSendTest) {
EXPECT_EQ ( sum_receive , 28U ) ;
EXPECT_EQ ( sum_receive , 28U ) ;
delete ch ;
delete ch ;
}
}
// This tests that destroying a channel unblocks
// any senders waiting for channel to have write space
void ChannelDestroyUnblockSenders ( Channel < int > * ch ) {
size_t num_threads = 5 ;
std : : thread t [ num_threads ] ;
bool thread_ended [ num_threads ] ;
bool send_success [ num_threads ] ;
// Launches threads that try to write and are blocked because of no readers
for ( size_t i = 0 ; i < num_threads ; i + + ) {
thread_ended [ i ] = false ;
send_success [ i ] = false ;
t [ i ] = std : : thread (
[ & ] ( bool * ended , bool * success ) {
int data = 10 ;
* success = ch - > Send ( & data ) ;
* ended = true ;
} ,
& thread_ended [ i ] , & send_success [ i ] ) ;
}
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 500 ) ) ; // wait 0.5 sec
bool is_buffered_channel = false ;
if ( dynamic_cast < Buffered < int > * > ( ch ) ) is_buffered_channel = true ;
if ( is_buffered_channel ) {
// If channel is buffered, verify that atleast 4 threads are blocked
int ct = 0 ;
for ( size_t i = 0 ; i < num_threads ; i + + ) {
if ( thread_ended [ i ] = = false ) ct + + ;
}
// Atleast 4 threads must be blocked
EXPECT_GE ( ct , 4 ) ;
} else {
// Verify that all the threads are blocked
for ( size_t i = 0 ; i < num_threads ; i + + ) {
EXPECT_EQ ( thread_ended [ i ] , false ) ;
}
}
// Explicitly destroy the channel
delete ch ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 200 ) ) ; // wait
// Verify that all threads got unblocked
for ( size_t i = 0 ; i < num_threads ; i + + ) {
EXPECT_EQ ( thread_ended [ i ] , true ) ;
}
// Count number of successfuld sends
int ct = 0 ;
for ( size_t i = 0 ; i < num_threads ; i + + ) {
if ( send_success [ i ] ) ct + + ;
}
if ( is_buffered_channel ) {
// Only 1 send must be successful
EXPECT_EQ ( ct , 1 ) ;
} else {
// In unbuffered channel, no send should be successful
EXPECT_EQ ( ct , 0 ) ;
}
// Join all threads
for ( size_t i = 0 ; i < num_threads ; i + + ) t [ i ] . join ( ) ;
}
// This tests that destroying a channel also unblocks
// any receivers waiting on the channel
void ChannelDestroyUnblockReceivers ( Channel < int > * ch ) {
size_t num_threads = 5 ;
std : : thread t [ num_threads ] ;
bool thread_ended [ num_threads ] ;
// Launches threads that try to read and are blocked because of no writers
for ( size_t i = 0 ; i < num_threads ; i + + ) {
thread_ended [ i ] = false ;
t [ i ] = std : : thread (
[ & ] ( bool * p ) {
int data ;
// All reads should return false
EXPECT_EQ ( ch - > Receive ( & data ) , false ) ;
* p = true ;
} ,
& thread_ended [ i ] ) ;
}
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 100 ) ) ; // wait
// Verify that all threads are blocked
for ( size_t i = 0 ; i < num_threads ; i + + ) {
EXPECT_EQ ( thread_ended [ i ] , false ) ;
}
// delete the channel
delete ch ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 200 ) ) ; // wait
// Verify that all threads got unblocked
for ( size_t i = 0 ; i < num_threads ; i + + ) {
EXPECT_EQ ( thread_ended [ i ] , true ) ;
}
for ( size_t i = 0 ; i < num_threads ; i + + ) t [ i ] . join ( ) ;
}
TEST ( Channel , BufferedChannelDestroyUnblocksReceiversTest ) {
size_t buffer_size = 1 ;
auto ch = MakeChannel < int > ( buffer_size ) ;
ChannelDestroyUnblockReceivers ( ch ) ;
}
TEST ( Channel , BufferedChannelDestroyUnblocksSendersTest ) {
size_t buffer_size = 1 ;
auto ch = MakeChannel < int > ( buffer_size ) ;
ChannelDestroyUnblockSenders ( ch ) ;
}
// This tests that destroying an unbuffered channel also unblocks
// unblocks any receivers waiting for senders
TEST ( Channel , UnbufferedChannelDestroyUnblocksReceiversTest ) {
auto ch = MakeChannel < int > ( 0 ) ;
ChannelDestroyUnblockReceivers ( ch ) ;
}
TEST ( Channel , UnbufferedChannelDestroyUnblocksSendersTest ) {
auto ch = MakeChannel < int > ( 0 ) ;
ChannelDestroyUnblockSenders ( ch ) ;
}