Reader Q&A: Acquire/release and sequential consistency

Reader Ernie Cohen emailed me this morning to ask a question about one slide in my atomic<> Weapons talk from last year’s C++ and Beyond:

In your atomic weapons talk (part 1) (updated 2/15/2013) ,page 18, titled “Sc > Acq/Rel Alone: Some examples”, the first example listed “transitivity/causality”:

T0: g = 1; x = 1;

T1: if (x == 1) y = 1;

T2: if (y == 1) assert(g == 1);

I understood you to mean that the assertion might fail if the loads were simple C++11 acquires and the stores were simple C++ releases. But this works just fine with the weaker memory order; the operations in each thread are related by sequenced-before, the communications between the threads create happens-before, and without consumes happens-before is transitive, so there is a happens-before edge from g = 1 to the assertion. Am I missing something?

[Note: g is an ordinary variable, x and y are std::atomic, and all initially zero as usual.] The motivation behind this example, and the other example on the same slide, was to show that when we specified the C++ memory model and atomics, we had to consider more than individual acquire-release pairs in isolation, but also provide additional guarantees to ensure that the whole program was sequentially consistent (SC).

In the above example, yes, we guarantee that the assertion cannot fail with C++ acquire and release semantics, and making sure the memory model required this transitivity is exactly one of the two key points of this example. As you point out, it requires getting the “right” answer when combining sequenced-before and happens-before.

The second point illustrated here is that it was essential to support cases where the programmer could depend on reasoning based on tests of whether a particular write was read and then making SC assumptions based on the outcome of the test, otherwise the whole program wouldn’t be SC.

For completeness, the other example on the slide showed an additional case where individual pairwise acquire/release alone was insufficient to guarantee SC outcomes unless we added requirements. Here is the example, with x and y std::atomic and initially zero:

T1: x = 1;

T2: y = 1;

T3: if( x == 1 && y == 0 ) print( “x first” );

T4: if( y == 1 && x == 0 ) print( “y first” );

This illustrates the total store order requirement: It must be impossible to print both messages, else the result wouldn’t be SC.

Note that in most cases using (non-SC) memory_order_acquire and memory_order_release explicitly happens to give you SC results, except when they don’t (e.g., Dekker’s fails, and I think the second example above fails as well). And of course other relaxed atomics can allow non-SC results at the drop of a hat.

Bjarne and I are speaking in Chicago on Tuesday night

Bjarne Stroustrup and I are giving back-to-back talks on Tuesday night in Chicago, while we’re both in town for the standards meeting next week. Admission is free. Register by email here (and ignore the “it’s full / sold out” note on the page — see below.)

Note that my talk will be 80% new material followed by the last 5-10 min of my GoingNative 2013 keynote, so even if you’ve seen that talk most of the material will be new. Here are the coordinates:

A joint Chicago Chapter ACM and Chicago C/C++ Users Group meeting

Broad-spectrum C++/C++14 (Bjarne Stroustrup)

One C++ (Herb Sutter)

Tuesday, September 24, 2013
Doors open approximately 6:30 pm
Presentations run 7:00 pm – 9:30 pm

Renaissance Chicago Downtown Hotel
1 West Wacker Drive, Chicago, IL 60601
Room A/B [or a nearby larger room, just follow the crowd when you arrive]

Note: The page says the event is at capacity and the primary registration link balks, but there is space because they keep expanding the room — it has already expanded from the original 100 seats to 200 and currently 300 seats, and they’re looking into moving to a still larger room as we speak. So do add yourself to the waiting list via this email link as it looks likely you will still be able to get in.

Reader Q&A: “Will C++ remain indispensable…?”

A reader wrote me today to ask the following. Since this is a FAQ, I thought I’d post the answer here.

With the advent of C++11 and upcoming C++14 and C++1y, the language has strapped much of the digital electronics industry under its belt. High performance software, Libraries, Embedded, Research, Web backends, our everyday software, and trivial systems; Everything directly or indirectly depends on C/C++.

My question is -: Will C++ remain indispensable for creating above mentioned class of production software for very long foreseeable future ?

Short answer: Yes, but don’t stop there because that’s not all of what C++ is about!

Expanding on “Yes”: Yes, I think so. The main reason is that several long-term industry trends now in progress favor C++’s core strength of “efficiency per Watt/cycle/transistor + full flexibility and control” in a portable language. For example, for mobile (notebook, tablet, phone) and cloud datacenter (Facebook, Google, etc.) app environments, both of which are clearly large and will continue to experience long-term growth, the critical measure has already switched to “performance per Watt”… if that’s your primary constraint and cost, you’ll usually end up at either C or C++, and the latter gives you stronger abstraction. And performance per Watt is just one example; besides that one, as other trends like the power wall and the eventual end of Moore’s Law continue to grind out, the value proposition of “performance per Watt/cycle/transistor + full flexibility (to express what you need) and control (over hardware and things like memory layout)” will remain valuable in the long term.

Expanding on “but don’t stop there because that’s not all of what C++ is about!”: Old C++ used to be harder, but today modern C++ is far from limited to those categories. As I pointed out my “One C++” talk last week (start around the 42:00 mark), C++ is already a de facto standard language to teach artists (not professional programmers) in the world’s top design schools such as Parson’s, notably using C++ plus the openFrameworks library, and C++ is also a widely used library outside schools for creative interactive applications (again often by people who are not programmers by training), notably using C++ plus the Cinder library.

Other languages are important and useful too, and often are built on top of C++, and that’s great – there is no such thing as “one language fits all,” and everyone realizes that like any language C++ is not for everyone or every use. Nevertheless, C++ is very horizontal, meaning usable in many domains, and the cases where C++ is applicable and actively used is growing, not shrinking; one effect is that we have to relearn what a “C++ programmer” is because that group is getting increasingly diverse. As we continue to make C++ more accessible, I expect we will continue to hear about its being used more and more even by people we didn’t expect, like artists and financial gurus who are not programmers by training and whom we never used to think of as C++ programmers… it’s happening already.

My “One C++” talk from GoingNative is now posted

I see the recording went live this morning. Thanks again to all the speakers and in-room and worldwide attendees for coming and watching!

Day 2 Keynote: One C++

Herb Sutter

My favorite part was seeing the response to the challenge to write a cool graphical interactive C++ program from scratch in 24 hours using a library most had never seen before – Cinder or openFrameworks. In two other GN segments we showed the results. Here’s they are:

Herb’s UI Challenge Results

My Favorite C++ 10-Liner, plus UI Challenge Results Part 2 / Closing comments

Thank you all for coming! It was a blast.

Visual Studio 2013 RC is now available

At Build in June, we announced that VC++ 2013 RTM “later this year” would include the ISO conformance features in the June preview (explicit conversion operators, raw string literals, function template default arguments, delegating constructors, uniform initialization and initializer_lists, and variadic templates) plus also several more to be added between the Preview and the RTM: non-static data member initializers, =default, =delete, “using” aliases, and library support for same plus four C99 features.

Four days ago in my talk at GoingNative (recording was just posted today!), I updated that by saying we would ship an update to VC++ 2013 Preview with all of those features, and again a go-live license, “in September.”

Well, today is “in September”: Today we shipped Visual Studio 2013 RC, which includes all of those features in a production-supported go-live Visual C++ product. Thank you very much to everyone on the front-end, back-end, and other VC++ teams for making this possible! See that post for additional features – there’s more there than just conformance work, from new auto-vectorizer optimizations to graphics and native/Javascript debugging to quite a bit more.

What’s next? As announced last week we’re going to be shipping another CTP in the fourth quarter (compiler tech preview, think of it as an “alpha”) containing another bunch of conformance features. It’s designed to give you early access to a raft of new conformance features in Visual C++, including the following (the first couple are small but I’m breaking them out because the usual C++11/14 conformance lists do it that way):

  • __func__
  • Extended sizeof
  • Implicit move generation
  • Ref-qualifiers: & and && for *this
  • Thread-safe function local static initialization (aka “magic statics”)
  • noexcept (note: this is unconditional noexcept; the CTP likely won’t have the noexcept(expr) style yet)
  • constexpr (note: except for constructors, so the CTP won’t support literal types yet)
  • C++14: generalized lambda capture
  • C++14: auto function return type deduction
  • C++14: decltype(auto)
  • Bonus: “await” as currently proposed for the ISO Concurrency TS

Those are the “high probability” features to make it into the CTP. There’s a chance we might also get a couple more “medium probability” features into the Q4 CTP, such as C++14 generic lambdas, but never fear, we’re implementing them all – whatever features don’t get into this CTP will be at the front of the next batch as we push toward full C++11/14 conformance including the C++14-accompanying specifications for the file system library, basic networking library, and especially Concepts Lite. We’ll keep you posted as we know more.

Livestreamed talk at GoingNative this week: One C++

Don’t forget that the year’s great C++-fest GoingNative 2013 starts tomorrow morning and will be livestreamed on the Channel 9 home page.

Don’t miss the opening keynote by Bjarne Stroustrup at 9:00am Seattle time on Wednesday (other time zones). It will be followed by many other insightful and enlightening talks, from many of the gurus of C++. The first two days are about Standard C++ on all platforms; the third day contains a mix of portable platform topics and Microsoft-specific content that I think will be of interest to people working on other platforms too.

My talk is first thing Thursday, 9:00am Seattle time:

