24constexpr int kPipeBytes = 64 * 1024;
29std::atomic<bool> g_active{
false};
32bool g_atexitRegistered =
false;
36 return _fileno(stderr);
38 return fileno(stderr);
50int duplicateFd(
int fd) {
66int writeFd(
int fd,
const char* data,
int size) {
68 return _write(fd, data,
static_cast<unsigned>(
size));
70 return static_cast<int>(write(fd, data,
static_cast<std::size_t
>(
size)));
74int readFd(
int fd,
char* data,
int size) {
76 return _read(fd, data,
static_cast<unsigned>(
size));
78 return static_cast<int>(
read(fd, data,
static_cast<std::size_t
>(
size)));
83void flushCompleteLines(std::string&
pending) {
85 const auto newline =
pending.find(
'\n');
86 if (newline == std::string::npos)
break;
89 if (!
line.empty() &&
line.back() ==
'\r')
line.pop_back();
90 if (g_sink) g_sink(
line);
102 if (g_savedFd >= 0) {
106 if (written <= 0)
break;
116 if (!
line.empty() &&
line.back() ==
'\r')
line.pop_back();
117 if (g_sink) g_sink(
line);
124 std::lock_guard<std::mutex> lock(g_mutex);
128 if (!g_atexitRegistered) {
132 g_atexitRegistered =
true;
135 int fds[2] = {-1, -1};
143 const int target = stderrFd();
144 const int saved = duplicateFd(
target);
150 if (replaceFd(fds[1],
target) < 0) {
162 g_sink = std::move(sink);
163 g_active.store(
true);
164 g_thread = std::thread(drainLoop);
169 std::lock_guard<std::mutex> lock(g_mutex);
170 if (!g_active.load())
return;
177 const int saved = g_savedFd;
178 if (saved >= 0) replaceFd(saved, stderrFd());
179 if (g_thread.joinable()) g_thread.join();
180 if (saved >= 0) closeFd(saved);
181 if (g_readFd >= 0) closeFd(g_readFd);
186 g_active.store(
false);
std::vector< QuestEvent > pending
std::unique_ptr< gpgpu::GpuBuffer > buffer
Mirror process stderr into an in-process line sink without hiding it.
const UnitySourceAsset & source
std::function< void(const std::string &)> StderrLineSink
Sink invoked on the drain thread for every complete stderr line.
StderrCaptureStatus
Outcome of a capture request.
void stopStderrCapture()
Restore stderr and join the drain thread. No-op when inactive.
bool stderrCaptureActive()
Whether stderr is currently mirrored into a sink.
StderrCaptureStatus startStderrCapture(StderrLineSink sink)
Start mirroring stderr into sink.
T read(const RuntimeTensor &v, size_t i)
Reads read.