Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

[Bug Report] OMP threadprivate objects not being destructed

Avi_G_
Beginner
310 Views

I'm using Intel C++ 15.0 (_OPENMP == 201307; i.e. the July 2013 version of OpenMP which is 4.0). According to the OMP 4.0 standard threadprivate objects are to be destroyed in an unspecifed order (p. 152 line 9-10):

The order in which destructors for different threadprivate variables of class type are called is unspecified

The manner of destruction of the objects should be the same as that of static objects (p.151, lines 7-9):

The storage of all copies of a threadprivate variable is freed according to how static variables are handled in the base language, but at an unspecified point in the program.

However, it appears that the objects' destructors  are not being called. Based on this link, I've added an explicit barrier (doesn't help). I also set the lifetime of the threads to zero to make sure they don't outlive the master thread as well as adding some busy work after the last parallel region.

Here's the (roughly) minimal example:

#include <iostream>
#include <omp.h>

class myclass {
    int _n;
public:
    myclass(int n) : _n(n) { std::cout << "int c'tor\n"; }

    myclass() : _n(0) { std::cout << "def c'tor\n"; }

    myclass(const myclass & other) : _n(other._n)
                    { std::cout << "copy c'tor\n"; }

    ~myclass() { std::cout << "bye bye from " << _n << "\n"; }

    void print() { std::cout << _n << "\n"; }

    void add(int t) { _n += t; }
};

myclass globalClass;
#pragma omp threadprivate (globalClass)

int main(int argc, char* argv[])
{
    std::cout << "\nBegninning main()\n";

    // Kill the threads immediately
    kmp_set_blocktime(0);

#pragma omp parallel
    {
        globalClass.add(omp_get_thread_num());
        globalClass.print();
#pragma omp barrier
        //Barrier doesn't help
    }

    // Try some busy work, takes a few seconds
    double dummy = 0.0;
    for (int i = 0; i < 199999999; i++)
    {
        dummy += (sin(i + 0.1));
    }
    std::cout << dummy << "\n";

    std::cout << "Exiting main()\n";
    return 0;
}

I would expect 4 (on my machine) "bye bye" messages, whereas I only see one.

0 Kudos
2 Replies
jimdempseyatthecove
Honored Contributor III
310 Views

Consider a variation on this:

class myclass {
    static volatile int _refcnt = 0;
    static myclass* _p = NULL;
    int _n;
public:
    myclass(int n) : _n(n) { 
      if(_p==NULL) {
        _p = this; _refcnt = 1;
      } else {
         #pragma omp atomic
         ++_refcnt;
      }
      std::cout << "int c'tor\n"; }

    myclass() : _n(0) { 
       if(_p==NULL) {
        _p = this; _refcnt = 1;
      } else {
         #pragma omp atomic
         ++_refcnt;
      }
     std::cout << "def c'tor\n"; }

    myclass(const myclass & other) : _n(other._n)
                    { std::cout << "copy c'tor\n"; }

    ~myclass() {
      if(_p == this) {
        while(_refcnt > 1)
          sleep(0);
      }
      #pragma omp atomic
      --_refcnt;
      std::cout << "bye bye from " << _n << "\n"; }

    void print() { std::cout << _n << "\n"; }

    void add(int t) { _n += t; }
};

(untested)

Jim Dempsey

0 Kudos
Avi_G_
Beginner
310 Views

 

In your case, none of the four objects are properly disposed. The main threads instances destructor (_p == this) is called, but because _refcnt is equal to the number of threads it never passes the while loop. The other threads don't call the destructor for their objects and therefore _refcnt is never decremented. The end result is that the program never exits. The issue remains the same.

(tested)

Avi

jimdempseyatthecove wrote:

Consider a variation on this:

class myclass {
    static volatile int _refcnt = 0;
    static myclass* _p = NULL;
    int _n;
public:
    myclass(int n) : _n(n) { 
      if(_p==NULL) {
        _p = this; _refcnt = 1;
      } else {
         #pragma omp atomic
         ++_refcnt;
      }
      std::cout << "int c'tor\n"; }

    myclass() : _n(0) { 
       if(_p==NULL) {
        _p = this; _refcnt = 1;
      } else {
         #pragma omp atomic
         ++_refcnt;
      }
     std::cout << "def c'tor\n"; }

    myclass(const myclass & other) : _n(other._n)
                    { std::cout << "copy c'tor\n"; }

    ~myclass() {
      if(_p == this) {
        while(_refcnt > 1)
          sleep(0);
      }
      #pragma omp atomic
      --_refcnt;
      std::cout << "bye bye from " << _n << "\n"; }

    void print() { std::cout << _n << "\n"; }

    void add(int t) { _n += t; }
};

(untested)

Jim Dempsey

0 Kudos
Reply