Subj : Re: Memory Barriers, Compiler Optimizations, etc. To : comp.programming.threads From : Alexander Terekhov Date : Wed Feb 02 2005 01:01 pm Scott Meyers wrote: > > I've encountered documents (including, but not limited to, postings on this > newsgroup) suggesting that acquire barriers and release barriers are not > standalone entities but are instead associated with loads and stores, > respectively. Not quite. E.g. see m_lock_status.store_conditional(new, msync) below. // doesn't provide "POSIX-safety" with respect to destruction class mutex_for_XBOX_NEXT { // noncopyable atomic m_lock_status; // 0: free, 1/-1: locked/contention auto_reset_event m_retry_event; // prohibitively slow bin.sema/gate template int attempt_update(int old, int new, T msync) { while (!m_lock_status.store_conditional(new, msync)) { int fresh = m_lock_status.load_reserved(msync::none); if (fresh != old) return fresh; } return old; } public: // ctor/dtor [w/o lazy event init] bool trylock() throw() { return !(m_lock_status.load_reserved(msync::none) || attempt_update(0, 1, msync::acq)); } // bool timedlock() omitted for brevity void lock() throw() { int old = m_lock_status.load_reserved(msync::none); if (old || old = attempt_update(0, 1, msync::acq)) { do { while (old < 0 || old = attempt_update(1, -1, msync::acq)) { m_retry_event.wait(); old = m_lock_status.load_reserved(msync::none); if (!old) break; } } while (old = attempt_update(0, -1, msync::acq)); } } void unlock() throw() { if (m_lock_status.load_reserved(msync::none) < 0 || attempt_update(1, 0, msync::rel) < 0) { // or just !SC m_lock_status.store(0, msync::rel); m_retry_event.set(); } } }; > Furthermore, making them standalone seems to change > semantics (see below). Yet APIs for inserting them via languages like C or > C++ seem to deal with barriers alone -- there are no associated reads or > writes. (See, for example, > http://msdn.microsoft.com/library/default.asp?url=/library/en- > us/vclang/html/vclrf_readwritebarrier.asp.) Yeah. > So I have two questions about this. First, are acquire/release properly > part of loads/stores, or does it make sense for them to be standalone? It's part of "operation". > If > the former, how are programmers in languages like C/C++ expected to make > the association between reads/writes and memory barriers? std::atomic<> > > Next, is it reasonable to assume that compilers will recognize memory > barrier instructions and not perform code motion that is contrary to their > meaning? Yep. > For example: > > x = a; > insertAcquireBarrier(); // or x.acquire = 1 if the barrier > // should not be standalone > y = b; > > Assuming that x, y, a, and b are all distinct locations, is it reasonable > to assume that no compiler will move the assignment to y above the barrier, > or is it necessary to declare x and y volatile to prevent such code motion? I guess you mean ... x = a; y.store(b, msync::rel); It is necessary to have a compiler capable to understand atomic<> and unidirectional reordering constraint associated with its store(T, msync::rel_t) member function. [...] > But within a critical section, instructions can be reordered at will, as > long as they are independent. So let's assume that the two memory > locations are independent. That makes this reordering possible: > > Announce entry to critical section > Access memory location 2 > Access memory location 1 > Announce exit from critical section > > And now we're hosed. What do you mean? > > On the other hand, if the memory barriers are part of the loads/stores, No. Acquire is part of "Announce entry to critical section" operation and release is part of "Announce exit from critical section" thing. regards, alexander. .