
On Aug 25, 2007, at 6:24 PM, Peter Dimov wrote:
Howard Hinnant:
They are people, just like you and me. And when the standard gives them choices, they must make them. They try to make them in such a way as to please most of their customers. Sometimes they choose correctly, and sometimes they do not (as a vendor I admit to having chosen incorrectly sometimes). No vendor that I'm aware of is immune to making incorrect choices.
I accept that. So what's the problem? Vendor A ignores the mutex argument, his users complain, so he finds a way to add checking that doesn't increase sizeof(condition) and doesn't compromise performance for the rest of the user base. Vendor B stores the mutex pointer in std::condition, his users complain, he waits for the next ABI breakage and finds a way to achieve comparable checking quality and efficiency without increasing sizeof(condition). Everyone is happy.
...
I don't know any more than you do, so my "forward looking" is no more or less valid than yours. But I strongly believe that it would be careless of us to be careless with sizeof(std::condition).
I'm not being careless with sizeof(condition). I already demonstrated two ways to achieve checking without storing the pointer into the condition itself, and hinted at another possibility (exploiting an unused void* in pthread_cond_t). I also questioned your assertion that increasing sizeof(condition) from 28 to 32 is of practical importance.
I've been using "sizeof(condition)" as shorthand for "reducing L1 cache misses". Your technique of moving the checking to a static map (or unordered_map)<void*, void*> does indeed reduce the sizeof(condition), but does not reduce L1 cache misses. It is simply moving the checking data from one place to another. The checking data still has to be written and read, even when a non-checking condition is desired. My estimation is that your "external checking data storage" plan is less efficient, not more efficient, than just storing the checking data internal to the condition. Though admittedly that is just an estimate. I haven't measured. At this point I'm really leaning towards getting back to basics: class condition { public: condition(); ~condition(); condition(const condition&) = delete; condition& operator=(const condition&) = delete; void notify_one(); void notify_all(); template <class Lock> void wait(Lock& lock); // Lock::mutex_type must be mutex template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred); // Lock::mutex_type must be mutex template <class Lock> // Lock::mutex_type must be mutex bool timed_wait(Lock& lock, const utc_time& abs_time); template <class Lock, class Predicate> // Lock::mutex_type must be mutex bool timed_wait(Lock& lock, const utc_time& abs_time, Predicate pred); }; Or perhaps even: class condition { public: condition(); ~condition(); condition(const condition&) = delete; condition& operator=(const condition&) = delete; void notify_one(); void notify_all(); void wait(unique_lock<mutex>& lock); template <class Predicate> void wait(unique_lock<mutex>& lock, Predicate pred); bool timed_wait(unique_lock<mutex>& lock, const utc_time& abs_time); template <class Predicate> bool timed_wait(unique_lock<mutex>& lock, const utc_time& abs_time, Predicate pred); }; Or perhaps: class condition { public: condition(); ~condition(); condition(const condition&) = delete; condition& operator=(const condition&) = delete; void notify_one(); void notify_all(); void wait(mutex& mut); template <class Predicate> void wait(mutex& mut, Predicate pred); bool timed_wait(mutex& mut, const utc_time& abs_time); template <class Predicate> bool timed_wait(mutex& mut, const utc_time& abs_time, Predicate pred); }; The above is small, relatively simple, and everything else we've discussed can be built on top of it (some parts more easily than others). The above is the one part of the entire original proposal that is indispensable (goal #1). The above is a one-to-one mapping to pthread_cond_t/pthread_mutex_t. We might use the diagnostic library to return error codes or throw system_error for any non-zero returns from pthread_cond_*, perhaps with overloads to specify which error reporting style the user wants to have. class condition { public: condition(); ~condition(); condition(const condition&) = delete; condition& operator=(const condition&) = delete; void notify_one(); void notify_one(error_code&); void notify_all(); void notify_all(error_code&); void wait(mutex& mut); void wait(mutex& mut, error_code&); template <class Predicate> void wait(mutex& mut, Predicate pred); template <class Predicate> void wait(mutex& mut, Predicate pred, error_code&); bool timed_wait(mutex& mut, const utc_time& abs_time); bool timed_wait(mutex& mut, const utc_time& abs_time, error_code&); template <class Predicate> bool timed_wait(mutex& mut, const utc_time& abs_time, Predicate pred); template <class Predicate> bool timed_wait(mutex& mut, const utc_time& abs_time, Predicate pred, error_code&); }; -Howard