9
9
*/
10
10
#include <truffleruby-impl.h>
11
11
#include <fcntl.h>
12
+ #include <errno.h>
12
13
#include <ruby/thread.h>
14
+ #include <time.h>
13
15
14
16
// For howmany()
15
17
#ifdef HAVE_SYS_PARAM_H
@@ -105,6 +107,65 @@ void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src) {
105
107
memcpy (dst -> fdset , src -> fdset , size );
106
108
}
107
109
110
+ void rb_fd_init_copy (rb_fdset_t * dst , rb_fdset_t * src ) {
111
+ size_t size = howmany (rb_fd_max (src ), NFDBITS ) * sizeof (fd_mask );
112
+
113
+ if (size < sizeof (fd_set )) {
114
+ size = sizeof (fd_set );
115
+ }
116
+ dst -> maxfd = src -> maxfd ;
117
+ dst -> fdset = xmalloc (size );
118
+ memcpy (dst -> fdset , src -> fdset , size );
119
+ }
120
+
121
+ static bool timespec_subtract (struct timespec * result , struct timespec x , struct timespec y ) {
122
+ /* Perform the carry for the later subtraction by updating y. */
123
+ if (x .tv_nsec < y .tv_nsec ) {
124
+ long nsec = (y .tv_nsec - x .tv_nsec ) / 1000000000 + 1 ;
125
+ y .tv_nsec -= 1000000000 * nsec ;
126
+ y .tv_sec += nsec ;
127
+ }
128
+ if (x .tv_nsec - y .tv_nsec > 1000000000 ) {
129
+ long nsec = (x .tv_nsec - y .tv_nsec ) / 1000000000 ;
130
+ y .tv_nsec += 1000000000 * nsec ;
131
+ y .tv_sec -= nsec ;
132
+ }
133
+
134
+ /* Compute the time remaining to wait.
135
+ tv_nsec is certainly positive. */
136
+ result -> tv_sec = x .tv_sec - y .tv_sec ;
137
+ result -> tv_nsec = x .tv_nsec - y .tv_nsec ;
138
+
139
+ /* Return 1 if result is negative. */
140
+ return x .tv_sec < y .tv_sec ;
141
+ }
142
+
143
+ static bool timeval_subtract (struct timeval * result , struct timeval x , struct timeval y ) {
144
+ /* Perform the carry for the later subtraction by updating y. */
145
+ if (x .tv_usec < y .tv_usec ) {
146
+ long usec = (y .tv_usec - x .tv_usec ) / 1000000 + 1 ;
147
+ y .tv_usec -= 1000000 * usec ;
148
+ y .tv_sec += usec ;
149
+ }
150
+ if (x .tv_usec - y .tv_usec > 1000000 ) {
151
+ long usec = (x .tv_usec - y .tv_usec ) / 1000000 ;
152
+ y .tv_usec += 1000000 * usec ;
153
+ y .tv_sec -= usec ;
154
+ }
155
+
156
+ /* Compute the time remaining to wait.
157
+ tv_usec is certainly positive. */
158
+ result -> tv_sec = x .tv_sec - y .tv_sec ;
159
+ result -> tv_usec = x .tv_usec - y .tv_usec ;
160
+
161
+ /* Return 1 if result is negative. */
162
+ return x .tv_sec < y .tv_sec ;
163
+ }
164
+
165
+ static int should_retry (int result ) {
166
+ return (result < 0 ) && (errno == EINTR );
167
+ }
168
+
108
169
int rb_fd_select (int n , rb_fdset_t * readfds , rb_fdset_t * writefds , rb_fdset_t * exceptfds , struct timeval * timeout ) {
109
170
fd_set * r = NULL , * w = NULL , * e = NULL ;
110
171
if (readfds ) {
@@ -122,21 +183,90 @@ int rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *e
122
183
return select (n , r , w , e , timeout );
123
184
}
124
185
186
+
125
187
// NOTE: MRI's version has more fields
126
188
struct select_set {
127
189
int max ;
128
190
rb_fdset_t * rset ;
129
191
rb_fdset_t * wset ;
130
192
rb_fdset_t * eset ;
193
+ rb_fdset_t * orig_rset ;
194
+ rb_fdset_t * orig_wset ;
195
+ rb_fdset_t * orig_eset ;
131
196
struct timeval * timeout ;
197
+ struct timeval * orig_timeout ;
132
198
};
133
199
200
+ static inline void restore_fds (rb_fdset_t * dst , rb_fdset_t * src ) {
201
+ if (dst ) {
202
+ rb_fd_dup (dst , src );
203
+ }
204
+ }
205
+
206
+ static bool update_timeout (struct timeval * timeout , struct timeval * orig_timeout , struct timespec * starttime ) {
207
+ struct timespec currenttime ;
208
+ struct timespec difftime ;
209
+ struct timeval difftimeout ;
210
+ bool timeleft = true;
211
+ if (timeout ) {
212
+ clock_gettime (CLOCK_MONOTONIC , & currenttime );
213
+ timespec_subtract (& difftime , currenttime , * starttime );
214
+ difftimeout .tv_sec = difftime .tv_sec ;
215
+ difftimeout .tv_usec = difftime .tv_nsec / 1000 ;
216
+ timeleft = timeval_subtract (timeout , * orig_timeout , difftimeout );
217
+ }
218
+
219
+ return timeleft ;
220
+ }
221
+
134
222
static void * rb_thread_fd_select_blocking (void * data ) {
135
223
struct select_set * set = (struct select_set * )data ;
136
- int result = rb_fd_select (set -> max , set -> rset , set -> wset , set -> eset , set -> timeout );
224
+ struct timespec starttime ;
225
+
226
+ if (set -> timeout ) {
227
+ clock_gettime (CLOCK_MONOTONIC , & starttime );
228
+ }
229
+
230
+ int result = 0 ;
231
+ bool timeleft = true;
232
+ do {
233
+ restore_fds (set -> rset , set -> orig_rset );
234
+ restore_fds (set -> wset , set -> orig_wset );
235
+ restore_fds (set -> eset , set -> orig_eset );
236
+ timeleft = update_timeout (set -> timeout , set -> orig_timeout , & starttime );
237
+ if (!timeleft ) {
238
+ break ;
239
+ }
240
+ result = rb_fd_select (set -> max , set -> rset , set -> wset , set -> eset , set -> timeout );
241
+ } while (should_retry (result ));
137
242
return (void * )(long )result ;
138
243
}
139
244
245
+ static void * rb_thread_fd_select_internal (void * sets ) {
246
+ return rb_thread_call_without_gvl (rb_thread_fd_select_blocking , sets , RUBY_UBF_IO , 0 );
247
+ }
248
+
249
+ static void rb_thread_fd_select_set_free (struct select_set * sets ) {
250
+ if (sets -> orig_rset ) {
251
+ rb_fd_term (sets -> orig_rset );
252
+ }
253
+ if (sets -> orig_wset ) {
254
+ rb_fd_term (sets -> orig_wset );
255
+ }
256
+ if (sets -> orig_eset ) {
257
+ rb_fd_term (sets -> orig_eset );
258
+ }
259
+ }
260
+
261
+ static void fd_init_copy (rb_fdset_t * dst , int max , rb_fdset_t * src ) {
262
+ if (src ) {
263
+ rb_fd_resize (max - 1 , src );
264
+ if (dst != src ) {
265
+ rb_fd_init_copy (dst , src );
266
+ }
267
+ }
268
+ }
269
+
140
270
int rb_thread_fd_select (int max , rb_fdset_t * read , rb_fdset_t * write , rb_fdset_t * except , struct timeval * timeout ) {
141
271
// NOTE: MRI has more logic in here
142
272
struct select_set set ;
@@ -145,7 +275,12 @@ int rb_thread_fd_select(int max, rb_fdset_t *read, rb_fdset_t *write, rb_fdset_t
145
275
set .wset = write ;
146
276
set .eset = except ;
147
277
set .timeout = timeout ;
278
+ fd_init_copy (set .orig_rset , set .max , set .rset );
279
+ fd_init_copy (set .orig_wset , set .max , set .wset );
280
+ fd_init_copy (set .orig_eset , set .max , set .eset );
281
+ struct timeval orig_timeval = * timeout ;
282
+ set .orig_timeout = & orig_timeval ;
148
283
149
- void * result = rb_thread_call_without_gvl ( rb_thread_fd_select_blocking , (void * )( & set ), RUBY_UBF_IO , 0 );
284
+ void * result = rb_ensure ( rb_thread_fd_select_internal , (VALUE ) & set , rb_thread_fd_select_set_free , ( VALUE ) & set );
150
285
return (int )(long )result ;
151
286
}
0 commit comments