3

Somewhere in a library I've a function which looks like -

inline int getIword()
{
    static int i = std::ios_base::xalloc();
    return i;
}

Now you can read about std::ios_base::xalloc() call here, but I want to emphasize this line from the metioned link -

This function is thread-safe; concurrent access by multiple threads does not result in a data race. (since C++14)

It says "since C++14" but I need C++11 support too. Since the function call is actually initializing a static local variable of getIword() method and we know local static variable initialization is thread safe for C++11 code, is it safe to assume this code is -

  • safe if there is only subsequent read calls made to function, eg. auto something = getIword().

  • safe? in case of code such as given below:

...

operator<<(std::ostream &os, T const value)
{
    if (value == ...) {
        os.iword(getIword()) = 1;
    } else if (value == ...) {
        os.iword(getIword()) = 0;
    }
    return os;
}

...

and if it is unsafe in the later example, where should I put the lock_guards to make it safe for C++11? Around return i or whole method or where call is being made?

Community
  • 1
  • 1
Abhinav Gauniyal
  • 7,034
  • 7
  • 50
  • 93
  • It may be worthwhile checking whether your C++ implementation is already threadsafe. The _guarantee_ is new with C++14, but it's common enough for actual implementations to offer such details early. – MSalters Dec 23 '16 at 09:59
  • @MSalters The guarantee about thread safe static initialization goes back to C++11. – NathanOliver Dec 23 '16 at 17:54
  • @NathanOliver MSalters was probably talking about guarantee of that function. – Abhinav Gauniyal Dec 24 '16 at 04:44

2 Answers2

4

Static local variable initialization is thread safe in the sense that if multiple threads call that function then only one of them will actually initialize the variable. It does not protect the thing that you initialize from. That means that in this case if you have two different threads, one calling getIword and another calling a different function that also happens to call std::ios_base::xalloc() at the same time then those two calls will not be synchronized and you will have a data race which in turn is undefined behavior.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

On my PC, I can see the implementation of std::ios_base::xalloc() is :

    static int __CLRCALL_OR_CDECL xalloc()
    {   // allocate new iword/pword index
    _BEGIN_LOCK(_LOCK_STREAM)   // lock thread to ensure atomicity
        return (_Index++);
    _END_LOCK()
    }

So basically it is just doing a value incremental, and without thread safe guarantee you will get some duplicate value out of std::ios_base::xalloc() under race condition.

The solution I would take will be writing your own xalloc() by using std::atomic.

int xalloc()
{
    static std::atomic_int i;
    return i++;
}

Or wrapping std::ios_base::xalloc() to a new method with lock.

Xu Pan
  • 310
  • 3
  • 12