You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

duplex_pipe.cc 5.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /**
  2. * Copyright 2020 Huawei Technologies Co., Ltd
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "common/duplex_pipe.h"
  17. #include <sys/wait.h>
  18. #include <iostream>
  19. #include <vector>
  20. #include <algorithm>
  21. #include "utils/convert_utils_base.h"
  22. namespace mindspore {
  23. DuplexPipe::~DuplexPipe() {
  24. // pid_ < 0 means the child process is invalid or closed, pid_ == 0 means this process is child
  25. if (pid_ > 0) {
  26. (void)kill(pid_, SIGKILL);
  27. }
  28. }
  29. int DuplexPipe::Open(const std::initializer_list<std::string> &arg_list, bool append_fds) {
  30. if (pipe(fd1_) == -1) {
  31. DP_EXCEPTION << "pipe 1 failed, errno: " << errno;
  32. }
  33. if (pipe(fd2_) == -1) {
  34. close(fd1_[0]);
  35. close(fd1_[1]);
  36. DP_EXCEPTION << "pipe 2 failed, errno: " << errno;
  37. }
  38. pid_ = fork();
  39. if (pid_ < 0) {
  40. close(fd1_[0]);
  41. close(fd1_[1]);
  42. close(fd2_[0]);
  43. close(fd2_[1]);
  44. DP_EXCEPTION << "fork failed, errno: " << errno;
  45. } else if (pid_ == 0) { // Remote process
  46. DP_INFO << "Remote process, id: " << getpid() << ", " << fd1_[0] << "/" << fd2_[1];
  47. DP_INFO << "Execute: arg_list:" << arg_list;
  48. remote_stdout_ = dup(STDOUT_FILENO);
  49. remote_stdin_ = dup(STDIN_FILENO);
  50. close(fd1_[1]);
  51. close(fd2_[0]);
  52. if (!append_fds) {
  53. dup2(fd1_[0], STDIN_FILENO);
  54. dup2(fd2_[1], STDOUT_FILENO);
  55. }
  56. std::vector<const char *> args;
  57. std::transform(arg_list.begin(), arg_list.end(), std::back_inserter(args),
  58. [](const std::string &arg) -> const char * { return arg.c_str(); });
  59. if (append_fds) {
  60. std::string fd10 = std::to_string(fd1_[0]).c_str();
  61. args.emplace_back(fd10.c_str());
  62. std::string fd21 = std::to_string(fd2_[1]).c_str();
  63. args.emplace_back(fd21.c_str());
  64. }
  65. args.emplace_back(nullptr);
  66. if (execvp(args[0], const_cast<char *const *>(&args[0])) == -1) {
  67. DP_EXCEPTION << "execute " << args[0] << " failed, errno: " << errno;
  68. }
  69. } else { // Local process
  70. DP_INFO << "Local process, id: " << getpid() << ", " << fd2_[0] << "/" << fd1_[1];
  71. local_stdout_ = dup(STDOUT_FILENO);
  72. local_stdin_ = dup(STDIN_FILENO);
  73. close(fd1_[0]);
  74. close(fd2_[1]);
  75. signal_handler_ = std::make_shared<SignalHandler>(weak_from_this(), &pid_);
  76. }
  77. return 0;
  78. }
  79. void DuplexPipe::Write(const std::string &buf, bool flush) const {
  80. // Write the string into pipe
  81. if (write(fd1_[1], buf.data(), buf.size()) == -1) {
  82. DP_ERROR << "write failed, errno: " << errno;
  83. return;
  84. }
  85. if (flush) {
  86. // Flush into the pipe
  87. if (write(fd1_[1], "\n", 1) == -1) {
  88. DP_ERROR << "write failed, errno: " << errno;
  89. return;
  90. }
  91. }
  92. DP_DEBUG << "<< [" << buf << "]";
  93. }
  94. std::string DuplexPipe::Read() {
  95. // Read the string from pipe
  96. std::string buf;
  97. // Read one line or multiple lines
  98. while (1) {
  99. SetTimeOut();
  100. ssize_t size = read(fd2_[0], c_buf_, kBufferSize); // MAYBE BLOCKED, Till reading something
  101. if (size <= 0) {
  102. break;
  103. }
  104. CancelTimeOut();
  105. bool line_end = c_buf_[size - 1] == '\n';
  106. buf.append(c_buf_, LongToSize(line_end ? size - 1 : size)); // Copy without the last '\n'
  107. if (line_end) {
  108. break;
  109. }
  110. }
  111. DP_DEBUG << ">> [" << buf << "]";
  112. return buf;
  113. }
  114. void DuplexPipe::WriteWithStdout(const std::string &buf, bool flush) {
  115. dup2(fd1_[1], STDOUT_FILENO);
  116. // Write the string into pipe
  117. std::cout << buf;
  118. if (flush) {
  119. // Flush into the pipe
  120. std::cout << std::endl;
  121. }
  122. dup2(local_stdout_, STDOUT_FILENO);
  123. }
  124. std::string DuplexPipe::ReadWithStdin() {
  125. std::string buf;
  126. dup2(fd2_[0], STDIN_FILENO);
  127. // Maybe blocked
  128. SetTimeOut();
  129. std::getline(std::cin, buf); // Not use 'std::cin >>' to include space
  130. CancelTimeOut();
  131. dup2(local_stdin_, STDIN_FILENO);
  132. return buf;
  133. }
  134. DuplexPipe &DuplexPipe::operator<<(const std::string &buf) {
  135. Write(buf);
  136. return *this;
  137. }
  138. DuplexPipe &DuplexPipe::operator>>(std::string &buf) {
  139. buf = Read();
  140. return *this;
  141. }
  142. void DuplexPipe::Close() {
  143. close(fd1_[0]);
  144. close(fd1_[1]);
  145. close(fd2_[0]);
  146. close(fd2_[1]);
  147. pid_ = -1;
  148. }
  149. DuplexPipe::SignalHandler::SignalHandler(const std::weak_ptr<DuplexPipe> &dp, pid_t *pid) {
  150. dp_ = dp;
  151. child_pid_ = pid;
  152. signal(SIGCHLD, SigChildHandler);
  153. signal(SIGPIPE, SigPipeHandler);
  154. }
  155. DuplexPipe::SignalHandler::~SignalHandler() {}
  156. void DuplexPipe::SignalHandler::SetAlarm(unsigned int interval_secs) const {
  157. signal(SIGALRM, SigAlarmHandler);
  158. alarm(interval_secs);
  159. }
  160. void DuplexPipe::SignalHandler::CancelAlarm() const { (void)alarm(0); }
  161. void DuplexPipe::SignalHandler::SigAlarmHandler(int sig) {
  162. DP_INFO << "Signal: " << sig << ", child_pid_: " << child_pid_;
  163. auto shared_dp = dp_.lock();
  164. if (shared_dp != nullptr) {
  165. shared_dp->NotifyTimeOut();
  166. }
  167. if (child_pid_ != nullptr) {
  168. *child_pid_ = -1;
  169. }
  170. }
  171. void DuplexPipe::SignalHandler::SigPipeHandler(int sig) {
  172. DP_INFO << "Signal: " << sig << ", child_pid_: " << child_pid_;
  173. auto shared_dp = dp_.lock();
  174. if (shared_dp != nullptr) {
  175. shared_dp->NotifyFinalize();
  176. }
  177. if (child_pid_ != nullptr) {
  178. *child_pid_ = -1;
  179. }
  180. }
  181. void DuplexPipe::SignalHandler::SigChildHandler(int) {
  182. int status;
  183. if (child_pid_ != nullptr) {
  184. (void)waitpid(*child_pid_, &status, WNOHANG | WUNTRACED);
  185. *child_pid_ = -1;
  186. }
  187. }
  188. } // namespace mindspore