You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@stdcxx.apache.org by Liviu Nicoara <ni...@hates.ms> on 2012/09/22 17:48:04 UTC

STDCXX-1056 DCII [was: Re: STDCXX-1056 [was: Re: STDCXX forks]]

On 9/17/12 5:42 PM, Liviu Nicoara wrote:
>[...]
> However, after more careful thought, I think there is a problem there even
> though we don't have an objective proof for it, yet.
>
> The writes are not atomic and they function just like DCII, being subject to
> both compiler reordering and out of order execution. E.g., it is assumed that
> the writes to the _C_impsize and _C_impdata members occur in the program order,
> which would make unguarded reads/tests of _C_impsize safe. This is what the
> facet initialization and access code does, conceptually:
>
> if (_C_impsize) {
>      // already initialized, use data
> }
> else {
>      // initialize
>      mutex.lock ();
>      _C_impdata = get_data_from_database ();
>      _C_impsize = get_data_size ();
>      mutex.unlock ();
> }
>
> The mutex lock and unlock operations introduce memory barriers but they are not
> guaranteeing the order in which the two writes occur. If the writes would occur
> in the exact program order, unguarded reads and tests of _C_impsize would be
> safe. Unfortunately, a thread may see the _C_impsize variable being updated
> before the _C_impdata.

Elaborating on the above -- facet.cpp:~250, the code is actually closer to this:

if (_C_impsize) {
      // already initialized, use _C_impdata
}
else {
      // initialize
      mutex.lock ();

      if (_C_impsize)
          return _C_impdata;

      void* const pv = ...;
      size_t sz = ...;

      _C_impdata = pv;   // 1
      _C_impsize = sz;   // 2

      mutex.unlock ();
}

(1) and (2) can be reordered. Also, when mapping the STDCXX locale database, 
facet.cpp:288, there is another defect writing the two variables:

    _C_impdata = __rw_get_facet_data (cat, _C_impsize, 0, codeset);
                                           ^^^^^^^^^^^
                                           |||||||||||
Set early in __rw_get_facet_data ---------+++++++++++

The only way to order the above writes, preserve the fast checking for 
initialization, and generally salvaging the current algorithm is a full memory 
barrier in between the writes, e.g.:

if (_C_impsize) {
      // already initialized, use data
}
else {
      // ...

      _C_impdata = pv;
      full_membar ();
      _C_impsize = sz;

      // ...
}

Atomics are part of C++11, I wonder if anybody here has working experience with 
the atomic API. A mutex locking will not do because the lock is only acquiring 
and the write before the lock can shift down.

Of course I could be wrong. Any input is welcome.

Thanks,
Liviu