[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#810868: threads: condition_variable or atomic causes _sometimes_ a deadlock



Package: g++-4.9
Version: 4.9.2-10
Severity: normal

Dear Maintainer,

there is a mysterious bug in g++ or its libraries.
The bug happens when one compiles and executes the following test code multiple
times:
sometimes the executable just hangs, ie. a deadlock happens.
I think the error lies not in the test code, but in the g++ libraries or even
in g++ itself.

/*
test.cpp

2016-01-13-We
Author: U.Mutlu

condition variable example (but is [sometimes] buggy)

Code adapted from the example at [1]

Question:
  How to fix this code?
  --> it could be a low-level issue, ie. compiler-, stdlibrary-, or pthread-
issue

What-it-does:
  10 threads race for a job. The main thread supplies the job.
  One of the threads takes the job and processes it.
  This is repeated 10000 times.
  BUT: sometimes the pgm freezes (ie. a deadlock bug happens) --> see below

Compile:
  g++ -Wall -O2 -std=gnu++11 test.cpp -lpthread

Run:
  ./a.out
  repeat multiple times (up to 10 times should be ok for the runtime bug to
show up)

Symptoms of the bug:
  sometimes pgm lands in an endless-loop! ie. deadlock happens --> pgm not
finishing

Test environment:
  - g++ --version
    g++ (Debian 4.9.2-10) 4.9.2

  - uname -a
    Linux my 4.2.0-0.bpo.1-amd64 #1 SMP Debian 4.2.6-3~bpo8+2 (2015-12-14)
x86_64 GNU/Linux

  - dpkg -l | grep -i pthread
    ii libpthread-stubs0-dev:amd64  0.3-4  amd64 pthread stubs not provided by
native libc, development files

See also:
  [1] http://www.cplusplus.com/reference/condition_variable/condition_variable/
  [2] http://en.cppreference.com/w/cpp/thread/condition_variable
*/

#include <iostream>
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
#include <vector>

using namespace std;

mutex        mtx;
condition_variable cv;
atomic<bool> fReady;      // init done in main
atomic<bool> fQuit;       // init done in main
atomic<bool> fThreadsOk;  // init done in main

void threadfunc(int id)
  {
/*
Any thread that intends to wait on condition_variable has to acquire a
unique_lock<mutex> first.
The wait operations atomically release the mutex and suspend the execution of
the thread.
When the condition variable is notified, the thread is awakened, and the mutex
is reacquired [2].
*/

    while (!fQuit)
      {
        // wait for fReady:
        unique_lock<mutex> lck(mtx);
        fThreadsOk = true;
        cv.wait(lck);   // unlocks lck, and waits for cv be signalled; then
autom. reacquires lck
        if (fQuit) break;
        if (!fReady) continue;

        // work, ie. process the job:
        cout << "thread " << id << " has grabbed the job\n";
        //...

        // notify parent with fReady=false; wakes up also all the other threads
        fReady = false;
        cv.notify_all();
      }
  }

int main()
  {
    fQuit      = false;
    fReady     = false;
    fThreadsOk = false;

    const size_t N = 10;
    vector<thread> threads;
    for (size_t i = 0; i < N; ++i)
      threads.push_back(thread(threadfunc, i));

    // wait so that at least one thread has done "cv.wait(lck)", see above
    while (!fThreadsOk) sleep(1);

    for (size_t i = 0; i < 10000; ++i)
      {
        cout << N << " threads ready to race: ";

        // notify threads about work to do
        {
        unique_lock<mutex> lck(mtx);
        fReady = true;
        cv.notify_all();

        // wait till job was done
        while (fReady) cv.wait(lck);
        }
      }

    // set fQuit and wake up the threads
    {
    unique_lock<mutex> lck(mtx);
    fQuit = true;
    cv.notify_all();
    }

    // wait till all threads quit:
    for (auto& th : threads) th.join();

    cout << "pgm finished\n";
    return 0;
  }

// end of test.cpp



-- System Information:
Debian Release: 8.2
  APT prefers stable
  APT policy: (500, 'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 4.2.0-0.bpo.1-amd64 (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: sysvinit (via /sbin/init)

Versions of packages g++-4.9 depends on:
ii  gcc-4.9            4.9.2-10
ii  gcc-4.9-base       4.9.2-10
ii  libc6              2.19-18+deb8u1
ii  libcloog-isl4      0.18.2-1+b2
ii  libgmp10           2:6.0.0+dfsg-6
ii  libisl10           0.12.2-2
ii  libmpc3            1.0.2-1
ii  libmpfr4           3.1.2-2
ii  libstdc++-4.9-dev  4.9.2-10
ii  zlib1g             1:1.2.8.dfsg-2+b1

g++-4.9 recommends no packages.

Versions of packages g++-4.9 suggests:
pn  g++-4.9-multilib    <none>
pn  gcc-4.9-doc         <none>
pn  libstdc++6-4.9-dbg  <none>

-- no debconf information


Reply to: