audiofuntest: Use nonblock write to avoid being stuck during playback

The buffer size of fd is small. If it is full, the playback will be
blocked until it has enough space. This behavior makes this thread not
able to be stopped immediately, which may cause the program stop to
capture audio and trigger overrun.

Make fd nonblocking so the thread can be interrupted at any time.

BUG=b:168281719
TEST=Pass audiofuntest on Dirinboz

Change-Id: Iee9f2d359a75c6286fa162ce33082fe5bbea9d69
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/audiotest/+/2457207
Reviewed-by: Cheng-Yi Chiang <cychiang@chromium.org>
Commit-Queue: Yu-Hsuan Hsu <yuhsuan@chromium.org>
Tested-by: Yu-Hsuan Hsu <yuhsuan@chromium.org>
diff --git a/include/binary_client.h b/include/binary_client.h
index 5cf0b5b..26b75dc 100644
--- a/include/binary_client.h
+++ b/include/binary_client.h
@@ -29,7 +29,7 @@
   void Terminate();
 
   // Plays the given block of samples.
-  void Play(const void *buffer, size_t size);
+  void Play(const void *buffer, size_t size, bool *is_stopped);
 
  private:
   int child_pid_;
diff --git a/src/binary_client.cc b/src/binary_client.cc
index 66c758d..4907645 100644
--- a/src/binary_client.cc
+++ b/src/binary_client.cc
@@ -114,6 +114,7 @@
 void PlayClient::Start() {
   int other_side_fd;
   play_fd_ = CreateFIFO(FIFO_OUT, fifo_name_, &other_side_fd);
+  fcntl(play_fd_, F_SETFL, O_NONBLOCK);
   child_pid_ = StartProcess(command_, other_side_fd, -1);
 }
 
@@ -123,13 +124,16 @@
   kill(child_pid_, SIGKILL);
 }
 
-void PlayClient::Play(const void *buffer, size_t size) {
+void PlayClient::Play(const void *buffer, size_t size, bool *is_stopped) {
   int res;
   int byte_to_write = size;
   const uint8_t *ptr = static_cast<const uint8_t *>(buffer);
   // Keep writing to pipe until error or finishing.
-  while ((res = write(play_fd_, ptr, byte_to_write)) < byte_to_write) {
+  while (!*is_stopped &&
+          (res = write(play_fd_, ptr, byte_to_write)) < byte_to_write) {
     if (res < 0) {
+      if (errno == EAGAIN)
+        continue;
       perror("Failed to write to player.");
       exit(EXIT_FAILURE);
     }
diff --git a/src/generator_player.cc b/src/generator_player.cc
index 826e98e..9350e36 100644
--- a/src/generator_player.cc
+++ b/src/generator_player.cc
@@ -40,7 +40,8 @@
   while (!is_stopped_ && generator->HasMoreFrames()) {
     size_t frame_read = generator->GetFrames(
         format_, num_channels_, active_channels_, buffer_.get(), buf_size_);
-    player_->Play(buffer_.get(), frame_read * num_channels_ * format_.bytes());
+    player_->Play(buffer_.get(), frame_read * num_channels_ * format_.bytes(),
+	&is_stopped_);
   }
   is_stopped_ = true;
 }