@@ -371,6 +371,7 @@ static void invalid_parameter(const wchar_t *expr, const wchar_t *func, const wc
371
371
}
372
372
#endif
373
373
374
+ static CRITICAL_SECTION select_mutex ;
374
375
static BOOL fWinsock ;
375
376
static char * envarea ;
376
377
static void
@@ -384,6 +385,7 @@ exit_handler(void)
384
385
FreeEnvironmentStrings (envarea );
385
386
envarea = NULL ;
386
387
}
388
+ DeleteCriticalSection (& select_mutex );
387
389
}
388
390
389
391
static void
@@ -472,6 +474,8 @@ NtInitialize(int *argc, char ***argv)
472
474
473
475
init_stdhandle ();
474
476
477
+ InitializeCriticalSection (& select_mutex );
478
+
475
479
atexit (exit_handler );
476
480
477
481
// Initialize Winsock
@@ -2043,87 +2047,250 @@ rb_w32_fdisset(int fd, fd_set *set)
2043
2047
static int NtSocketsInitialized = 0 ;
2044
2048
2045
2049
static int
2046
- extract_file_fd (fd_set * set , fd_set * fileset )
2050
+ extract_fd (fd_set * dst , fd_set * src , int ( * func )( SOCKET ) )
2047
2051
{
2048
- int idx ;
2052
+ int s = 0 ;
2053
+ if (!src || !dst ) return 0 ;
2049
2054
2050
- fileset -> fd_count = 0 ;
2051
- if (!set )
2052
- return 0 ;
2053
- for (idx = 0 ; idx < set -> fd_count ; idx ++ ) {
2054
- SOCKET fd = set -> fd_array [idx ];
2055
+ while (s < src -> fd_count ) {
2056
+ SOCKET fd = src -> fd_array [s ];
2055
2057
2056
- if (!is_socket ( fd )) {
2057
- int i ;
2058
+ if (!func || ( * func )( fd )) { /* move it to dst */
2059
+ int d ;
2058
2060
2059
- for (i = 0 ; i < fileset -> fd_count ; i ++ ) {
2060
- if (fileset -> fd_array [i ] == fd ) {
2061
- break ;
2062
- }
2061
+ for (d = 0 ; d < dst -> fd_count ; d ++ ) {
2062
+ if (dst -> fd_array [d ] == fd ) break ;
2063
2063
}
2064
- if (i == fileset -> fd_count ) {
2065
- if (fileset -> fd_count < FD_SETSIZE ) {
2066
- fileset -> fd_array [i ] = fd ;
2067
- fileset -> fd_count ++ ;
2068
- }
2064
+ if (d == dst -> fd_count && dst -> fd_count < FD_SETSIZE ) {
2065
+ dst -> fd_array [dst -> fd_count ++ ] = fd ;
2066
+ }
2067
+ memmove (
2068
+ & src -> fd_array [s ],
2069
+ & src -> fd_array [s + 1 ],
2070
+ sizeof (src -> fd_array [0 ]) * (-- src -> fd_count - s ));
2071
+ }
2072
+ else s ++ ;
2073
+ }
2074
+
2075
+ return dst -> fd_count ;
2076
+ }
2077
+
2078
+ static int
2079
+ is_not_socket (SOCKET sock )
2080
+ {
2081
+ return !is_socket (sock );
2082
+ }
2083
+
2084
+ static int
2085
+ is_pipe (SOCKET sock ) /* DONT call this for SOCKET! it clains it is PIPE. */
2086
+ {
2087
+ int ret ;
2088
+
2089
+ RUBY_CRITICAL (
2090
+ ret = (GetFileType ((HANDLE )sock ) == FILE_TYPE_PIPE )
2091
+ );
2092
+
2093
+ return ret ;
2094
+ }
2095
+
2096
+ static int
2097
+ is_readable_pipe (SOCKET sock ) /* call this for pipe only */
2098
+ {
2099
+ int ret ;
2100
+ DWORD n = 0 ;
2101
+
2102
+ RUBY_CRITICAL (
2103
+ if (PeekNamedPipe ((HANDLE )sock , NULL , 0 , NULL , & n , NULL )) {
2104
+ ret = (n > 0 );
2105
+ }
2106
+ else {
2107
+ ret = (GetLastError () == ERROR_BROKEN_PIPE ); /* pipe was closed */
2108
+ }
2109
+ );
2110
+
2111
+ return ret ;
2112
+ }
2113
+
2114
+ static int
2115
+ is_console (SOCKET sock ) /* DONT call this for SOCKET! */
2116
+ {
2117
+ int ret ;
2118
+ DWORD n = 0 ;
2119
+ INPUT_RECORD ir ;
2120
+
2121
+ RUBY_CRITICAL (
2122
+ ret = (PeekConsoleInput ((HANDLE )sock , & ir , 1 , & n ))
2123
+ );
2124
+
2125
+ return ret ;
2126
+ }
2127
+
2128
+ static int
2129
+ is_readable_console (SOCKET sock ) /* call this for console only */
2130
+ {
2131
+ int ret = 0 ;
2132
+ DWORD n = 0 ;
2133
+ INPUT_RECORD ir ;
2134
+
2135
+ RUBY_CRITICAL (
2136
+ if (PeekConsoleInput ((HANDLE )sock , & ir , 1 , & n ) && n > 0 ) {
2137
+ if (ir .EventType == KEY_EVENT && ir .Event .KeyEvent .bKeyDown &&
2138
+ ir .Event .KeyEvent .uChar .AsciiChar ) {
2139
+ ret = 1 ;
2140
+ }
2141
+ else {
2142
+ ReadConsoleInput ((HANDLE )sock , & ir , 1 , & n );
2143
+ }
2144
+ }
2145
+ );
2146
+
2147
+ return ret ;
2148
+ }
2149
+
2150
+ static int
2151
+ do_select (int nfds , fd_set * rd , fd_set * wr , fd_set * ex ,
2152
+ struct timeval * timeout )
2153
+ {
2154
+ int r = 0 ;
2155
+
2156
+ if (nfds == 0 ) {
2157
+ if (timeout )
2158
+ rb_w32_sleep (timeout -> tv_sec * 1000 + timeout -> tv_usec / 1000 );
2159
+ else
2160
+ rb_w32_sleep (INFINITE );
2161
+ }
2162
+ else {
2163
+ RUBY_CRITICAL (
2164
+ EnterCriticalSection (& select_mutex );
2165
+ r = select (nfds , rd , wr , ex , timeout );
2166
+ LeaveCriticalSection (& select_mutex );
2167
+ if (r == SOCKET_ERROR ) {
2168
+ errno = map_errno (WSAGetLastError ());
2169
+ r = -1 ;
2069
2170
}
2171
+ );
2172
+ }
2173
+
2174
+ return r ;
2175
+ }
2176
+
2177
+ static inline int
2178
+ subst (struct timeval * rest , const struct timeval * wait )
2179
+ {
2180
+ while (rest -> tv_usec < wait -> tv_usec ) {
2181
+ if (rest -> tv_sec <= wait -> tv_sec ) {
2182
+ return 0 ;
2070
2183
}
2184
+ rest -> tv_sec -= 1 ;
2185
+ rest -> tv_usec += 1000 * 1000 ;
2071
2186
}
2072
- return fileset -> fd_count ;
2187
+ rest -> tv_sec -= wait -> tv_sec ;
2188
+ rest -> tv_usec -= wait -> tv_usec ;
2189
+ return 1 ;
2073
2190
}
2074
2191
2192
+ static inline int
2193
+ compare (const struct timeval * t1 , const struct timeval * t2 )
2194
+ {
2195
+ if (t1 -> tv_sec < t2 -> tv_sec )
2196
+ return -1 ;
2197
+ if (t1 -> tv_sec > t2 -> tv_sec )
2198
+ return 1 ;
2199
+ if (t1 -> tv_usec < t2 -> tv_usec )
2200
+ return -1 ;
2201
+ if (t1 -> tv_usec > t2 -> tv_usec )
2202
+ return 1 ;
2203
+ return 0 ;
2204
+ }
2205
+
2206
+ #undef Sleep
2075
2207
long
2076
2208
rb_w32_select (int nfds , fd_set * rd , fd_set * wr , fd_set * ex ,
2077
2209
struct timeval * timeout )
2078
2210
{
2079
2211
long r ;
2080
- fd_set file_rd ;
2081
- fd_set file_wr ;
2082
- #ifdef USE_INTERRUPT_WINSOCK
2083
- fd_set trap ;
2084
- #endif /* USE_INTERRUPT_WINSOCK */
2085
- int file_nfds ;
2212
+ fd_set pipe_rd ;
2213
+ fd_set cons_rd ;
2214
+ fd_set else_rd ;
2215
+ fd_set else_wr ;
2216
+ int nonsock = 0 ;
2086
2217
2218
+ if (nfds < 0 || (timeout && (timeout -> tv_sec < 0 || timeout -> tv_usec < 0 ))) {
2219
+ errno = EINVAL ;
2220
+ return -1 ;
2221
+ }
2087
2222
if (!NtSocketsInitialized ) {
2088
2223
StartSockets ();
2089
2224
}
2225
+
2226
+ // assume else_{rd,wr} (other than socket, pipe reader, console reader)
2227
+ // are always readable/writable. but this implementation still has
2228
+ // problem. if pipe's buffer is full, writing to pipe will block
2229
+ // until some data is read from pipe. but ruby is single threaded system,
2230
+ // so whole system will be blocked forever.
2231
+
2232
+ else_rd .fd_count = 0 ;
2233
+ nonsock += extract_fd (& else_rd , rd , is_not_socket );
2234
+
2235
+ pipe_rd .fd_count = 0 ;
2236
+ extract_fd (& pipe_rd , & else_rd , is_pipe ); // should not call is_pipe for socket
2237
+
2238
+ cons_rd .fd_count = 0 ;
2239
+ extract_fd (& cons_rd , & else_rd , is_console ); // ditto
2240
+
2241
+ else_wr .fd_count = 0 ;
2242
+ nonsock += extract_fd (& else_wr , wr , is_not_socket );
2243
+
2090
2244
r = 0 ;
2091
2245
if (rd && rd -> fd_count > r ) r = rd -> fd_count ;
2092
2246
if (wr && wr -> fd_count > r ) r = wr -> fd_count ;
2093
2247
if (ex && ex -> fd_count > r ) r = ex -> fd_count ;
2094
2248
if (nfds > r ) nfds = r ;
2095
- if (nfds == 0 && timeout ) {
2096
- Sleep (timeout -> tv_sec * 1000 + timeout -> tv_usec / 1000 );
2097
- return 0 ;
2098
- }
2099
- file_nfds = extract_file_fd (rd , & file_rd );
2100
- file_nfds += extract_file_fd (wr , & file_wr );
2101
- if (file_nfds )
2249
+
2102
2250
{
2103
- // assume normal files are always readable/writable
2104
- // fake read/write fd_set and return value
2105
- if (rd ) * rd = file_rd ;
2106
- if (wr ) * wr = file_wr ;
2107
- return file_nfds ;
2108
- }
2251
+ struct timeval rest ;
2252
+ struct timeval wait ;
2253
+ struct timeval zero ;
2254
+ if (timeout ) rest = * timeout ;
2255
+ wait .tv_sec = 0 ; wait .tv_usec = 10 * 1000 ; // 10ms
2256
+ zero .tv_sec = 0 ; zero .tv_usec = 0 ; // 0ms
2257
+ do {
2258
+ if (nonsock ) {
2259
+ // modifying {else,pipe,cons}_rd is safe because
2260
+ // if they are modified, function returns immediately.
2261
+ extract_fd (& else_rd , & pipe_rd , is_readable_pipe );
2262
+ extract_fd (& else_rd , & cons_rd , is_readable_console );
2263
+ }
2109
2264
2110
- #if USE_INTERRUPT_WINSOCK
2111
- if (ex )
2112
- trap = * ex ;
2113
- else
2114
- trap .fd_count = 0 ;
2115
- if (trap .fd_count < FD_SETSIZE )
2116
- trap .fd_array [trap .fd_count ++ ] = (SOCKET )interrupted_event ;
2117
- // else unable to catch interrupt.
2118
- ex = & trap ;
2119
- #endif /* USE_INTERRUPT_WINSOCK */
2265
+ if (else_rd .fd_count || else_wr .fd_count ) {
2266
+ r = do_select (nfds , rd , wr , ex , & zero ); // polling
2267
+ if (r < 0 ) break ; // XXX: should I ignore error and return signaled handles?
2268
+ r += extract_fd (rd , & else_rd , NULL ); // move all
2269
+ r += extract_fd (wr , & else_wr , NULL ); // move all
2270
+ break ;
2271
+ }
2272
+ else {
2273
+ struct timeval * dowait =
2274
+ compare (& rest , & wait ) < 0 ? & rest : & wait ;
2275
+
2276
+ fd_set orig_rd ;
2277
+ fd_set orig_wr ;
2278
+ fd_set orig_ex ;
2279
+ if (rd ) orig_rd = * rd ;
2280
+ if (wr ) orig_wr = * wr ;
2281
+ if (ex ) orig_ex = * ex ;
2282
+ r = do_select (nfds , rd , wr , ex , & zero ); // polling
2283
+ if (r != 0 ) break ; // signaled or error
2284
+ if (rd ) * rd = orig_rd ;
2285
+ if (wr ) * wr = orig_wr ;
2286
+ if (ex ) * ex = orig_ex ;
2287
+
2288
+ // XXX: should check the time select spent
2289
+ Sleep (dowait -> tv_sec * 1000 + dowait -> tv_usec / 1000 );
2290
+ }
2291
+ } while (!timeout || subst (& rest , & wait ));
2292
+ }
2120
2293
2121
- RUBY_CRITICAL ({
2122
- r = select (nfds , rd , wr , ex , timeout );
2123
- if (r == SOCKET_ERROR ) {
2124
- errno = map_errno (WSAGetLastError ());
2125
- }
2126
- });
2127
2294
return r ;
2128
2295
}
2129
2296
@@ -3258,7 +3425,6 @@ rb_w32_times(struct tms *tmbuf)
3258
3425
return 0 ;
3259
3426
}
3260
3427
3261
- #undef Sleep
3262
3428
#define yield_once () Sleep(0)
3263
3429
#define yield_until (condition ) do yield_once(); while (!(condition))
3264
3430
0 commit comments