Day 2 Keynote: One C++

After a few minutes giving a promised update on VC++ conformance progress, I’ll switch to pure Standard C++ for the rest of the talk and focus on three topics:

  • ISO C++ update: The latest news from C++14 standardization now that the CD comment ballot recently concluded.
  • Portable C++ libraries update: I mentioned this at the last GoingNative, and here’s the status update people have been asking me for. This topic will get extra time for emphasis and news.
  • One C++: You’ll see what I mean…

I hope that you’ll find it to be informative, and leave with at least one or two thoughts you weren’t expecting to hear about and maybe with a different view of C++ than you had before.

In addition to the livestream, the entire conference will also be available on demand. Note that sessions typically take a few days to post after they occur, so if you miss a talk on the live feed, just be patient and it will appear.

Pre-emptive FAQ: Every time we do this, someone is disappointed that the livestream doesn’t work on their iPads or some other popular hardware. But it actually does work – just look for the alternative-format link if it doesn’t autodetect. You should be able to view it on any major platform.

Enjoy!

GotW #7b: Minimizing Compile-Time Dependencies, Part 2

Now that the unnecessary headers have been removed, it’s time for Phase 2: How can you limit dependencies on the internals of a class?

Problem

JG Questions

1. What does private mean for a class member in C++?

2. Why does changing the private members of a type cause a recompilation?

Guru Question

3. Below is how the header from the previous Item looks after the initial cleanup pass. What further #includes could be removed if we made some suitable changes, and how?

This time, you may make changes to X as long as X‘s base classes and its public interface remain unchanged; any current code that already uses X should not be affected beyond requiring a simple recompilation.

//  x.h: sans gratuitous headers
//
#include <iosfwd>
#include <list>

// None of A, B, C, or D are templates.
// Only A and C have virtual functions.
#include "a.h"  // class A
#include "b.h"  // class B
#include "c.h"  // class C
#include "d.h"  // class D
class E;

class X : public A, private B {
public:
       X( const C& );
    B  f( int, char* );
    C  f( int, C );
    C& g( B );
    E  h( E );
    virtual std::ostream& print( std::ostream& ) const;

  private:
    std::list<C> clist;
    D            d_;
};

std::ostream& operator<<( std::ostream& os, const X& x ) {
    return x.print(os);
}

GotW #7a Solution: Minimizing Compile-Time Dependencies, Part 1

Managing dependencies well is an essential part of writing solid code. C++ supports two powerful methods of abstraction: object-oriented programming and generic programming. Both of these are fundamentally tools to help manage dependencies, and therefore manage complexity. It’s telling that all of the common OO/generic buzzwords—including encapsulation, polymorphism, and type independence—along with most design patterns, are really about describing ways to manage complexity within a software system by managing the code’s interdependencies.

When we talk about dependencies, we usually think of run-time dependencies like class interactions. In this Item, we will focus instead on how to analyze and manage compile-time dependencies. As a first step, try to identify (and root out) unnecessary headers.

Problem

JG Question

1. For a function or a class, what is the difference between a forward declaration and a definition?

Guru Question

2. Many programmers habitually #include many more headers than necessary. Unfortunately, doing so can seriously degrade build times, especially when a popular header file includes too many other headers.

In the following header file, what #include directives could be immediately removed without ill effect? You may not make any changes other than removing or rewriting (including replacing) #include directives. Note that the comments are important.

//  x.h: original header
//
#include <iostream>
#include <ostream>
#include <list>

// None of A, B, C, D or E are templates.
// Only A and C have virtual functions.
#include "a.h"  // class A
#include "b.h"  // class B
#include "c.h"  // class C
#include "d.h"  // class D
#include "e.h"  // class E

class X : public A, private B {
public:
       X( const C& );
    B  f( int, char* );
    C  f( int, C );
    C& g( B );
    E  h( E );
    virtual std::ostream& print( std::ostream& ) const;

  private:
    std::list<C> clist;
    D            d_;
  };

std::ostream& operator<<( std::ostream& os, const X& x ) {
    return x.print(os);
}

Solution

1. For a function or class, what is the difference between a forward declaration and a definition?

A forward declaration of a (possibly templated) function or class simply introduces a name. For example:

class widget;  // "widget" names a class 

widget* p;     // ok: allocates sizeof(*) space typed as widget*

widget  w;     // error: wait, what? how big is that? does it have a
               //        default constructor?

Again, a forward declaration only introduces a name. It lets you do things that require only the name, such as declaring a pointer to it—all pointers to objects are the same size and have the same set of operations you can perform on them, and ditto for pointers to nonmember functions, so the name is all you need to make a strongly-typed and fully-usable variable that’s a pointer to class or pointer to function.

