[cpp-threads] Prism 0.9.1, and further on the effects of races

Herb Sutter hsutter at microsoft.com
Fri Sep 22 02:35:37 BST 2006


Hans wrote:
> I'm actually not sure I understand the objection to fully undefined
> behavior.  If I read an element past the end of an array, I believe I
> get fully undefined behavior right?  It's not just the returned value
> that's unspecified.

I admit I'm not a fan of UD. :-) But it makes sense to me that if I have a race on a single variable x, and on nothing else in the program, then I ought to be able to reason about the program in terms of unexpected (and possibly SC-impossible) values of x alone. Of course if x is a function pointer I can expect a wild branch. But, conceptually, why should that be possible otherwise, especially when the wild branch happens because of a function pointer that isn't even visible in the source code but is part of compiler-generated machinery (which presumably is generated "correctly", and here I realize one issue in question is what's a reasonable definition of "correctly" in balancing implementation realities with behavior that programmers accept as sane).


> I think the situation here is basically the same.  A data race on an
> ordinary variable in C++ is a program error, just like an out-of-bounds
> array reference.  We're proposing to provide sufficient "atomic"
> operations to ensure that a data race on an ordinary variable is never
> necessary.  So I think there isn't really a reason to support this to
> any extent whatsoever.

OK, see the parallel email I just sent responding to Peter and asking about the subcase where the object pointer is of atomic type (atomic only, not with acq/rel semantics).


> Assuming I'm correct, we'd have to allow for the case that foo *p =
> global_ptr yields a value for p that was never assigned to global_ptr
> and in fact doesn't point to foo.

I wonder (in the other email) if there's a problem even without that, given reads/writes of p are atomic (but not ordered). You can still get wild branches for objects with vtables, can't you? Isn't that a problem?

> If we do try to partially define races, I think other optimizations are
> also affected.  We would have to disallow reloading of a spilled
> register from a global, since the impact of that is very hard to
> describe in the presence of a race.

That a separate example, though a good one. That's permitted in a race (in the current Prism spec, and also in the draft ISO C++ proposal I thought?).

> As far as constructors are concerned, I think I'm most worried about a
> different class of example.  Consider, at function scope:
>
> {
>         foo x();

This is intended to be an object x of type foo, right?

>         x.bar();
>         f();
> }
>
> Assume a foo has virtual functions and hence needs a vtable, and the
> definitions of bar() and f() are in separate compilation units.  I don't
> think there's anything to stop bar from publishing "this" to another
> thread.  Clearly I would need a release operation or a fence somewhere
> to make the vtable pointer store visible before such a publication
> happens.  But putting it in any of the obvious places, e.g. at the end
> of the foo() constructor, seems to impact sequential code.  And if bar()
> and f() are sufficiently fast, this code could slow down by large
> factors as a result, at least if I only have 100 cycle fence
> instructions.

This is an interesting example!

Herb




More information about the cpp-threads mailing list