1 //===-- FifoFiles.cpp -------------------------------------------*- C++ -*-===// 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 #include "FifoFiles.h" 10 #include "JSONUtils.h" 11 12 #if !defined(_WIN32) 13 #include <sys/stat.h> 14 #include <sys/types.h> 15 #include <unistd.h> 16 #endif 17 18 #include <chrono> 19 #include <fstream> 20 #include <future> 21 #include <optional> 22 23 using namespace llvm; 24 25 namespace lldb_dap { 26 27 FifoFile::FifoFile(StringRef path) : m_path(path) {} 28 29 FifoFile::~FifoFile() { 30 #if !defined(_WIN32) 31 unlink(m_path.c_str()); 32 #endif 33 } 34 35 Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) { 36 #if defined(_WIN32) 37 return createStringError(inconvertibleErrorCode(), "Unimplemented"); 38 #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); 43 #endif 44 } 45 46 FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name) 47 : m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {} 48 49 Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) { 50 // We use a pointer for this future, because otherwise its normal destructor 51 // would wait for the getline to end, rendering the timeout useless. 52 std::optional<std::string> line; 53 std::future<void> *future = 54 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; 60 })); 61 if (future->wait_for(timeout) == std::future_status::timeout || !line) 62 // Indeed this is a leak, but it's intentional. "future" obj destructor 63 // will block on waiting for the worker thread to join. And the worker 64 // thread might be stuck in blocking I/O. Intentionally leaking the obj 65 // as a hack to avoid blocking main thread, and adding annotation to 66 // supress static code inspection warnings 67 68 // coverity[leaked_storage] 69 return createStringError(inconvertibleErrorCode(), 70 "Timed out trying to get messages from the " + 71 m_other_endpoint_name); 72 delete future; 73 return json::parse(*line); 74 } 75 76 Error FifoFileIO::SendJSON(const json::Value &json, 77 std::chrono::milliseconds timeout) { 78 bool done = false; 79 std::future<void> *future = 80 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; 83 done = true; 84 })); 85 if (future->wait_for(timeout) == std::future_status::timeout || !done) { 86 // Indeed this is a leak, but it's intentional. "future" obj destructor will 87 // block on waiting for the worker thread to join. And the worker thread 88 // might be stuck in blocking I/O. Intentionally leaking the obj as a hack 89 // to avoid blocking main thread, and adding annotation to supress static 90 // code inspection warnings" 91 92 // coverity[leaked_storage] 93 return createStringError(inconvertibleErrorCode(), 94 "Timed out trying to send messages to the " + 95 m_other_endpoint_name); 96 } 97 delete future; 98 return Error::success(); 99 } 100 101 } // namespace lldb_dap 102