Project

General

Profile

Feature #9330 ยป 0001-socket-avoid-redundant-fcntl-fd-F_GETFD-with-SOCK_CL.patch

normalperson (Eric Wong), 12/31/2013 11:22 AM

View differences:

ext/socket/init.c
249 249
    return rb_assoc_new(str, addr);
250 250
}
251 251

  
252
/* returns true if SOCK_CLOEXEC is supported */
253
int rsock_detect_cloexec(int fd)
254
{
255
#ifdef SOCK_CLOEXEC
256
    int flags = fcntl(fd, F_GETFD);
257

  
258
    if (flags == -1)
259
	rb_bug("rsock_detect_cloexec: fcntl(%d, F_GETFD) failed: %s", fd, strerror(errno));
260

  
261
    if (flags & FD_CLOEXEC)
262
	return 1;
263
#endif
264
    return 0;
265
}
266

  
252 267
static int
253 268
rsock_socket0(int domain, int type, int proto)
254 269
{
255 270
    int ret;
256 271

  
257 272
#ifdef SOCK_CLOEXEC
258
    static int try_sock_cloexec = 1;
259
    if (try_sock_cloexec) {
273
    static int cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */
274

  
275
    if (cloexec_state > 0) { /* common path, if SOCK_CLOEXEC is defined */
276
        ret = socket(domain, type|SOCK_CLOEXEC, proto);
277
        if (ret >= 0 && ret <= 2)
278
            rb_maygvl_fd_fix_cloexec(ret);
279
        return ret;
280
    }
281
    else if (cloexec_state < 0) { /* usually runs once only for detection */
260 282
        ret = socket(domain, type|SOCK_CLOEXEC, proto);
261
        if (ret == -1 && errno == EINVAL) {
283
        if (ret >= 0) {
284
            cloexec_state = rsock_detect_cloexec(ret);
285
            if (cloexec_state == 0 || ret <= 2)
286
                rb_maygvl_fd_fix_cloexec(ret);
287
            return ret;
288
        }
289
        else if (ret == -1 && errno == EINVAL) {
262 290
            /* SOCK_CLOEXEC is available since Linux 2.6.27.  Linux 2.6.18 fails with EINVAL */
263 291
            ret = socket(domain, type, proto);
264 292
            if (ret != -1) {
265
                try_sock_cloexec = 0;
293
                cloexec_state = 0;
266 294
            }
267 295
        }
268 296
    }
269
    else {
297
    else { /* cloexec_state == 0 */
270 298
        ret = socket(domain, type, proto);
271 299
    }
272 300
#else
......
275 303
    if (ret == -1)
276 304
        return -1;
277 305

  
278
    rb_fd_fix_cloexec(ret);
306
    rb_maygvl_fd_fix_cloexec(ret);
279 307

  
280 308
    return ret;
281 309

  
ext/socket/rubysocket.h
304 304
#endif
305 305

  
306 306
int rsock_socket(int domain, int type, int proto);
307
int rsock_detect_cloexec(int fd);
307 308
VALUE rsock_init_sock(VALUE sock, int fd);
308 309
VALUE rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass);
309 310
VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type);
ext/socket/socket.c
152 152
    int ret;
153 153

  
154 154
#ifdef SOCK_CLOEXEC
155
    static int try_sock_cloexec = 1;
156
    if (try_sock_cloexec) {
155
    static int cloexec_state = -1; /* <0: unknown, 0: ignored, >0: working */
156

  
157
    if (cloexec_state > 0) { /* common path, if SOCK_CLOEXEC is defined */
158
        ret = socketpair(domain, type|SOCK_CLOEXEC, protocol, sv);
159
        if (ret == 0 && (sv[0] <= 2 || sv[1] <= 2)) {
160
            goto fix_cloexec; /* highly unlikely */
161
        }
162
        return ret;
163
    }
164
    else if (cloexec_state < 0) { /* usually runs once only for detection */
157 165
        ret = socketpair(domain, type|SOCK_CLOEXEC, protocol, sv);
158
        if (ret == -1 && errno == EINVAL) {
166
        if (ret == 0) {
167
            cloexec_state = rsock_detect_cloexec(sv[0]);
168
            if ((cloexec_state == 0) || (sv[0] <= 2 || sv[1] <= 2))
169
                goto fix_cloexec;
170
            return ret;
171
        }
172
        else if (ret == -1 && errno == EINVAL) {
159 173
            /* SOCK_CLOEXEC is available since Linux 2.6.27.  Linux 2.6.18 fails with EINVAL */
160 174
            ret = socketpair(domain, type, protocol, sv);
161 175
            if (ret != -1) {
......
163 177
                 * So disable SOCK_CLOEXEC only if socketpair() succeeds without SOCK_CLOEXEC.
164 178
                 * Ex. Socket.pair(:UNIX, 0xff) fails with EINVAL.
165 179
                 */
166
                try_sock_cloexec = 0;
180
                cloexec_state = 0;
167 181
            }
168 182
        }
169 183
    }
170
    else {
184
    else { /* cloexec_state == 0 */
171 185
        ret = socketpair(domain, type, protocol, sv);
172 186
    }
173 187
#else
......
178 192
        return -1;
179 193
    }
180 194

  
181
    rb_fd_fix_cloexec(sv[0]);
182
    rb_fd_fix_cloexec(sv[1]);
195
fix_cloexec:
196
    rb_maygvl_fd_fix_cloexec(sv[0]);
197
    rb_maygvl_fd_fix_cloexec(sv[1]);
183 198

  
184 199
    return ret;
185 200
}
......
245 260
    if (ret < 0) {
246 261
	rb_sys_fail("socketpair(2)");
247 262
    }
248
    rb_fd_fix_cloexec(sp[0]);
249
    rb_fd_fix_cloexec(sp[1]);
250 263

  
251 264
    s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
252 265
    s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
253
-