aboutsummaryrefslogtreecommitdiff
path: root/include/cru/common/SubProcess.h
blob: 0e05fd5a8e58475d15257100c80b2614b0488d81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#pragma once
#include "Base.h"
#include "Exception.h"
#include "String.h"
#include "io/Stream.h"

#include <chrono>
#include <mutex>
#include <optional>
#include <thread>
#include <vector>

namespace cru {
enum class PlatformSubProcessStatus {
  /**
   * @brief The process has not been created and started.
   */
  Prepare,
  /**
   * @brief The process is failed to start.
   */
  FailToStart,
  /**
   * @brief The process is running now.
   */
  Running,
  /**
   * @brief The process has been exited.
   */
  Exited,
};

class CRU_BASE_API SubProcessException : public Exception {
 public:
  SubProcessException(String message = {});
  ~SubProcessException() override;
};

struct PlatformSubProcessStartInfo {
  String program;
  std::vector<String> arguments;
  std::vector<String> environments;
};

/**
 * @brief Base class of a platform process. It is one-time, which means it
 * starts and exits and can't start again.
 */
class PlatformSubProcessBase : public Object {
 public:
  explicit PlatformSubProcessBase(
      const PlatformSubProcessStartInfo& start_info);

  ~PlatformSubProcessBase() override;

  /**
   * @brief Create and start a real process. If the program can't be created or
   * start, an exception should be thrown.
   */
  void Start();
  /**
   * @brief Wait for the process to exit for at most `wait_time`.
   */
  void Wait(std::optional<std::chrono::milliseconds> wait_time);
  PlatformSubProcessStatus GetStatus() const;
  int GetExitCode() const;
  io::Stream* GetStdinStream();
  io::Stream* GetStdoutStream();
  io::Stream* GetStderrStream();

  void Join();
  void Detach();

 protected:
  /**
   * @brief Create and start a real process. If the program can't be created or
   * start, an exception should be thrown.
   *
   * Implementation should fill internal data of the new process and start it.
   *
   * This method will be called on this thread with data lock hold. It will only
   * called once in first call of `Start`.
   */
  virtual void PlatformCreateProcess() = 0;

  /**
   * @brief Wait for the created process forever. After process exits, fill the
   * result info of internal data.
   *
   * Implementation should wait for the real process forever.
   *
   * This method will be called on another thread. It will only called once
   * after a success call of `Start`. It is safe to write internal data in this
   * method because process lock will be hold and we won't write to internal.
   */
  virtual void PlatformWaitForProcess() = 0;

 private:
  auto CreateDataLockGuard() {
    return std::lock_guard<std::unique_lock<std::mutex>>(data_lock_);
  }

  auto CreateProcessLockGuard() {
    return std::lock_guard<std::unique_lock<std::timed_mutex>>(process_lock_);
  }

 protected:
  PlatformSubProcessStartInfo start_info_;

 private:
  enum class DisposeKind {
    None,
    Join,
    Detach,
  };

  DisposeKind dispose_;

  PlatformSubProcessStatus status_;

  std::mutex data_mutex_;
  /**
   * Lock for protecting data of this class.
   */
  std::unique_lock<std::mutex> data_lock_;
  std::thread process_thread_;

  std::timed_mutex process_mutex_;
  /**
   * Lock for protecting internal data of sub-class, and used for detect whether
   * process is running.
   */
  std::unique_lock<std::timed_mutex> process_lock_;
};

class CRU_BASE_API SubProcess : public Object {
 public:
  SubProcess();

  CRU_DELETE_COPY(SubProcess)

  SubProcess(SubProcess&& other);
  SubProcess& operator=(SubProcess&& other);

  ~SubProcess();

 private:
};
}  // namespace cru