0

My environment is C++ for Linux-Xenomai on ARM gnueabi. After spawning a new pthread successfully I discovered that the class instance was out of scope to the thread. Accessing class instance objects, variables, structures etc. from the thread returned arbitrary values and often 'Segmentation Fault'.

After having spent days of onerous time searching for a solution on the net, I took a guess and tried using the 'this' pointer as argument to pthread_create. And voila! The class instance became visible to the thread. The question is why?

void*(*server_listener_fptr)(void*);    // declare the function ptr
server_listener_fptr = reinterpret_cast<void*(*)(void*)>(&UDP_ClientServer::server_listener);
iret = pthread_create(&s_thread, NULL, server_listener_fptr, this);
usysinc
  • 17
  • 2
  • 1
    `server_listener` needs to know what `UDP_ClientServer` object to operate on, and it gets it as an argument. If you don't pass `this`, how would it know which object to use? – Barmar May 05 '17 at 21:03
  • 3
    I don't know what `UDP_ClientServer::server_listener` is, but that reinterpret cast tells me you just traded one sort of undefined behavior for another. – StoryTeller - Unslander Monica May 05 '17 at 21:26
  • 1
    This is a gross violation of C++ specification. You cannot just reinterpret_cast C++ member-function to a free function - this is an UB. You just got lucky that gcc happens to use same calling conventions for member functions with no arguments and for free functions with single argument. There are numerous articles on how to use C++ member functions with C callbacks, please read up on that (general rule - you can't use them directly). – Andrey Turkin May 05 '17 at 21:28
  • 1
    If you use c++11 I would suggest using std::thread stuff, it is quite portable and meant for c++, not c. http://www.cplusplus.com/reference/thread/thread/ – Bamaco May 05 '17 at 21:30
  • 2
    http://stackoverflow.com/questions/1151582/pthread-function-from-a-class – stark May 05 '17 at 21:31
  • StroyTeller - reinterpret cast is the standard method for casting a C++ class method to a C style function pointer. – usysinc May 06 '17 at 18:31
  • Bamaco - I don't intend modifying my environment and using C++11, so that's the reason for not usinf std::thread, but yes it would make life easier. I am however really looking for the technical explanation for why the 'this' pointer as argument to pthread_create manages to make available the entire class object instance members to the threaded method. – usysinc May 06 '17 at 18:36

1 Answers1

0

There is a simple reason why this effectively launches a class instance as an independent thread of a parent process. The debug execution log below sheds some light on the situation. The UDP_ClientServer class instance's ::init() method is entered followed by the creation of a ::server_listener(void*) thread which is a class method of a class instance of the class UDP_ClientServer. The ::init() method that spawned the thread then exits as UDP_ClientServer::init() exit ..., followed shorthy by the class instance method ::server_listener(void*) announcing itself as a thread, as in UDP_ClientServer::server_listener(void*) entry ....

# ./xeno_pruss 37 -INOAUTOENA -FREQ 100
-> -IRQ  37
-> -I_NOAUTOENA
-> -FREQ 100.000000
-> Starting UDP_ClientServer...
-> UDP_ClientServer::init() entry ...
-> UDP Server on wlan0 IP: 192.168.1.10 PORT: 9930 
-> UDP Server fd: 3 
-> Bind to IP address: 0.0.0.0
-> UDP_ClientServer::init() creating thread ::server_listener(void*) ...
-> UDP_ClientServer::init() exit ...
-> main - Opening server on IRQ 37
-> main - rt_intr_create - interrupt object created on IRQ 37
-> UDP_ClientServer::server_listener(void*) entry ...
-> rt_task_create created task MyIrqServer
-> disabling and reseting the I2C1 peripheral, writing I2C_CON = 0x0
-> disabling and reseting the I2C2 peripheral, writing I2C_CON = 0x0
-> rt_task_start started thread MyIrqServer
-> started real-time interrupt server thread for IRQ37 
-> pausing ...
-> *** irq_server entry ***
-> Task name: MyIrqServer 
-> initializing the pru subsystem driver
-> prussdrv_open() opened pru interrupt...
-> prussdrv_map_prumem completed...
-> initializing 16 x 32-bit words of p_pru_shared_memu ...
-> current value @ p_pru_shared_memu[0] : 0
-> current value @ p_pru_shared_memu[0] : 10000000
-> mapped device (Success)
-> *** mem mapped CM_PER registers...
-> enabling I2C1 peripheral clocking, writing CM_PER_I2C1_CLKCTRL = 0x02
-> CM_PER_I2C1_CLKCTRL: 00000002 

The thread is created as below. The (void*)this pointer is provided as argument passed by of pthread_create to class instance method ::server_listener.

printf("\t-> UDP_ClientServer::init() creating thread      ::server_listener(void*) ...\n");
void*(*server_listener_fptr)(void*);    // declare the function ptr
server_listener_fptr = reinterpret_cast<void*(*)(void*)>(&UDP_ClientServer::server_listener);
iret = pthread_create(&s_thread, NULL, server_listener_fptr, this);

The spawned ::server_listener thread never exits as seen below.

void* UDP_ClientServer::server_listener(void*ptr)
{
    printf("\t-> UDP_ClientServer::server_listener(void*) entry ...\n");
    for(;;) /* Run forever */ 
    {

This of course gives the programmer the unique ability to describe complex state machines in a robust concurrent versus sequential processes manner similar to the methods employed in writing RTL in VHDL or Verilog.

The answer to the question is simply that for a class

class My_Class
{
public:
    My_Class();
    void func(void);
};

For the declaration of a class object instance

My_Class instance;

A call on the class object instance, to a member

instance.func(void);

Is by definition of the C++ language specification, compiled to

func(&instance);

Where the passed reference '&instance' IS the member's 'this' pointer.

usysinc
  • 17
  • 2