xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.cpp (revision 767b328e506ef069ecbb89b7cc9e2da7f8f84c6c)
1 //===------ SimpleRemoteEPCUtils.cpp - Utils for Simple Remote EPC --------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Message definitions and other utilities for SimpleRemoteEPC and
10 // SimpleRemoteEPCServer.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
15 #include "llvm/Support/Endian.h"
16 #include "llvm/Support/FormatVariadic.h"
17 
18 #if !defined(_MSC_VER) && !defined(__MINGW32__)
19 #include <unistd.h>
20 #else
21 #include <io.h>
22 #endif
23 
24 namespace {
25 
26 struct FDMsgHeader {
27   static constexpr unsigned MsgSizeOffset = 0;
28   static constexpr unsigned OpCOffset = MsgSizeOffset + sizeof(uint64_t);
29   static constexpr unsigned SeqNoOffset = OpCOffset + sizeof(uint64_t);
30   static constexpr unsigned TagAddrOffset = SeqNoOffset + sizeof(uint64_t);
31   static constexpr unsigned Size = TagAddrOffset + sizeof(uint64_t);
32 };
33 
34 } // namespace
35 
36 namespace llvm {
37 namespace orc {
38 namespace SimpleRemoteEPCDefaultBootstrapSymbolNames {
39 
40 const char *ExecutorSessionObjectName =
41     "__llvm_orc_SimpleRemoteEPC_dispatch_ctx";
42 const char *DispatchFnName = "__llvm_orc_SimpleRemoteEPC_dispatch_fn";
43 
44 } // end namespace SimpleRemoteEPCDefaultBootstrapSymbolNames
45 
46 SimpleRemoteEPCTransportClient::~SimpleRemoteEPCTransportClient() {}
47 SimpleRemoteEPCTransport::~SimpleRemoteEPCTransport() {}
48 
49 Expected<std::unique_ptr<FDSimpleRemoteEPCTransport>>
50 FDSimpleRemoteEPCTransport::Create(SimpleRemoteEPCTransportClient &C, int InFD,
51                                    int OutFD) {
52 #if LLVM_ENABLE_THREADS
53   if (InFD == -1)
54     return make_error<StringError>("Invalid input file descriptor " +
55                                        Twine(InFD),
56                                    inconvertibleErrorCode());
57   if (OutFD == -1)
58     return make_error<StringError>("Invalid output file descriptor " +
59                                        Twine(OutFD),
60                                    inconvertibleErrorCode());
61   std::unique_ptr<FDSimpleRemoteEPCTransport> FDT(
62       new FDSimpleRemoteEPCTransport(C, InFD, OutFD));
63   return std::move(FDT);
64 #else
65   return make_error<StringError>("FD-based SimpleRemoteEPC transport requires "
66                                  "thread support, but llvm was built with "
67                                  "LLVM_ENABLE_THREADS=Off",
68                                  inconvertibleErrorCode());
69 #endif
70 }
71 
72 FDSimpleRemoteEPCTransport::FDSimpleRemoteEPCTransport(
73     SimpleRemoteEPCTransportClient &C, int InFD, int OutFD)
74     : C(C), InFD(InFD), OutFD(OutFD) {
75 #if LLVM_ENABLE_THREADS
76   ListenerThread = std::thread([this]() { listenLoop(); });
77 #endif
78 }
79 
80 FDSimpleRemoteEPCTransport::~FDSimpleRemoteEPCTransport() {
81 #if LLVM_ENABLE_THREADS
82   ListenerThread.join();
83 #endif
84 }
85 
86 Error FDSimpleRemoteEPCTransport::sendMessage(SimpleRemoteEPCOpcode OpC,
87                                               uint64_t SeqNo,
88                                               ExecutorAddress TagAddr,
89                                               ArrayRef<char> ArgBytes) {
90   char HeaderBuffer[FDMsgHeader::Size];
91 
92   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset)) =
93       FDMsgHeader::Size + ArgBytes.size();
94   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset)) =
95       static_cast<uint64_t>(OpC);
96   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset)) = SeqNo;
97   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset)) =
98       TagAddr.getValue();
99 
100   std::lock_guard<std::mutex> Lock(M);
101   if (OutFD == -1)
102     return make_error<StringError>("FD-transport disconnected",
103                                    inconvertibleErrorCode());
104   if (int ErrNo = writeBytes(HeaderBuffer, FDMsgHeader::Size))
105     return errorCodeToError(std::error_code(ErrNo, std::generic_category()));
106   if (int ErrNo = writeBytes(ArgBytes.data(), ArgBytes.size()))
107     return errorCodeToError(std::error_code(ErrNo, std::generic_category()));
108   return Error::success();
109 }
110 
111 void FDSimpleRemoteEPCTransport::disconnect() {
112   int CloseInFD = -1, CloseOutFD = -1;
113   {
114     std::lock_guard<std::mutex> Lock(M);
115     std::swap(InFD, CloseInFD);
116     std::swap(OutFD, CloseOutFD);
117   }
118 
119   // If CloseOutFD == CloseInFD then set CloseOutFD to -1 up-front so that we
120   // don't double-close.
121   if (CloseOutFD == CloseInFD)
122     CloseOutFD = -1;
123 
124   // Close InFD.
125   if (CloseInFD != -1)
126     while (close(CloseInFD) == -1) {
127       if (errno == EBADF)
128         break;
129     }
130 
131   // Close OutFD.
132   if (CloseOutFD != -1) {
133     while (close(CloseOutFD) == -1) {
134       if (errno == EBADF)
135         break;
136     }
137   }
138 }
139 
140 static Error makeUnexpectedEOFError() {
141   return make_error<StringError>("Unexpected end-of-file",
142                                  inconvertibleErrorCode());
143 }
144 
145 Error FDSimpleRemoteEPCTransport::readBytes(char *Dst, size_t Size,
146                                             bool *IsEOF) {
147   assert(Dst && "Attempt to read into null.");
148   ssize_t Completed = 0;
149   while (Completed < static_cast<ssize_t>(Size)) {
150     ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed);
151     if (Read <= 0) {
152       auto ErrNo = errno;
153       if (Read == 0) {
154         if (Completed == 0 && IsEOF) {
155           *IsEOF = true;
156           return Error::success();
157         } else
158           return makeUnexpectedEOFError();
159       } else if (ErrNo == EAGAIN || ErrNo == EINTR)
160         continue;
161       else {
162         std::lock_guard<std::mutex> Lock(M);
163         if (InFD == -1 && IsEOF) { // Disconnected locally. Pretend this is EOF.
164           *IsEOF = true;
165           return Error::success();
166         }
167         return errorCodeToError(
168             std::error_code(ErrNo, std::generic_category()));
169       }
170     }
171     Completed += Read;
172   }
173   return Error::success();
174 }
175 
176 int FDSimpleRemoteEPCTransport::writeBytes(const char *Src, size_t Size) {
177   assert(Src && "Attempt to append from null.");
178   ssize_t Completed = 0;
179   while (Completed < static_cast<ssize_t>(Size)) {
180     ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed);
181     if (Written < 0) {
182       auto ErrNo = errno;
183       if (ErrNo == EAGAIN || ErrNo == EINTR)
184         continue;
185       else
186         return ErrNo;
187     }
188     Completed += Written;
189   }
190   return 0;
191 }
192 
193 void FDSimpleRemoteEPCTransport::listenLoop() {
194   Error Err = Error::success();
195   do {
196 
197     char HeaderBuffer[FDMsgHeader::Size];
198     // Read the header buffer.
199     {
200       bool IsEOF;
201       if (auto Err2 = readBytes(HeaderBuffer, FDMsgHeader::Size, &IsEOF)) {
202         Err = joinErrors(std::move(Err), std::move(Err2));
203         break;
204       }
205       if (IsEOF)
206         break;
207     }
208 
209     // Decode header buffer.
210     uint64_t MsgSize;
211     SimpleRemoteEPCOpcode OpC;
212     uint64_t SeqNo;
213     ExecutorAddress TagAddr;
214 
215     MsgSize =
216         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset));
217     OpC = static_cast<SimpleRemoteEPCOpcode>(static_cast<uint64_t>(
218         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset))));
219     SeqNo =
220         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset));
221     TagAddr.setValue(
222         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset)));
223 
224     if (MsgSize < FDMsgHeader::Size) {
225       Err = joinErrors(std::move(Err),
226                        make_error<StringError>("Message size too small",
227                                                inconvertibleErrorCode()));
228       break;
229     }
230 
231     // Read the argument bytes.
232     SimpleRemoteEPCArgBytesVector ArgBytes;
233     ArgBytes.resize(MsgSize - FDMsgHeader::Size);
234     if (auto Err2 = readBytes(ArgBytes.data(), ArgBytes.size())) {
235       Err = joinErrors(std::move(Err), std::move(Err2));
236       break;
237     }
238 
239     if (auto Action = C.handleMessage(OpC, SeqNo, TagAddr, ArgBytes)) {
240       if (*Action == SimpleRemoteEPCTransportClient::EndSession)
241         break;
242     } else {
243       Err = joinErrors(std::move(Err), Action.takeError());
244       break;
245     }
246   } while (true);
247 
248   C.handleDisconnect(std::move(Err));
249 }
250 
251 } // end namespace orc
252 } // end namespace llvm
253