Skip to content

Commit 8fab477

Browse files
committed
Refactor Unix Process I/O And Workspace Formatting
Improve the Unix async process reader so it distinguishes between normal reads, timeouts, termination, and real errors. The reader thread now keeps draining stdout and stderr independently until both pipes close, which makes process shutdown and termination reporting more reliable. Also reformat the macOS workspace file to use consistent JSON-style indentation without changing its configuration content. * AsyncProcess * Workspace **Generated by CodeLite** Signed-off-by: Eran Ifrah <eran@codelite.org>
1 parent 212d6be commit 8fab477

File tree

3 files changed

+120
-66
lines changed

3 files changed

+120
-66
lines changed

CodeLite/AsyncProcess/UnixProcess.cpp

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ UnixProcess::~UnixProcess()
9191
Wait();
9292
}
9393

94-
bool UnixProcess::ReadAll(int fd, std::string& content, int timeoutMilliseconds)
94+
UnixProcess::ReadResult UnixProcess::ReadAll(int fd, std::string& content, int timeoutMilliseconds)
9595
{
9696
fd_set rset;
9797
char buff[65536];
@@ -105,19 +105,31 @@ bool UnixProcess::ReadAll(int fd, std::string& content, int timeoutMilliseconds)
105105
int rc = ::select(fd + 1, &rset, nullptr, nullptr, &tv);
106106
if (rc > 0) {
107107
for (;;) {
108+
// fd is non blocking, read until we EWOULDBLOCK, EAGAIN
109+
// or the server process terminated.
110+
errno = 0;
108111
int len = read(fd, buff, (sizeof(buff) - 1));
109-
if (len <= 0)
110-
break;
111-
buff[len] = 0;
112-
content.append(buff);
112+
if (len < 0) {
113+
if (errno == EWOULDBLOCK || errno == EAGAIN) {
114+
return ReadResult::kOk;
115+
}
116+
// read error
117+
return ReadResult::kError;
118+
} else if (len == 0) {
119+
return ReadResult::kTerminated;
120+
} else {
121+
// we read something
122+
buff[len] = 0;
123+
content.append(buff);
124+
}
113125
}
114-
return true;
115126
} else if (rc == 0) {
116127
// timeout
117-
return true;
128+
return ReadResult::kTimeout;
129+
} else {
130+
// select returned error
131+
return ReadResult::kError;
118132
}
119-
// error
120-
return false;
121133
}
122134

123135
bool UnixProcess::Write(int fd, const std::string& message, std::atomic_bool& shutdown)
@@ -194,34 +206,41 @@ void UnixProcess::StartReaderThread()
194206
fcntl(stdoutFd, F_SETFL, O_NONBLOCK);
195207
fcntl(stderrFd, F_SETFL, O_NONBLOCK);
196208

