xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.cpp (revision 2ccfa855b2fc331819953e3de1b1c15ce5b95a7e)
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() = default;
47  SimpleRemoteEPCTransport::~SimpleRemoteEPCTransport() = default;
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  #if LLVM_ENABLE_THREADS
74    ListenerThread.join();
75  #endif
76  }
77  
78  Error FDSimpleRemoteEPCTransport::start() {
79  #if LLVM_ENABLE_THREADS
80    ListenerThread = std::thread([this]() { listenLoop(); });
81    return Error::success();
82  #endif
83    llvm_unreachable("Should not be called with LLVM_ENABLE_THREADS=Off");
84  }
85  
86  Error FDSimpleRemoteEPCTransport::sendMessage(SimpleRemoteEPCOpcode OpC,
87                                                uint64_t SeqNo,
88                                                ExecutorAddr 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 (Disconnected)
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    if (Disconnected)
113      return; // Return if already disconnected.
114  
115    Disconnected = true;
116    bool CloseOutFD = InFD != OutFD;
117  
118    // Close InFD.
119    while (close(InFD) == -1) {
120      if (errno == EBADF)
121        break;
122    }
123  
124    // Close OutFD.
125    if (CloseOutFD) {
126      while (close(OutFD) == -1) {
127        if (errno == EBADF)
128          break;
129      }
130    }
131  }
132  
133  static Error makeUnexpectedEOFError() {
134    return make_error<StringError>("Unexpected end-of-file",
135                                   inconvertibleErrorCode());
136  }
137  
138  Error FDSimpleRemoteEPCTransport::readBytes(char *Dst, size_t Size,
139                                              bool *IsEOF) {
140    assert((Size == 0 || Dst) && "Attempt to read into null.");
141    ssize_t Completed = 0;
142    while (Completed < static_cast<ssize_t>(Size)) {
143      ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed);
144      if (Read <= 0) {
145        auto ErrNo = errno;
146        if (Read == 0) {
147          if (Completed == 0 && IsEOF) {
148            *IsEOF = true;
149            return Error::success();
150          } else
151            return makeUnexpectedEOFError();
152        } else if (ErrNo == EAGAIN || ErrNo == EINTR)
153          continue;
154        else {
155          std::lock_guard<std::mutex> Lock(M);
156          if (Disconnected && IsEOF) { // disconnect called,  pretend this is EOF.
157            *IsEOF = true;
158            return Error::success();
159          }
160          return errorCodeToError(
161              std::error_code(ErrNo, std::generic_category()));
162        }
163      }
164      Completed += Read;
165    }
166    return Error::success();
167  }
168  
169  int FDSimpleRemoteEPCTransport::writeBytes(const char *Src, size_t Size) {
170    assert((Size == 0 || Src) && "Attempt to append from null.");
171    ssize_t Completed = 0;
172    while (Completed < static_cast<ssize_t>(Size)) {
173      ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed);
174      if (Written < 0) {
175        auto ErrNo = errno;
176        if (ErrNo == EAGAIN || ErrNo == EINTR)
177          continue;
178        else
179          return ErrNo;
180      }
181      Completed += Written;
182    }
183    return 0;
184  }
185  
186  void FDSimpleRemoteEPCTransport::listenLoop() {
187    Error Err = Error::success();
188    do {
189  
190      char HeaderBuffer[FDMsgHeader::Size];
191      // Read the header buffer.
192      {
193        bool IsEOF = false;
194        if (auto Err2 = readBytes(HeaderBuffer, FDMsgHeader::Size, &IsEOF)) {
195          Err = joinErrors(std::move(Err), std::move(Err2));
196          break;
197        }
198        if (IsEOF)
199          break;
200      }
201  
202      // Decode header buffer.
203      uint64_t MsgSize;
204      SimpleRemoteEPCOpcode OpC;
205      uint64_t SeqNo;
206      ExecutorAddr TagAddr;
207  
208      MsgSize =
209          *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset));
210      OpC = static_cast<SimpleRemoteEPCOpcode>(static_cast<uint64_t>(
211          *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset))));
212      SeqNo =
213          *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset));
214      TagAddr.setValue(
215          *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset)));
216  
217      if (MsgSize < FDMsgHeader::Size) {
218        Err = joinErrors(std::move(Err),
219                         make_error<StringError>("Message size too small",
220                                                 inconvertibleErrorCode()));
221        break;
222      }
223  
224      // Read the argument bytes.
225      SimpleRemoteEPCArgBytesVector ArgBytes;
226      ArgBytes.resize(MsgSize - FDMsgHeader::Size);
227      if (auto Err2 = readBytes(ArgBytes.data(), ArgBytes.size())) {
228        Err = joinErrors(std::move(Err), std::move(Err2));
229        break;
230      }
231  
232      if (auto Action = C.handleMessage(OpC, SeqNo, TagAddr, ArgBytes)) {
233        if (*Action == SimpleRemoteEPCTransportClient::EndSession)
234          break;
235      } else {
236        Err = joinErrors(std::move(Err), Action.takeError());
237        break;
238      }
239    } while (true);
240  
241    // Attempt to close FDs, set Disconnected to true so that subsequent
242    // sendMessage calls fail.
243    disconnect();
244  
245    // Call up to the client to handle the disconnection.
246    C.handleDisconnect(std::move(Err));
247  }
248  
249  } // end namespace orc
250  } // end namespace llvm
251