2

Suppose i have an atomic pointer:

std::atomic<void*> mItems;

and in a function when one thread need to access that, it first check it, and if it is null, thread will allocate memory for it:

void* lItems = std::atomic_load_explicit(&mItems, memory_order_relaxed);
if(lItems == nullptr)
{
    void* lAllocation = malloc(...);

    if(!std::atomic_compare_exchange_strong_explicit(
        &mItems, 
        &lItems, 
        lAllocation, 
        memory_order_relaxed, 
        memory_order_relaxed))
    {
        free(lAllocation);
    }
}
    ...

But if N thread run this method concurrency and see mItems equal to null then all of them will allocate memory and N - 1 of them will free agian.

How i can write similar method with better approach.

MRB
  • 3,617
  • 3
  • 29
  • 40
  • 1
    Punch ["double checked locking"](http://en.wikipedia.org/wiki/Double-checked_locking) into your favorite search engine. – David Schwartz Oct 08 '13 at 10:07
  • 4
    Surprise! This is a solved problem http://en.cppreference.com/w/cpp/thread/call_once – R. Martinho Fernandes Oct 08 '13 at 10:12
  • 1
    @R.MartinhoFernandes: I doubt that `call_once` is lock-free, though. – Kerrek SB Oct 08 '13 at 10:24
  • @KerrekSB why wouldn't it? (IOW is this problem unsolvable?) – R. Martinho Fernandes Oct 08 '13 at 10:25
  • @DavidSchwartz How can i use double check lock in my method without using lock or mutex. (My mind is lock due to writing multithread code ;) – MRB Oct 08 '13 at 10:26
  • @R.MartinhoFernandes: I doubt you could make a complex call guaranteed to happen only once, with its result available to all threads, in a lock-free way. You only get one single operation to synchronize, so you either accept discarded operations, or you make someone wait. – Kerrek SB Oct 08 '13 at 10:27
  • @KerrekSB so it can't be done better than what the OP has? – R. Martinho Fernandes Oct 08 '13 at 10:28
  • http://stackoverflow.com/a/11711991/700825 – NoSenseEtAl Oct 08 '13 at 12:28
  • 1
    std::atomic is *not* a substitute for std::mutex – Hans Passant Oct 08 '13 at 13:47
  • @R.MartinhoFernandes: The OP is solving a different problem; he's happy to throw away the result if there was a race. I'd say that's far less desirable than having a simple mutex and genuinely just performing the expensive allocation once. – Kerrek SB Oct 08 '13 at 15:16
  • @KerrekSB: If i use my own allocators that are faster than c++ default allocator functions and also are lock-free what solution you suggest? Use mutext or use same approach as above – MRB Oct 08 '13 at 15:49
  • @MohammadRB: Use a mutex. You'll only be holding the lock for a very short time in the general case, and good mutexes are reasonably efficient for that use case. – Kerrek SB Oct 08 '13 at 15:53

2 Answers2

1

I guess you can make the pointer your mutex, using some well-known value (say, the address of a global) as a flag that some other thread is already doing the allocation.

So, your values are: NULL -> magic "allocation in progress" pointer -> real allocation.

The code would do something like:

  • load address: it will have one of the following values:
    1. NULL: CAS with magic value
      • did CAS succeed? If yes, we're doing the allocation and everyone knows it
        • do the allocation, store the new address, we're done (shouldn't have to CAS it since we already guaranteed exclusion with the first CAS)
      • no, then someone else is doing the allocation, go back to 1
    2. address not NULL, but the magic value
      • so someone is already doing the allocation - just wait until it changes, and use the eventual value
    3. neither NULL nor magic, so it's already a real allocated value - just use it

This way only one thread does the allocation, but your other N-1 threads may be busy waiting. Whether this is really better will vary ...

Useless
  • 55,472
  • 5
  • 73
  • 117
  • Thanks, this approach was in my mind too. but this is similar to once_flag. i only search for better approach although all of these are acceptable, but +1 for your answer. – MRB Oct 08 '13 at 14:52
  • The difference is you don't have a separate flag: you do a single load and, only in the corner case, CAS a single word. You incur no cache or other overhead in the usual fast case. – Useless Oct 08 '13 at 14:57
0

As I get it, you need that structure as soon as the first thread execute your function, so how about move the allocation before starting any thread? I mean, rearrange the code so that your function is called with the atomic pointer as a parameter, and the structure is allocated before any of the threads calling your function are spawned (you can also avoid creating any thread if the allocation fails)

something like:

std::atomic<void*> mItems;
void func_that_uses_mItems();

int main()
{
    mItems = init_struct();
    std::thread t1 { &func_that_uses_mItems };
    std::thread t2 { &func_that_uses_mItems };


    // ... join with or detach threads ...

    return( 0 );
}

If the allocation fails an exception is thrown and no thread is started.

  • Well, the problem is that i don't want do this. suppose i have an array of items and each of them point to an array of other items and i want initialize them only if some body access them. – MRB Oct 08 '13 at 14:46