1 | //===-- llvm/Support/raw_socket_stream.h - Socket streams --*- 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 | // This file contains raw_ostream implementations for streams to communicate |
10 | // via UNIX sockets |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_SUPPORT_RAW_SOCKET_STREAM_H |
15 | #define LLVM_SUPPORT_RAW_SOCKET_STREAM_H |
16 | |
17 | #include "llvm/Support/Threading.h" |
18 | #include "llvm/Support/raw_ostream.h" |
19 | |
20 | #include <atomic> |
21 | #include <chrono> |
22 | |
23 | namespace llvm { |
24 | |
25 | class raw_socket_stream; |
26 | |
27 | #ifdef _WIN32 |
28 | /// Ensures proper initialization and cleanup of winsock resources |
29 | /// |
30 | /// Make sure that calls to WSAStartup and WSACleanup are balanced. |
31 | class WSABalancer { |
32 | public: |
33 | WSABalancer(); |
34 | ~WSABalancer(); |
35 | }; |
36 | #endif // _WIN32 |
37 | |
38 | /// Manages a passive (i.e., listening) UNIX domain socket |
39 | /// |
40 | /// The ListeningSocket class encapsulates a UNIX domain socket that can listen |
41 | /// and accept incoming connections. ListeningSocket is portable and supports |
42 | /// Windows builds begining with Insider Build 17063. ListeningSocket is |
43 | /// designed for server-side operations, working alongside \p raw_socket_streams |
44 | /// that function as client connections. |
45 | /// |
46 | /// Usage example: |
47 | /// \code{.cpp} |
48 | /// std::string Path = "/path/to/socket" |
49 | /// Expected<ListeningSocket> S = ListeningSocket::createUnix(Path); |
50 | /// |
51 | /// if (S) { |
52 | /// Expected<std::unique_ptr<raw_socket_stream>> connection = S->accept(); |
53 | /// if (connection) { |
54 | /// // Use the accepted raw_socket_stream for communication. |
55 | /// } |
56 | /// } |
57 | /// \endcode |
58 | /// |
59 | class ListeningSocket { |
60 | |
61 | std::atomic<int> FD; |
62 | std::string SocketPath; // Not modified after construction |
63 | |
64 | /// If a seperate thread calls ListeningSocket::shutdown, the ListeningSocket |
65 | /// file descriptor (FD) could be closed while ::poll is waiting for it to be |
66 | /// ready to perform a I/O operations. ::poll will continue to block even |
67 | /// after FD is closed so use a self-pipe mechanism to get ::poll to return |
68 | int PipeFD[2]; // Not modified after construction other then move constructor |
69 | |
70 | ListeningSocket(int SocketFD, StringRef SocketPath, int PipeFD[2]); |
71 | |
72 | #ifdef _WIN32 |
73 | WSABalancer _; |
74 | #endif // _WIN32 |
75 | |
76 | public: |
77 | ~ListeningSocket(); |
78 | ListeningSocket(ListeningSocket &&LS); |
79 | ListeningSocket(const ListeningSocket &LS) = delete; |
80 | ListeningSocket &operator=(const ListeningSocket &) = delete; |
81 | |
82 | /// Closes the FD, unlinks the socket file, and writes to PipeFD. |
83 | /// |
84 | /// After the construction of the ListeningSocket, shutdown is signal safe if |
85 | /// it is called during the lifetime of the object. shutdown can be called |
86 | /// concurrently with ListeningSocket::accept as writing to PipeFD will cause |
87 | /// a blocking call to ::poll to return. |
88 | /// |
89 | /// Once shutdown is called there is no way to reinitialize ListeningSocket. |
90 | void shutdown(); |
91 | |
92 | /// Accepts an incoming connection on the listening socket. This method can |
93 | /// optionally either block until a connection is available or timeout after a |
94 | /// specified amount of time has passed. By default the method will block |
95 | /// until the socket has recieved a connection. |
96 | /// |
97 | /// \param Timeout An optional timeout duration in milliseconds. Setting |
98 | /// Timeout to -1 causes accept to block indefinitely |
99 | /// |
100 | Expected<std::unique_ptr<raw_socket_stream>> |
101 | accept(std::chrono::milliseconds Timeout = std::chrono::milliseconds(-1)); |
102 | |
103 | /// Creates a listening socket bound to the specified file system path. |
104 | /// Handles the socket creation, binding, and immediately starts listening for |
105 | /// incoming connections. |
106 | /// |
107 | /// \param SocketPath The file system path where the socket will be created |
108 | /// \param MaxBacklog The max number of connections in a socket's backlog |
109 | /// |
110 | static Expected<ListeningSocket> createUnix( |
111 | StringRef SocketPath, |
112 | int MaxBacklog = llvm::hardware_concurrency().compute_thread_count()); |
113 | }; |
114 | |
115 | //===----------------------------------------------------------------------===// |
116 | // raw_socket_stream |
117 | //===----------------------------------------------------------------------===// |
118 | |
119 | class raw_socket_stream : public raw_fd_stream { |
120 | uint64_t current_pos() const override { return 0; } |
121 | #ifdef _WIN32 |
122 | WSABalancer _; |
123 | #endif // _WIN32 |
124 | |
125 | public: |
126 | raw_socket_stream(int SocketFD); |
127 | /// Create a \p raw_socket_stream connected to the UNIX domain socket at \p |
128 | /// SocketPath. |
129 | static Expected<std::unique_ptr<raw_socket_stream>> |
130 | createConnectedUnix(StringRef SocketPath); |
131 | ~raw_socket_stream(); |
132 | }; |
133 | |
134 | } // end namespace llvm |
135 | |
136 | #endif |
137 | |