1/*
2 * IXSelectInterruptPipe.cpp
3 * Author: Benjamin Sergeant
4 * Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
5 */
6
7//
8// On UNIX we use pipes to wake up select. There is no way to do that
9// on Windows so this file is compiled out on Windows.
10//
11#ifndef _WIN32
12
13#include "IXSelectInterruptPipe.h"
14
15#include <assert.h>
16#include <errno.h>
17#include <fcntl.h>
18#include <sstream>
19#include <string.h> // for strerror
20#include <unistd.h> // for write
21
22namespace ix
23{
24 // File descriptor at index 0 in _fildes is the read end of the pipe
25 // File descriptor at index 1 in _fildes is the write end of the pipe
26 const int SelectInterruptPipe::kPipeReadIndex = 0;
27 const int SelectInterruptPipe::kPipeWriteIndex = 1;
28
29 SelectInterruptPipe::SelectInterruptPipe()
30 {
31 _fildes[kPipeReadIndex] = -1;
32 _fildes[kPipeWriteIndex] = -1;
33 }
34
35 SelectInterruptPipe::~SelectInterruptPipe()
36 {
37 ::close(_fildes[kPipeReadIndex]);
38 ::close(_fildes[kPipeWriteIndex]);
39 _fildes[kPipeReadIndex] = -1;
40 _fildes[kPipeWriteIndex] = -1;
41 }
42
43 bool SelectInterruptPipe::init(std::string& errorMsg)
44 {
45 std::lock_guard<std::mutex> lock(_fildesMutex);
46
47 // calling init twice is a programming error
48 assert(_fildes[kPipeReadIndex] == -1);
49 assert(_fildes[kPipeWriteIndex] == -1);
50
51 if (pipe(_fildes) < 0)
52 {
53 std::stringstream ss;
54 ss << "SelectInterruptPipe::init() failed in pipe() call"
55 << " : " << strerror(errno);
56 errorMsg = ss.str();
57 return false;
58 }
59
60 if (fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK) == -1)
61 {
62 std::stringstream ss;
63 ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
64 << " : " << strerror(errno);
65 errorMsg = ss.str();
66
67 _fildes[kPipeReadIndex] = -1;
68 _fildes[kPipeWriteIndex] = -1;
69 return false;
70 }
71
72 if (fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK) == -1)
73 {
74 std::stringstream ss;
75 ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
76 << " : " << strerror(errno);
77 errorMsg = ss.str();
78
79 _fildes[kPipeReadIndex] = -1;
80 _fildes[kPipeWriteIndex] = -1;
81 return false;
82 }
83
84#ifdef F_SETNOSIGPIPE
85 if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
86 {
87 std::stringstream ss;
88 ss << "SelectInterruptPipe::init() failed in fcntl(.... F_SETNOSIGPIPE) call"
89 << " : " << strerror(errno);
90 errorMsg = ss.str();
91
92 _fildes[kPipeReadIndex] = -1;
93 _fildes[kPipeWriteIndex] = -1;
94 return false;
95 }
96
97 if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
98 {
99 std::stringstream ss;
100 ss << "SelectInterruptPipe::init() failed in fcntl(..., F_SETNOSIGPIPE) call"
101 << " : " << strerror(errno);
102 errorMsg = ss.str();
103
104 _fildes[kPipeReadIndex] = -1;
105 _fildes[kPipeWriteIndex] = -1;
106 return false;
107 }
108#endif
109
110 return true;
111 }
112
113 bool SelectInterruptPipe::notify(uint64_t value)
114 {
115 std::lock_guard<std::mutex> lock(_fildesMutex);
116
117 int fd = _fildes[kPipeWriteIndex];
118 if (fd == -1) return false;
119
120 ssize_t ret = -1;
121 do
122 {
123 ret = ::write(fd, &value, sizeof(value));
124 } while (ret == -1 && errno == EINTR);
125
126 // we should write 8 bytes for an uint64_t
127 return ret == 8;
128 }
129
130 // TODO: return max uint64_t for errors ?
131 uint64_t SelectInterruptPipe::read()
132 {
133 std::lock_guard<std::mutex> lock(_fildesMutex);
134
135 int fd = _fildes[kPipeReadIndex];
136
137 uint64_t value = 0;
138
139 ssize_t ret = -1;
140 do
141 {
142 ret = ::read(fd, &value, sizeof(value));
143 } while (ret == -1 && errno == EINTR);
144
145 return value;
146 }
147
148 bool SelectInterruptPipe::clear()
149 {
150 return true;
151 }
152
153 int SelectInterruptPipe::getFd() const
154 {
155 std::lock_guard<std::mutex> lock(_fildesMutex);
156
157 return _fildes[kPipeReadIndex];
158 }
159} // namespace ix
160
161#endif // !_WIN32
162