C++: std::async fails with "mutex lock failed: Invalid argument"

Originator:bstreiff
Number:rdar://18707735 Date Originated:10/20/2014
Status:Open Resolved:
Product:iOS SDK Product Version:iOS 7.1
Classification:Serious Bug Reproducible:Always
 
Summary:
In a platform-agnostic C++ library, we use std::async to divide work between threads. On iOS 7.1 (11D167), this can fail because locking an internal mutex results in an "invalid argument" error.

With launch policy set to std::launch::async, the failure is thrown from the std::async call site.

If we change the launch policy to std::launch::async|std::launch::deferred, the failure will be thrown at the callsite of get() for the resulting std::future.

I do not appear to get a failure if I use only std::launch::deferred, but this renders makes std::async somewhat useless, as I want to use it for launching asynchronous tasks. However, on iOS there is no way to safely do so.

(The library in question is written in platform-agnostic C++11 code, which is why this uses std::async and not iOS-specific functionality such as GCD or direct pthreads.)

Steps to Reproduce:
[[[ See attached AsyncTest.cpp ]]]

On an iPad MD328LL/A running iOS 7.1 (11D167), the application will terminate within about two minutes with "libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument"

Expected Results:
No failures.

Actual Results:
"libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument" if launch policy is std::launch::async or std::launch::async|std::launch::deferred.

Version:
iOS 7.1 (11D167)

Notes:
This may be related to libc++ bug 19766, http://llvm.org/bugs/show_bug.cgi?id=19766 which was closed as "File a bug with Apple"

Configuration:
iPad MD328LL/A; Xcode 6.0.1 (6A317)


[[[ AsyncTest.cpp:

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <thread>
#include <future>

void threadFunc(uint32_t* begin, uint32_t* end)
{
   // "Work". In the real thing this is a more complicated algorithm.
   for (; begin != end; ++begin)
   {
      *begin = *begin ^ 0xDEADBEEF;
   }
}

void asyncTest()
{
   printf("Starting.\n");

   std::vector<uint32_t> data(1024*1024);

   // Fill with random data.
   for (size_t i = 0; i < data.size(); ++i)
      data[i] = rand();

   for (int iteration = 0; ; ++iteration)
   {
      std::vector<std::future<void>> tasks;
      const size_t kTaskCount = 8;

      // Split up work into kTaskCount chunks.
      for (size_t chunk = 0; chunk < kTaskCount; ++chunk)
      {
         // !!!
         // This can fail due to:
         //    std::system_error, "mutex lock failed: Invalid argument"
         // from the call to std::async.
         // !!!
         tasks.push_back(
            std::async(
               std::launch::async,
               threadFunc,
               data.data() + (chunk * (data.size()/kTaskCount)),
               data.data() + ((chunk+1) * (data.size()/kTaskCount))));
      }

      // Wait for completion.
      // !!!
      // This can fail due to:
      //     std::system_error, "mutex lock failed: Invalid argument"
      // if the call to std::async used std::launch::async|std::launch::deferred!
      // !!!
      for (auto& t : tasks)
      {
         t.get();
      }
   }
}
]]]

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!