197-
while (!process->m_goingDown.load()) {
198-
std::string content;
199-
if (!ReadAll(stdoutFd, content, 10)) {
200-
clProcessEvent evt(wxEVT_ASYNC_PROCESS_TERMINATED);
201-
wxString error_message;
202-
int exit_code = process->Wait();
203-
error_message << "Process exit code (" << exit_code << "):" << strerror(exit_code);
204-
evt.SetString(error_message);
205-
process->m_owner->AddPendingEvent(evt);
206-
break;
207-
} else if (!content.empty()) {
208-
clProcessEvent evt(wxEVT_ASYNC_PROCESS_OUTPUT);
209-
evt.SetOutput(wxString::FromUTF8(content));
210-
evt.SetOutputRaw(content);
211-
process->m_owner->AddPendingEvent(evt);
212-
}
209+
// Returns true if the fd is still alive
210+
auto ReadFD = [](UnixProcess* process, int fd, wxEventType event_type, std::string& content) -> bool {
213211
content.clear();
214-
if (!ReadAll(stderrFd, content, 10)) {
215-
clProcessEvent evt(wxEVT_ASYNC_PROCESS_TERMINATED);
216-
process->m_owner->AddPendingEvent(evt);
217-
break;
218-
} else if (!content.empty()) {
219-
clProcessEvent evt(wxEVT_ASYNC_PROCESS_STDERR);
212+
auto result = ReadAll(fd, content, 10);
213+
if (result == UnixProcess::ReadResult::kOk && !content.empty()) {
214+
clProcessEvent evt(event_type);
220215
evt.SetOutput(wxString::FromUTF8(content));
221216
evt.SetOutputRaw(content);
222217
process->m_owner->AddPendingEvent(evt);
223218
}
219+
return result != UnixProcess::ReadResult::kError && result != UnixProcess::ReadResult::kTerminated;
220+
};
221+
222+
bool stdoutAlive{true};
223+
bool stderrAlive{true};
224+
225+
// Keep draining stdout / stderr until both are closed
226+
// or both the process got a shutdown request.
227+
while (!process->m_goingDown.load() && (stdoutAlive || stderrAlive)) {
228+
std::string content;
229+
if (stdoutAlive) {
230+
stdoutAlive = ReadFD(process, stdoutFd, wxEVT_ASYNC_PROCESS_OUTPUT, content);
231+
}
232+
if (stderrAlive) {
233+
stderrAlive = ReadFD(process, stderrFd, wxEVT_ASYNC_PROCESS_STDERR, content);
234+
}
224235
}
236+
237+
// Both fds closed - send termination event
238+
clProcessEvent evt(wxEVT_ASYNC_PROCESS_TERMINATED);
239+
wxString error_message;
240+
int exit_code = process->Wait();
241+
error_message << "Process exit code (" << exit_code << "):" << strerror(exit_code);
242+
evt.SetString(error_message);
243+
process->m_owner->AddPendingEvent(evt);
225244
clDEBUG1() << "UnixProcess reader thread: going down" << endl;
226245
},
227246
this,

CodeLite/AsyncProcess/UnixProcess.h

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,13 @@ class CPipe
5252

5353
class UnixProcess
5454
{
55-
private:
56-
CPipe m_childStdin;
57-
CPipe m_childStdout;
58-
CPipe m_childStderr;
59-
std::thread* m_writerThread = nullptr;
60-
std::thread* m_readerThread = nullptr;
61-
wxMessageQueue<std::string> m_outgoingQueue;
62-
std::atomic_bool m_goingDown;
63-
wxEvtHandler* m_owner = nullptr;
64-
65-
protected:
66-
// sync operations
67-
static bool ReadAll(int fd, std::string& content, int timeoutMilliseconds);
68-
static bool Write(int fd, const std::string& message, std::atomic_bool& shutdown);
69-
70-
void StartWriterThread();
71-
void StartReaderThread();
72-
7355
public:
56+
enum class ReadResult {
57+
kOk,
58+
kTimeout,
59+
kError,
60+
kTerminated,
61+
};
7462
int child_pid = -1;
7563

7664
UnixProcess(wxEvtHandler* owner, const wxArrayString& args);
@@ -89,6 +77,24 @@ class UnixProcess
8977
* @brief stop sending events from the process
9078
*/
9179
void Detach();
80+
81+
private:
82+
CPipe m_childStdin;
83+
CPipe m_childStdout;
84+
CPipe m_childStderr;
85+
std::thread* m_writerThread = nullptr;
86+
std::thread* m_readerThread = nullptr;
87+
wxMessageQueue<std::string> m_outgoingQueue;
88+
std::atomic_bool m_goingDown;
89+
wxEvtHandler* m_owner = nullptr;
90+
91+
protected:
92+
// sync operations
93+
static ReadResult ReadAll(int fd, std::string& content, int timeoutMilliseconds);
94+
static bool Write(int fd, const std::string& message, std::atomic_bool& shutdown);
95+
96+
void StartWriterThread();
97+
void StartReaderThread();
9298
};
9399
#endif // defined(__WXGTK__)||defined(__WXOSX__)
94100
#endif // UNIX_PROCESS_H

CodeLiteIDE-macOS.workspace

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,48 @@
11
{
2-
"workspace_type": "File System Workspace",
3-
"name": "CodeLiteIDE-Linux",
4-
"configs": [{
5-
"name": "Debug",
6-
"targets": [["Run CMake - Debug", "mkdir -p .build-debug && cd .build-debug && cmake -DCMAKE_BUILD_TYPE=Debug .. -Wno-dev"], ["build", "cd .build-debug && make -j12 install"], ["clean", "cd .build-debug && make -j12 clean"]],
7-
"file_extensions": "*.cpp;*.c;*.txt;*.json;*.hpp;*.cc;*.cxx;*.xml;*.h;*.wxcp;*.xrc;*.py",
8-
"excludeFilesPattern": "*.o;*.pyc;*.obj;*.workspace;*.o.d;*.exe;*.dll;*.project",
9-
"excludePaths": "codelite-cli;codelite-icons;codelite-icons-dark;codelite-icons-fresh-farm;codelite_echo;codelite_make;bitmaps;bitmaps-dark;bitmaps-light;.build-debug;.build-release;CallGraph",
10-
"debugger": "GNU gdb debugger"
11-
}, {
12-
"name": "Release",
13-
"targets": [["Run CMake - Release", "mkdir -p .build-release && cd .build-release && cmake -DCMAKE_BUILD_TYPE=Release .."], ["build", "cd .build-release && make -j12 install"], ["clean", "cd .build-release && make -j12 clean"]],
14-
"file_extensions": "*.cpp;*.c;*.txt;*.json;*.hpp;*.cc;*.cxx;*.xml;*.h;*.wxcp;.*.xrc;*.cmake;*.rc;*.py;*.md;*.xrc",
15-
"excludeFilesPattern": "*.o;*.pyc;*.obj;*.workspace;*.o.d;*.exe;*.dll;*.project",
16-
"excludePaths": "codelite-cli;codelite-icons;codelite-icons-dark;codelite-icons-fresh-farm;codelite_echo;codelite_make;bitmaps;bitmaps-dark;bitmaps-light;.build-debug;.build-release;CallGraph",
17-
"debugger": "GNU gdb debugger"
18-
}]
2+
"workspace_type": "File System Workspace",
3+
"name": "CodeLiteIDE-Linux",
4+
"configs": [
5+
{
6+
"name": "Debug",
7+
"targets": [
8+
[
9+
"Run CMake - Debug",
10+
"mkdir -p .build-debug && cd .build-debug && cmake -DCMAKE_BUILD_TYPE=Debug .. -Wno-dev"
11+
],
12+
[
13+
"build",
14+
"cd .build-debug && make -j12 install"
15+
],
16+
[
17+
"clean",
18+
"cd .build-debug && make -j12 clean"
19+
]
20+
],
21+
"file_extensions": "*.cpp;*.c;*.txt;*.json;*.hpp;*.cc;*.cxx;*.xml;*.h;*.wxcp;*.xrc;*.py",
22+
"excludeFilesPattern": "*.o;*.pyc;*.obj;*.workspace;*.o.d;*.exe;*.dll;*.project",
23+
"excludePaths": "codelite-cli;codelite-icons;codelite-icons-dark;codelite-icons-fresh-farm;codelite_echo;codelite_make;bitmaps;bitmaps-dark;bitmaps-light;.build-debug;.build-release;CallGraph",
24+
"debugger": "GNU gdb debugger"
25+
},
26+
{
27+
"name": "Release",
28+
"targets": [
29+
[
30+
"Run CMake - Release",
31+
"mkdir -p .build-release && cd .build-release && cmake -DCMAKE_BUILD_TYPE=Release .."
32+
],
33+
[
34+
"build",
35+
"cd .build-release && make -j12 install"
36+
],
37+
[
38+
"clean",
39+
"cd .build-release && make -j12 clean"
40+
]
41+
],
42+
"file_extensions": "*.cpp;*.c;*.txt;*.json;*.hpp;*.cc;*.cxx;*.xml;*.h;*.wxcp;.*.xrc;*.cmake;*.rc;*.py;*.md;*.xrc",
43+
"excludeFilesPattern": "*.o;*.pyc;*.obj;*.workspace;*.o.d;*.exe;*.dll;*.project",
44+
"excludePaths": "codelite-cli;codelite-icons;codelite-icons-dark;codelite-icons-fresh-farm;codelite_echo;codelite_make;bitmaps;bitmaps-dark;bitmaps-light;.build-debug;.build-release;CallGraph",
45+
"debugger": "GNU gdb debugger"
46+
}
47+
]
1948
}

0 commit comments

Comments
 (0)