[PATCH 3 of 3] commit: avoid a dirstate race with multiple commits in the same process
Matt Mackall
mpm at selenic.com
Thu Mar 17 10:23:14 CDT 2011
On Thu, 2011-03-17 at 09:10 -0400, Greg Ward wrote:
> On 15 March 2011, Matt Mackall said:
> > So what I don't like here is that some of the magic has leaked out of
> > dirstate and into localrepo. I think we should be able to prevent that.
> >
> > > changes = self.status(match=match, clean=force)
> >
> > For instance, in here.
> >
> > > - for f in changes[0] + changes[1]:
> > > + self._lastcommitfiles = changes[0] + changes[1]
> > > + for f in self._lastcommitfiles:
> > > self.dirstate.normal(f)
> >
> > And here.
> >
> > In other words, we build a _lastnormalfiles in dirstate, and check it
> > internally in status/walk. Then status/walk can always be right,
> > regardless of what code is calling it, without that code having to know
> > about the timing magic.
>
> I tried to implement something like this, and I'm stuck. I started in
> dirstate.write(), since that's one of the few places where we process
> the entire dirstate. The patch to write() looks like this:
>
> """
> @@ -436,16 +438,20 @@
> pack = struct.pack
> write = cs.write
> write("".join(self._pl))
> + normal = set()
> for f in self._map.iterkeys():
> e = self.checkmtime(f, now)
> if f in copymap:
> f = "%s\0%s" % (f, copymap[f])
> + if e[0] == 'n':
> + normal.add(f)
> e = pack(_format, e[0], e[1], e[2], e[3], len(f))
> write(e)
> write(f)
> st.write(cs.getvalue())
> st.rename()
> self._dirty = self._dirtypl = False
> + self._lastnormal = normal
> """
>
> Then I modified status() to pay attention to _lastnormal:
>
> """
> @@ -659,6 +665,9 @@
> radd = removed.append
> dadd = deleted.append
> cadd = clean.append
> + lastnormal = self._lastnormal
> + if lastnormal:
> + now = self.getfstime()
>
> lnkkind = stat.S_IFLNK
>
> @@ -672,7 +681,10 @@
> uadd(fn)
> continue
>
> - state, mode, size, time = dmap[fn]
> + if lastnormal and fn in lastnormal:
> + state, mode, size, time = self.checkmtime(fn, now)
> + else:
> + state, mode, size, time = dmap[fn]
>
> if not st and state in "nma":
> dadd(fn)
> """
>
> (Note: I'm ignoring performance for the moment. Correctness first.)
>
> But this doesn't fix the "commit twice" bug because repo.commit() does
> not call dirstate.write(). And that makes sense; we don't want to
> write the dirstate more than once per process. That should be done
> late in the game.
>
> So then I thought about putting _lastnormal entirely under the control
> of status(). But status() only looks at a subset of the dirstate, so
> it cannot create a definitive _lastnormal list. I'm not sure how much
> this matters.
>
> Maybe I'm barking up the wrong method. Perhaps normal() should be
> responsible for building up the list of "files last asserted to be in
> state normal", which then affects the behaviour of status() on those
> files. Hmmmmm.
You're in danger of becoming an expert. This is more or less what I
expected:
> But I'm a little confused about when dirstate._lastnormalfiles would
> be updated: in dirstate.write()? status()? normal()? I guess
write()
> makes the most sense: is that what you were thinking?
I was thinking there might not be a write?
I think normal() is the right place, which is why I picked _lastnormal.
I should have been more clear, sorry.
> Will try again this evening. Hints welcome in the meantime. ;-)
>
> Greg
--
Mathematics is the supreme nostalgia of our time.
More information about the Mercurial-devel
mailing list