What a class forward declaration does not do is tell you anything about what you can do with the type itself, such as what constructors or member functions it has or how big it is if you want to allocate space for one. If you try to create a widget w; with only the above code, you’ll get a compile-time error because widget has no definition yet and so the compiler can’t know how much space to allocate or what functions the type has (including whether it has a default constructor).

A class definition has a body and lets you know the class’s size and know the names and types of its members:

class widget { // "{" means definition
    widget();
    // ...
};

widget* p;     // ok: allocs sizeof(ptr) space typed as widget*

widget  w;     // ok: allocs sizeof(widget) space typed as widget
               //     and calls default constructor

2. In the following header file, what #include directives could be immediately removed without ill effect?

Of the first two standard headers mentioned in x.h, one can be immediately removed because it’s not needed at all, and the second can be replaced with a smaller header:

1. Remove iostream.

#include <iostream>

Many programmers #include <iostream> purely out of habit as soon as they see anything resembling a stream nearby. Class X does make use of streams, that’s true; but it doesn’t mention anything specifically from iostream, which mainly declares the standard stream objects like cout. At the most, X needs ostream alone for its basic_ostream type, and even that can be whittled down as we will see.

Guideline: Never #include unnecessary header files.

2. Replace ostream with iosfwd.

#include <ostream>

Parameter and return types only need to be forward-declared, so instead of the full definition of ostream we really only need its forward declaration.

However, you can’t write the forward declaration yourself using something like class ostream;. First, ostream lives in namespace std in which you can’t redeclare existing standard types and objects. Second, ostream is an alias for basic_ostream<char> which you couldn’t reliably forward-declare even if you were allowed to because library implementations are allowed to do things like add their own extra template parameters beyond those required by the standard that of course your code wouldn’t know about—which is one of the primary reasons for the rule that programmers aren’t allowed to write their own declarations for things in namespace std.

All is not lost, though: The standard library helpfully provides the header iosfwd, which contains forward declarations for all of the stream templates and their standard aliases, including basic_ostream and ostream. So all we need to do is replace #include <ostream> with #include <iosfwd>.

Guideline: Prefer to #include <iosfwd> when a forward declaration of a stream will suffice.

Incidentally, once you see iosfwd, one might think that the same trick would work for other standard library templates like string and list. There are, however, no comparable “stringfwd” or “listfwd” standard headers. The iosfwd header was created to give streams special treatment for backwards compatibility, to avoid breaking code written in years past for the “old” non-templated version of the iostreams subsystem. It is hoped that a real solution will come in a future version of C++ that supports modules, but that’s a topic for a later time.

There, that was easy. We can now move on to…

… what? “Not so fast!” I hear some of you say. “This header does a lot more with ostream than just mention it as a parameter or return type. The inlined operator<< actually uses an ostream object! So it must need ostream‘s definition, right?”

That’s a reasonable question. Happily, the answer is: No, it doesn’t. Consider again the function in question:

std::ostream& operator<<( std::ostream& os, const X& x ) {
    return x.print(os);
}

This function mentions an ostream& as both a parameter and a return type, which most people know doesn’t require a definition. And it passes its ostream& parameter in turn as a parameter to another function, which many people don’t know doesn’t require a definition either—it’s the same as if it were a pointer, ostream*, discussed above. As long as that’s all we’re doing with the ostream&, there’s no need for a full ostream definition—we’re not really using an ostream itself at all, such as by calling functions on it, we’re only using a reference to type for which we only need to know the name. Of course, we would need the full definition if we tried to call any member functions, for example, but we’re not doing anything like that here.

So, as I was saying, we can now move on to get rid of one of the other headers, but only one just yet:

3. Replace e.h with a forward declaration.

#include "e.h"  // class E

Class E is just being mentioned as a parameter and as a return type in function E h(E), so no definition is required and x.h shouldn’t be pulling in e.h in the first place because the caller couldn’t even be calling this function if he didn’t have the definition of E already, so there’s no point in including it again. (Note this would not be true if E were only a return type, such as if the signature were E h();, because in that case it’s good style to include E’s definition for the caller’s convenience so he can easily write code like auto val = x.h();.) All we need to do is replace #include “e.h” with class E;.

Guideline: Never #include a header when a forward declaration will suffice.

That’s it.

You may be wondering why we can’t get rid of the other headers yet. It’s because to define class X means you need to know its size in order to know how much space to allocate for an X object, and to know X’s size you need to know at least the size of every base class and data member. So we need the definitions of A and B because they are base classes, and we need the header definitions of list, C, and D because they are used to define the data members. How we can begin to address some of these is the subject of Part 2…

 

Acknowledgments

Thanks to the following for their feedback to improve this article: Gennaro, Sebastien Redl, Emmanuel Thivierge.