~mir-team/mir/rename-everything

« back to all changes in this revision

Viewing changes to src/client/rpc/stream_socket_transport.cpp

  • Committer: Tarmac
  • Author(s): Christopher James Halse Rogers, Christopher James Halse Rogers, Kevin Gunn
  • Date: 2015-01-28 06:19:07 UTC
  • mfrom: (2239.5.40 add-dispatchable-interface)
  • Revision ID: tarmac-20150128061907-0z33twh42elsk9ml
Add a Dispatchable interface, and transition StreamSocketTransport.
(LP: #1397375)

The Dispatchable interface is useful for anything that can provide a monitorable file descriptor. Fixes: https://bugs.launchpad.net/bugs/1397375.

Approved by PS Jenkins bot, Robert Carr, Alexandros Frantzis, Cemil Azizoglu.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
 
24
24
#include <system_error>
25
25
 
26
 
#include <signal.h>
27
26
#include <errno.h>
28
27
#include <sys/epoll.h>
29
28
#include <sys/types.h>
34
33
#include <boost/throw_exception.hpp>
35
34
 
36
35
namespace mclr = mir::client::rpc;
 
36
namespace md = mir::dispatch;
37
37
 
38
38
mclr::StreamSocketTransport::StreamSocketTransport(mir::Fd const& fd)
39
39
    : socket_fd{fd}
40
40
{
41
 
    init();
42
41
}
43
42
 
44
43
mclr::StreamSocketTransport::StreamSocketTransport(std::string const& socket_path)
46
45
{
47
46
}
48
47
 
49
 
mclr::StreamSocketTransport::~StreamSocketTransport()
50
 
{
51
 
    int dummy{0};
52
 
    send(shutdown_fd, &dummy, sizeof(dummy), MSG_NOSIGNAL);
53
 
    if (io_service_thread.joinable())
54
 
    {
55
 
        io_service_thread.join();
56
 
    }
57
 
    close(shutdown_fd);
58
 
}
59
 
 
60
48
void mclr::StreamSocketTransport::register_observer(std::shared_ptr<Observer> const& observer)
61
49
{
62
50
    std::lock_guard<decltype(observer_mutex)> lock(observer_mutex);
176
164
        mir::send_fds(socket_fd, fds);
177
165
}
178
166
 
179
 
void mclr::StreamSocketTransport::init()
 
167
mir::Fd mclr::StreamSocketTransport::watch_fd() const
180
168
{
181
 
    // We use sockets rather than a pipe so that we can control
182
 
    // EPIPE behaviour; we don't want SIGPIPE when the IO loop terminates.
183
 
    int socket_fds[2];
184
 
    socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
185
 
    this->shutdown_fd = mir::Fd{socket_fds[1]};
 
169
    return socket_fd;
 
170
}
186
171
 
187
 
    auto shutdown_fd = mir::Fd{socket_fds[0]};
188
 
    io_service_thread = std::thread([this, shutdown_fd]
 
172
bool mclr::StreamSocketTransport::dispatch(md::FdEvents events)
 
173
{
 
174
    if (events & (md::FdEvent::remote_closed | md::FdEvent::error))
189
175
    {
190
 
        // Our IO threads must not receive any signals
191
 
        sigset_t all_signals;
192
 
        sigfillset(&all_signals);
193
 
 
194
 
        if (auto error = pthread_sigmask(SIG_BLOCK, &all_signals, NULL))
195
 
            BOOST_THROW_EXCEPTION(
196
 
                boost::enable_error_info(
197
 
                    std::runtime_error("Failed to block signals on IO thread")) << boost::errinfo_errno(error));
198
 
 
199
 
        mir::set_thread_name("Client IO loop");
200
 
 
201
 
        int epoll_fd = epoll_create1(0);
202
 
 
203
 
        epoll_event event;
204
 
        // Make valgrind happy, harder
205
 
        memset(&event, 0, sizeof(event));
206
 
 
207
 
        event.events = EPOLLIN | EPOLLRDHUP;
208
 
        event.data.fd = socket_fd;
209
 
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);
210
 
 
211
 
        event.events = EPOLLIN | EPOLLRDHUP;
212
 
        event.data.fd = shutdown_fd;
213
 
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, shutdown_fd, &event);
214
 
 
215
 
        bool shutdown_requested{false};
216
 
        while (!shutdown_requested)
 
176
        if (events & md::FdEvent::readable)
217
177
        {
218
 
            epoll_event event;
219
 
            epoll_wait(epoll_fd, &event, 1, -1);
220
 
            if (event.data.fd == socket_fd)
221
 
            {
222
 
                if (event.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
223
 
                {
224
 
                    if (event.events & EPOLLIN)
225
 
                    {
226
 
                        // If the remote end shut down cleanly it's possible there's some more
227
 
                        // data left to read, or that reads will now return 0 (EOF)
228
 
                        //
229
 
                        // If there's more data left to read, notify of this before disconnect.
230
 
                        int dummy;
231
 
                        if (recv(socket_fd, &dummy, sizeof(dummy), MSG_PEEK | MSG_NOSIGNAL) > 0)
232
 
                        {
233
 
                            try
234
 
                            {
235
 
                                notify_data_available();
236
 
                            }
237
 
                            catch(...)
238
 
                            {
239
 
                                //It's quite likely that notify_data_available() will lead to
240
 
                                //an exception being thrown; after all, the remote has closed
241
 
                                //the connection.
242
 
                                //
243
 
                                //This doesn't matter; we're already shutting down.
244
 
                            }
245
 
                        }
246
 
                    }
247
 
                    notify_disconnected();
248
 
                    shutdown_requested = true;
249
 
                }
250
 
                else if (event.events & EPOLLIN)
251
 
                {
252
 
                    try
253
 
                    {
254
 
                        notify_data_available();
255
 
                    }
256
 
                    catch (socket_disconnected_error &err)
257
 
                    {
258
 
                        // We've already notified of disconnection.
259
 
                        shutdown_requested = true;
260
 
                    }
261
 
                    // These need not be fatal.
262
 
                    catch (fd_reception_error &err)
263
 
                    {
264
 
                    }
265
 
                    catch (socket_error &err)
266
 
                    {
267
 
                    }
268
 
                    catch (...)
269
 
                    {
270
 
                        // We've no idea what the problem is, so clean up as best we can.
271
 
                        notify_disconnected();
272
 
                        shutdown_requested = true;
273
 
                    }
274
 
                }
275
 
            }
276
 
            if (event.data.fd == shutdown_fd)
277
 
            {
278
 
                shutdown_requested = true;
 
178
            // If the remote end shut down cleanly it's possible there's some more
 
179
            // data left to read, or that reads will now return 0 (EOF)
 
180
            //
 
181
            // If there's more data left to read, notify of this before disconnect.
 
182
            int dummy;
 
183
            if (recv(socket_fd, &dummy, sizeof(dummy), MSG_PEEK | MSG_NOSIGNAL) > 0)
 
184
            {
 
185
                notify_data_available();
 
186
                return true;
279
187
            }
280
188
        }
281
 
        ::close(epoll_fd);
282
 
    });
 
189
        notify_disconnected();
 
190
        return false;
 
191
    }
 
192
    else if (events & md::FdEvent::readable)
 
193
    {
 
194
        notify_data_available();
 
195
    }
 
196
    return true;
 
197
}
 
198
 
 
199
md::FdEvents mclr::StreamSocketTransport::relevant_events() const
 
200
{
 
201
    return md::FdEvent::readable | md::FdEvent::remote_closed;
283
202
}
284
203
 
285
204
mir::Fd mclr::StreamSocketTransport::open_socket(std::string const& path)