Hi Julian and Daniel, Julian Foad writes: > Just a few comments on the implementation from me...
Thanks for the wonderful review! I've also included a few of Daniel's suggestions (on IRC)- I hope I've got it right this time. I'm not sure the volatile qualifier is necessary either- I'm just using it for extra safety, making sure that no compiler optimizations mangle it. [[[ Determine default perms in an elegant thread-safe way, not racily. * subversion/libsvn_subr/io.c (default_perms_baton, perms_init_state): New struct, variable. (get_default_file_perms): Remove all functional code into init_default_file_perms, and use apr_atomic__init_once so that code is only executed once. (init_default_file_perms): New function to determine default file permissions by creating a temporary file and extracting permissions from it. Default permissions are not determined racily anymore, since this function is an apr_atomic__init_once callback. ]]] Index: subversion/libsvn_subr/io.c =================================================================== --- subversion/libsvn_subr/io.c (revision 1057166) +++ subversion/libsvn_subr/io.c (working copy) @@ -1300,6 +1300,39 @@ reown_file(const char *path, return svn_error_return(svn_io_remove_file2(unique_name, FALSE, pool)); } +static volatile svn_atomic_t perms_init_state = 0; +struct default_perms_baton +{ + volatile apr_fileperms_t *default_perms; +}; + + +/* Helper function to discover default file permissions; it does this + by creating a temporary file and extracting the permissions from + it. Passed to svn_atomic__init_once. All temporary allocations are + done in SCRATCH_POOL. */ +static svn_error_t * +init_default_file_perms(void *baton, apr_pool_t *scratch_pool) +{ + volatile apr_fileperms_t *default_perms = + ((struct default_perms_baton *) baton)->default_perms; + apr_finfo_t finfo; + apr_file_t *fd; + + if (*default_perms != 0) + /* Nothing to do */ + return SVN_NO_ERROR; + + SVN_ERR(svn_io_open_uniquely_named(&fd, NULL, NULL, "svn-tempfile", ".tmp", + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); + SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool)); + SVN_ERR(svn_io_file_close(fd, scratch_pool)); + *default_perms = finfo.protection; + + return SVN_NO_ERROR; +} + /* Determine what the PERMS for a new file should be by looking at the permissions of a temporary file that we create. Unfortunately, umask() as defined in POSIX provides no thread-safe way @@ -1310,46 +1343,13 @@ reown_file(const char *path, static svn_error_t * get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool) { - /* the default permissions as read from the temp folder */ - static apr_fileperms_t default_perms = 0; + static volatile apr_fileperms_t default_perms = 0; + struct default_perms_baton baton; - /* Technically, this "racy": Multiple threads may use enter here and - try to figure out the default permisission concurrently. That's fine - since they will end up with the same results. Even more technical, - apr_fileperms_t is an atomic type on 32+ bit machines. - */ - if (default_perms == 0) - { - apr_finfo_t finfo; - apr_file_t *fd; - - /* Get the perms for a newly created file to find out what bits - should be set. - - Normally del_on_close can be problematic because APR might - delete the file if we spawned any child processes. In this - case, the lifetime of this file handle is about 3 lines of - code, so we can safely use del_on_close here. - - Not so fast! If some other thread forks off a child, then the - APR cleanups run, and the file will disappear. So use - del_on_pool_cleanup instead. - - Using svn_io_open_uniquely_named() here because other tempfile - creation functions tweak the permission bits of files they create. - */ - SVN_ERR(svn_io_open_uniquely_named(&fd, NULL, NULL, "svn-tempfile", ".tmp", - svn_io_file_del_on_pool_cleanup, - scratch_pool, scratch_pool)); - SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool)); - SVN_ERR(svn_io_file_close(fd, scratch_pool)); - - *perms = finfo.protection; - default_perms = finfo.protection; - } - else - *perms = default_perms; - + baton.default_perms = &default_perms; + SVN_ERR(svn_atomic__init_once(&perms_init_state, init_default_file_perms, + (void *) &baton, scratch_pool)); + *perms = default_perms; return SVN_NO_ERROR; }