8
8
9
9
#include " FifoFiles.h"
10
10
#include " JSONUtils.h"
11
+ #include " llvm/Support/FileSystem.h"
12
+ #include " llvm/Support/Path.h"
13
+ #include " llvm/Support/WindowsError.h"
11
14
12
- #if !defined(_WIN32)
15
+ #if defined(_WIN32)
16
+ #include < Windows.h>
17
+ #include < fcntl.h>
18
+ #include < io.h>
19
+ #else
13
20
#include < sys/stat.h>
14
21
#include < sys/types.h>
15
22
#include < unistd.h>
@@ -24,41 +31,105 @@ using namespace llvm;
24
31
25
32
namespace lldb_dap {
26
33
27
- FifoFile::FifoFile (StringRef path) : m_path(path) {}
34
+ FifoFile::FifoFile (std::string path, FILE *f) : m_path(path), m_file(f) {}
35
+
36
+ Expected<FifoFile> FifoFile::create (StringRef path) {
37
+ auto file = fopen (path.data (), " r+" );
38
+ if (file == nullptr )
39
+ return createStringError (inconvertibleErrorCode (),
40
+ " Failed to open fifo file " + path);
41
+ if (setvbuf (file, NULL , _IONBF, 0 ))
42
+ return createStringError (inconvertibleErrorCode (),
43
+ " Failed to setvbuf on fifo file " + path);
44
+ return FifoFile (path, file);
45
+ }
46
+
47
+ FifoFile::FifoFile (StringRef path, FILE *f) : m_path(path), m_file(f) {}
28
48
49
+ FifoFile::FifoFile (FifoFile &&other)
50
+ : m_path(other.m_path), m_file(other.m_file) {
51
+ other.m_path .clear ();
52
+ other.m_file = nullptr ;
53
+ }
29
54
FifoFile::~FifoFile () {
55
+ if (m_file)
56
+ fclose (m_file);
30
57
#if !defined(_WIN32)
58
+ // Unreferenced named pipes are deleted automatically on Win32
31
59
unlink (m_path.c_str ());
32
60
#endif
33
61
}
34
62
35
- Expected<std::shared_ptr<FifoFile>> CreateFifoFile (StringRef path) {
36
- #if defined(_WIN32)
37
- return createStringError (inconvertibleErrorCode (), " Unimplemented" );
63
+ // This probably belongs to llvm::sys::fs as another FSEntity type
64
+ Error createUniqueNamedPipe (const Twine &prefix, StringRef suffix,
65
+ int &result_fd,
66
+ SmallVectorImpl<char > &result_path) {
67
+ std::error_code EC;
68
+ #ifdef _WIN32
69
+ const char *middle = suffix.empty () ? " -%%%%%%" : " -%%%%%%." ;
70
+ EC = sys::fs::getPotentiallyUniqueFileName (
71
+ " \\\\ .\\ pipe\\ LOCAL\\ " + prefix + middle + suffix, result_path);
72
+ #else
73
+ EC = sys::fs::getPotentiallyUniqueTempFileName (prefix, suffix, result_path);
74
+ #endif
75
+ if (EC)
76
+ return errorCodeToError (EC);
77
+ result_path.push_back (0 );
78
+ const char *path = result_path.data ();
79
+ #ifdef _WIN32
80
+ HANDLE h = ::CreateNamedPipeA (
81
+ path, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
82
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1 , 512 , 512 , 0 , NULL );
83
+ if (h == INVALID_HANDLE_VALUE)
84
+ return createStringError (mapLastWindowsError (), " CreateNamedPipe" );
85
+ result_fd = _open_osfhandle ((intptr_t )h, _O_TEXT | _O_RDWR);
86
+ if (result_fd == -1 )
87
+ return createStringError (mapLastWindowsError (), " _open_osfhandle" );
38
88
#else
39
- if (int err = mkfifo (path.data (), 0600 ))
40
- return createStringError (std::error_code (err, std::generic_category ()),
41
- " Couldn't create fifo file: %s" , path.data ());
42
- return std::make_shared<FifoFile>(path);
89
+ if (mkfifo (path, 0600 ) == -1 )
90
+ return createStringError (std::error_code (errno, std::generic_category ()),
91
+ " mkfifo" );
92
+ EC = openFileForWrite (result_path, result_fd, sys::fs::CD_OpenExisting,
93
+ sys::fs::OF_None, 0600 );
94
+ if (EC)
95
+ return errorCodeToError (EC);
43
96
#endif
97
+ result_path.pop_back ();
98
+ return Error::success ();
44
99
}
45
100
46
- FifoFileIO::FifoFileIO (StringRef fifo_file, StringRef other_endpoint_name)
47
- : m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {}
101
+ FifoFileIO::FifoFileIO (FifoFile &&fifo_file, StringRef other_endpoint_name)
102
+ : m_fifo_file(std::move(fifo_file)),
103
+ m_other_endpoint_name (other_endpoint_name) {}
48
104
49
105
Expected<json::Value> FifoFileIO::ReadJSON (std::chrono::milliseconds timeout) {
50
106
// We use a pointer for this future, because otherwise its normal destructor
51
107
// would wait for the getline to end, rendering the timeout useless.
52
108
std::optional<std::string> line;
53
109
std::future<void > *future =
54
110
new std::future<void >(std::async (std::launch::async, [&]() {
55
- std::ifstream reader (m_fifo_file, std::ifstream::in);
56
- std::string buffer;
57
- std::getline (reader, buffer);
58
- if (!buffer.empty ())
59
- line = buffer;
111
+ rewind (m_fifo_file.m_file );
112
+ /*
113
+ * The types of messages passing through the fifo are:
114
+ * {"kind": "pid", "pid": 9223372036854775807}
115
+ * {"kind": "didAttach"}
116
+ * {"kind": "error", "error": "error message"}
117
+ *
118
+ * 512 characters should be more than enough.
119
+ */
120
+ constexpr size_t buffer_size = 512 ;
121
+ char buffer[buffer_size];
122
+ char *ptr = fgets (buffer, buffer_size, m_fifo_file.m_file );
123
+ if (ptr == nullptr || *ptr == 0 )
124
+ return ;
125
+ size_t len = strlen (buffer);
126
+ if (len <= 1 )
127
+ return ;
128
+ buffer[len - 1 ] = ' \0 ' ; // remove newline
129
+ line = buffer;
60
130
}));
61
- if (future->wait_for (timeout) == std::future_status::timeout || !line)
131
+
132
+ if (future->wait_for (timeout) == std::future_status::timeout)
62
133
// Indeed this is a leak, but it's intentional. "future" obj destructor
63
134
// will block on waiting for the worker thread to join. And the worker
64
135
// thread might be stuck in blocking I/O. Intentionally leaking the obj
@@ -69,6 +140,11 @@ Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
69
140
return createStringError (inconvertibleErrorCode (),
70
141
" Timed out trying to get messages from the " +
71
142
m_other_endpoint_name);
143
+ if (!line) {
144
+ return createStringError (inconvertibleErrorCode (),
145
+ " Failed to get messages from the " +
146
+ m_other_endpoint_name);
147
+ }
72
148
delete future;
73
149
return json::parse (*line);
74
150
}
@@ -78,8 +154,12 @@ Error FifoFileIO::SendJSON(const json::Value &json,
78
154
bool done = false ;
79
155
std::future<void > *future =
80
156
new std::future<void >(std::async (std::launch::async, [&]() {
81
- std::ofstream writer (m_fifo_file, std::ofstream::out);
82
- writer << JSONToString (json) << std::endl;
157
+ rewind (m_fifo_file.m_file );
158
+ auto payload = JSONToString (json);
159
+ if (fputs (payload.c_str (), m_fifo_file.m_file ) == EOF ||
160
+ fputc (' \n ' , m_fifo_file.m_file ) == EOF) {
161
+ return ;
162
+ }
83
163
done = true ;
84
164
}));
85
165
if (future->wait_for (timeout) == std::future_status::timeout || !done) {
@@ -98,4 +178,17 @@ Error FifoFileIO::SendJSON(const json::Value &json,
98
178
return Error::success ();
99
179
}
100
180
181
+ Error FifoFileIO::WaitForPeer () {
182
+ #ifdef _WIN32
183
+ if (!::ConnectNamedPipe ((HANDLE)_get_osfhandle (fileno (m_fifo_file.m_file )),
184
+ NULL ) &&
185
+ GetLastError () != ERROR_PIPE_CONNECTED) {
186
+ return createStringError (mapLastWindowsError (),
187
+ " Failed to connect to the " +
188
+ m_other_endpoint_name);
189
+ }
190
+ #endif
191
+ return Error::success ();
192
+ }
193
+
101
194
} // namespace lldb_dap
0 commit comments