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;
 }

Reply via email to