0

I want to know the way to pass a member function to pthread_clean_push. I don't want to declare the cleanup function as static and pass object's reference to it. Below is the scenario

class Foo{
public:
   Foo(string name):name(name){};
   void setBar1(){bar1=malloc(4);sleep(20);};
   void setBar2(){bar2=malloc(10);sleep(50);};
   void cleanBar1(void* arg){free(bar1);};
   void cleanBar2(void* arg){free(bar2);};
private:
   string name;
   void* bar1;
   void* bar2;
};

void* myPThread(void* arg){
   Foo theFoo(*(string*)(arg));
   theFoo.setBar1();
   pthread_cleanup_push(&theFoo.cleanBar1,NULL);   //what is the correct way to
   theFoo.setBar2();
   pthread_cleanup_push(&theFoo.cleanBar2,NULL);   //pass clean functions?
   sleep(100);
   pthread_cleanup_pop(1);
   pthread_cleanup_pop(1);
   return NULL;
}

int main(){
   string biryani="biryani";
   string pappu="pappu";
   pthread_t makeBirayani, makePappu;
   pthread_create(&makeBiryani,NULL,&myPThread,(void*)&biryani);
   pthread_create(&makePappu,NULL,&myPThread,(void*)&pappu);
   pthread_join(makeBiryani,NULL);
   pthread_join(makePappu,NULL);
   return 0;
}

I avoided compile-time error ISO C++ forbids taking the address of a bound member function to form a pointer to member function by using (void(*)(void*))&Foo::cleanBar1 as the argument to pthread_cleanup_push(). But run-time error(segmentation fault) occurs with multiple threads as it has ambiguity in determining the instance to which the cleanup function belongs. How to invoke the member function like here in this scenario? What is the syntax?

Community
  • 1
  • 1
Necktwi
  • 2,483
  • 7
  • 39
  • 62
  • 1
    http://stackoverflow.com/q/25077611/1171191 – BoBTFish Aug 01 '14 at 20:11
  • Strictly speaking, you'll have to have a C (or `extern "C"`) function that you pass the object pointer to that calls the member function: http://stackoverflow.com/a/2068048/12711 I'm curious what your objection is. Certainly, casting a member function pointer to something else won't work. – Michael Burr Aug 01 '14 at 20:47
  • No objection. Thank U folk! – Necktwi Aug 02 '14 at 09:59

3 Answers3

3

Foo::cleanBar1 and Foo::cleanBar2 are non-static member functions, which means that they take an implicit first argument, a pointer to the Foo instance on which they must be invoked (the this pointer). So you cannot pass a pointer to member function to pthread_cleanup_push and get the desired behavior.

You'll need to create a dispatcher function that calls the member function you want, and then pass a pointer to that function to pthread_cleanup_push. This dispatch function could either be a free function, or a static member function of Foo. For instance,

class Foo{
public:
   Foo(string name):name(name){}
   void setBar1(){bar1=malloc(4);sleep(20);}
   void cleanBar1(){free(bar1);}
   static void bar1_callback(void *arg)
   {
       static_cast<Foo*>(arg)->cleanBar1();
   }

   // ..
private:
   string name;
   void* bar1;
   void* bar2;
};

And then pass it to pthread_cleanup_push as

pthread_cleanup_push(&Foo::bar1_callback, &theFoo);

Now the call to pthread_cleanup_pop will execute Foo::bar1_callback and pass it a pointer to the theFoo instance, which will then invoke the cleanBar1() member function.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Thanks! The dispatcher function will do the work but I should try http://stackoverflow.com/questions/25077611/how-to-use-pointer-to-member-function-when-when-pointer-to-global-function-is-re – Necktwi Aug 02 '14 at 09:23
  • @neckTwi That's essentially the same solution, with the, in this case, unnecessary, abstraction of `std::function` thrown in. – Praetorian Aug 02 '14 at 22:32
0

From my understanding of the pthread function pthread_cleanup_push function you can pass the address of a free function (or possibly static to the class Foo) to it and a pointer to an object and then route the call to the correct member.

void clean_bar_1(void* arg)
{
  Foo* p = static_cast<Foo*>(arg);
  p->cleanBar1();
}

And then in myPThread function:

pthread_cleanup_push(&clean_bar_1, &theFoo);

And repeat for the cleanBar2 method.

Niall
  • 30,036
  • 10
  • 99
  • 142
0

The member function needs to know the object for which it is executed. This is why the standard doesn't allow this direct reference.

Just use a lambda-wrapper, like:

 pthread_cleanup_push( [](void*a)->void { reinterpret_cast<Foo*>(a)->cleanBar1(NULL);},
                       &theFoo);    //&theFoo will be passed as argument to the function

However you MUST ensure that your theFoo object still exist when the cleanup is called, because you give its adress when you push the cleanup function, and this address will later be used as argument for the cleanup by the lambda function.

Christophe
  • 68,716
  • 7
  • 72
  • 138