On Thu, 2010-12-02 at 14:56 +0200, Daniel Shahaf wrote: > Julian Foad wrote on Thu, Dec 02, 2010 at 12:15:19 +0000: > > Remove the re-try logic from svn_fs_fs__path_rev_absolute(). Since > > r1040832, all its callers correctly account for the possibility of an > > out-of-date result due to a concurrent packing operation. > > > > The re-try logic was introduced in r875097 and reduced but did not eliminate > > the window of opportunity for the caller to use an out-of-date result. > > > > See the email thread <http://svn.haxx.se/dev/archive-2010-12/0019.shtml>, > > subject "Re: svn commit: r1040832 - Port a fix for a FSFS packing race". > > > > * subversion/libsvn_fs_fs/fs_fs.c > > (svn_fs_fs__path_rev_absolute): Remove the re-try logic. > > > > * subversion/libsvn_fs_fs/fs_fs.h > > (svn_fs_fs__path_rev_absolute): Update the doc string accordingly. > > --This line, and those below, will be ignored-- > > > > Index: subversion/libsvn_fs_fs/fs_fs.c > > =================================================================== > > --- subversion/libsvn_fs_fs/fs_fs.c (revision 1041339) > > +++ subversion/libsvn_fs_fs/fs_fs.c (working copy) > > @@ -246,8 +246,6 @@ path_rev(svn_fs_t *fs, svn_revnum_t rev, > > apr_psprintf(pool, "%ld", rev), NULL); > > } > > > > -/* Returns the path of REV in FS, whether in a pack file or not. > > - Allocate in POOL. */ > > svn_error_t * > > svn_fs_fs__path_rev_absolute(const char **path, > > svn_fs_t *fs, > > @@ -256,45 +254,16 @@ svn_fs_fs__path_rev_absolute(const char > > { > > fs_fs_data_t *ffd = fs->fsap_data; > > > > - if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT) > > + if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT > > + || ! is_packed_rev(fs, rev)) > > { > > *path = path_rev(fs, rev, pool); > > - return SVN_NO_ERROR; > > } > > - > > - if (! is_packed_rev(fs, rev)) > > + else > > { > > - svn_node_kind_t kind; > > - > > - /* Initialize the return variable. */ > > - *path = path_rev(fs, rev, pool); > > - > > - SVN_ERR(svn_io_check_path(*path, &kind, pool)); > > - if (kind == svn_node_file) > > - { > > - /* *path is already set correctly. */ > > - return SVN_NO_ERROR; > > - } > > - else > > - { > > - /* Someone must have run 'svnadmin pack' while this fs object > > - * was open. */ > > - > > - SVN_ERR(update_min_unpacked_rev(fs, pool)); > > - > > - /* The rev really should be present now. */ > > - if (! is_packed_rev(fs, rev)) > > - return svn_error_createf(APR_ENOENT, NULL, > > - _("Revision file '%s' does not exist, > > " > > - "and r%ld is not packed"), > > - svn_dirent_local_style(*path, pool), > > - rev); > > - /* Fall through. */ > > - } > > + *path = path_rev_packed(fs, rev, "pack", pool); > > } > > > > - *path = path_rev_packed(fs, rev, "pack", pool); > > - > > return SVN_NO_ERROR; > > } > > > > This part looks good. I'm more concerned about a bug in the "All > callers use the write lock, ..." analysis than about a bug in the > above hunk.
Right. > In fact, doesn't the correctness of this change depend on the fact that > svn_fs_fs__with_write_lock() calls update_min_unpacked_rev() before > executing the body() callback? Yes, it does. Do you think it shouldn't? > (Otherwise we don't know whether there > is a "concurrent" packer, or just ffd->min_unpacked_rev is stale.) You know, I'm not too clear on the design intention here. I would have assumed it would be the job of all the fs_fs.c code to ensure that ffd->min_unpacked_rev is never stale (due to its own actions) at any point where it matters. But the way it does that seems to be to re-read the file in several places, instead of simply updating ffd->min_unpacked_rev when it writes the file. Maybe the idea was that that method would pick up both internal and concurrent (external) changes in the same way - I don't know. Seems odd. Do you have an idea whether something should be done differently? I don't, not without studying it further. I note that the following comment in pack_shard() is not quite right: /* Update the min-unpacked-rev file to reflect our newly packed shard. * (ffd->min_unpacked_rev will be updated by open_pack_or_rev_file().) That may or may not be true, but it doesn't seem like this function has any right to make that assertion. And in pack_revprop_shard(), it's the same, verbatim: /* Update the min-unpacked-rev file to reflect our newly packed shard. should say 'the min-unpacked-revprop file'; * (ffd->min_unpacked_rev will be updated by open_pack_or_rev_file().) and that second line looks completely bogus in this context. > > Index: subversion/libsvn_fs_fs/fs_fs.h > > =================================================================== > > --- subversion/libsvn_fs_fs/fs_fs.h (revision 1041339) > > +++ subversion/libsvn_fs_fs/fs_fs.h (working copy) > > @@ -411,8 +411,10 @@ svn_error_t *svn_fs_fs__move_into_place( > > Allocate *PATH in POOL. > > > > Note: If the caller does not have the write lock on FS, then the path is > > - not guaranteed to remain correct after the function returns, because the > > - revision might become packed just after this call. */ > > + not guaranteed to be correct or to remain correct after the function > > + returns, because the revision might become packed before or after this > > + call. If a file exists at that path, then it is correct; if not, then > > + the caller should call update_min_unpacked_rev() and re-try once. */ > > +1 semantically. However, update_min_unpacked_rev() is private to > fs_fs.c, so technically you aren't supposed to mention it in this > context. Good point. Really that means any external caller of this API has to hold the write lock. Calling update_min_unpacked_rev() and re-trying is only an option for internal callers (within fs_fs.c). I wonder if this should give us a clue about the questions above. - Julian > > svn_error_t * > > svn_fs_fs__path_rev_absolute(const char **path, > > svn_fs_t *fs, >