Bug 48557 - file_io/unix/open.c:apr_file_open() should cache O_CLOEXEC support status
Summary: file_io/unix/open.c:apr_file_open() should cache O_CLOEXEC support status
Status: CLOSED FIXED
Alias: None
Product: APR
Classification: Unclassified
Component: APR (show other bugs)
Version: HEAD
Hardware: PC Linux
: P2 enhancement (vote)
Target Milestone: ---
Assignee: Apache Portable Runtime bugs mailinglist
URL:
Keywords: FixedInTrunk
Depends on:
Blocks:
 
Reported: 2010-01-15 14:14 UTC by Mike Frysinger
Modified: 2014-01-21 13:02 UTC (History)
0 users



Attachments
apr-1.3.9-assume-cloexec.patch (992 bytes, patch)
2010-01-15 14:14 UTC, Mike Frysinger
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Mike Frysinger 2010-01-15 14:14:52 UTC
Created attachment 24850 [details]
apr-1.3.9-assume-cloexec.patch

apr_file_open currently uses O_CLOEXEC when opening file's to avoid race conditions with the classic behavior of then using fcntl() to set the FD_CLOEXEC flag.  this is all find and dandy except that on newer operating systems, the two fcntl() calls are still made even if the open() function supports O_CLOEXEC.  when doing something like `svn commit` on a large tree, these translates to a huge number of useless syscalls.

this should be easy to avoid -- if we called open() with O_CLOEXEC, and the fcntl() get step shows that FD_CLOEXEC is already set, we cache the result in a local variable.  then in all future steps, we check the cached value before doing any fcntl() syscalls.

getting back to subversion, before my fix, a simple `svn st` looks something like this:
......
open(".svn/entries", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
read(3, "10\n\ndir\n9382\nsvn://nwd2cvs1.corp"..., 80) = 80
close(3)                                = 0
open(".svn/entries", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
read(3, "10\n\ndir\n9382\nsvn://nwd2cvs1.corp"..., 4096) = 1187
read(3, "", 4096)                       = 0
read(3, "", 4096)                       = 0
close(3)                                = 0
open("vendors/.svn/entries", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
read(3, "10\n\ndir\n9382\nsvn://nwd2cvs1.corp"..., 80) = 80
close(3)                                = 0
......

and after, we see:
......
open(".svn/entries", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
read(3, "10\n\ndir\n9382\nsvn://nwd2cvs1.corp"..., 80) = 80
close(3)                                = 0
open(".svn/entries", O_RDONLY|O_CLOEXEC) = 3
read(3, "10\n\ndir\n9382\nsvn://nwd2cvs1.corp"..., 4096) = 1187
read(3, "", 4096)                       = 0
read(3, "", 4096)                       = 0
close(3)                                = 0
open("vendors/.svn/entries", O_RDONLY|O_CLOEXEC) = 3
read(3, "10\n\ndir\n9382\nsvn://nwd2cvs1.corp"..., 80) = 80
close(3)                                = 0
......

when working on a repo with thousands of dirs/files, you can see how this easily makes a significant difference.
Comment 1 Joe Orton 2010-01-31 05:47:12 UTC
This wouldn't fail safe in the case where O_CLOEXEC was ignored (an earlier kernel).  I've change the code to avoid the second fcntl call in r905040.  Thanks for the report!
Comment 2 Mike Frysinger 2010-02-02 20:37:51 UTC
sorry, i dont understand ... the has_o_cloexec variable is only set to 1 if the fcntl() get operation after the open(O_CLOEXEC) shows that O_CLOEXEC has been applied.  a running program wont be moved between systems on the fly.
Comment 3 Mike Frysinger 2010-10-16 03:46:16 UTC
please review my latest comments.  apr is not as optimal as it could be here.
Comment 4 Stefan Fritsch 2011-10-15 21:35:14 UTC
r1183698, r1183730, r1183732, will be in 1.4.6
Comment 5 Jeff Trawick 2014-01-21 13:02:18 UTC
Fixed in 1.4.6 and later releases