Thread: C++ thread inside a class hang issue

  1. #1
    Registered User
    Join Date
    Oct 2009
    Posts
    7

    C++ thread inside a class hang issue

    I have created a wrapper class with two threads for a serial line based interface. I ended up seeing Segmentation errors about once every 10 to 20 times of stopping and starting threads again in a single instance of the interface.

    I wrote a simpler wrapper for a thread and still see issue. I am including a sample code that I have been testing. I wrote a class that could start and stop a single thread. I also tried a few variations with pthread_join and without. What I find, is that my system hangs at the time of stopping the thread. I have observed the same with my serial driver wrapper. I am curious, if I am missing or making an error in handling the thread.

    The errors I receive always come, as the thread or threads are being terminated. I get Segmentation error and also a pthread_mutex error in my serial driver. In this example, I see a hang.

    Compiler
    Code:
    roman@the777 ~ $ gcc -v
    Using built-in specs.
    Target: i686-pc-linux-gnu
    Configured with: /var/tmp/portage/sys-devel/gcc-4.1.2/work/gcc-4.1.2/configure --prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/4.1.2 --includedir=/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include --datadir=/usr/share/gcc-data/i686-pc-linux-gnu/4.1.2 --mandir=/usr/share/gcc-data/i686-pc-linux-gnu/4.1.2/man --infodir=/usr/share/gcc-data/i686-pc-linux-gnu/4.1.2/info --with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/g++-v4 --host=i686-pc-linux-gnu --build=i686-pc-linux-gnu --disable-altivec --enable-nls --without-included-gettext --with-system-zlib --disable-checking --disable-werror --enable-secureplt --disable-multilib --enable-libmudflap --disable-libssp --disable-libgcj --with-arch=i686 --enable-languages=c,c++,treelang,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu
    Thread model: posix
    gcc version 4.1.2 (Gentoo 4.1.2 p1.3)
    Here is the code:

    Code:
    /**
     * thread_attempts.cpp
     *
     **/
    
    
    #include<stdlib.h>
    #include <pthread.h>
    #include <stddef.h>
    #include <unistd.h>
    #include <errno.h>
    #include <signal.h>
    #include <queue>
    #include <string.h>
    #include <stdio.h>
    
    class 	ThreadAttempt
    {
    	private:
    
    		static void * 	thread_init(void *arg);
    		pthread_t 		thread;
    		int 			thread_function();
    
    		void 	start_thread();
    		void 	stop_thread();
    
    
    		pthread_mutex_t data_access_mutex;
    
    		bool	thread_started;
    		bool 	thread_alive;
    		bool	thread_needs_to_exit;
    		int 	thread_run_counter;
    
    	public:
    
    		ThreadAttempt();
    		~ThreadAttempt();
    
    		void	start();
    		void	stop();
    		int		count();
    
    };
    
    ThreadAttempt::ThreadAttempt()
    {
    	printf("initialized the trial\n");fflush(stdout);
    
    	thread_started = false;
    	thread_alive = false;
    	thread_needs_to_exit = false;
    	thread_run_counter = 0;
    
    	pthread_mutex_init(&data_access_mutex, NULL);
    
    }
    
    ThreadAttempt::~ThreadAttempt()
    {
    	stop();
    	
    	pthread_mutex_destroy(&data_access_mutex);
    
    	printf("destoyed...\n");fflush(stdout);
    }
    
    void ThreadAttempt::start()
    {
    	bool clear_to_start = true;
    	int count = 0;
    
    	pthread_mutex_lock(&data_access_mutex);
    		if( thread_started || thread_alive ) clear_to_start = false;
    	pthread_mutex_unlock(&data_access_mutex);
    
    	if( clear_to_start )
    	{
    		pthread_mutex_lock(&data_access_mutex);
    			thread_started = true;
    			thread_alive = true;
    			thread_needs_to_exit = false;
    			thread_run_counter += 1;
    			count = thread_run_counter;
    		pthread_mutex_unlock(&data_access_mutex);
    
    		pthread_create(&thread, NULL, &thread_init, (void *) this);
    
    		printf("started the thread - attempt number %d\n", count);fflush(stdout);
    	}
    }
    
    void ThreadAttempt::stop()
    {
    	bool clear_to_stop = false;
    	bool alive = true;
    	int count = 0;
    
    	pthread_mutex_lock(&data_access_mutex);
    		if( thread_started && thread_alive ) clear_to_stop = true;
    	pthread_mutex_unlock(&data_access_mutex);
    
    	if( clear_to_stop )
    	{
    		pthread_mutex_lock(&data_access_mutex);
    			thread_needs_to_exit = true;
    		pthread_mutex_unlock(&data_access_mutex);
    
    		while( alive==true )
    		{
    //			usleep(1000000); // one sec
    			pthread_mutex_lock(&data_access_mutex);
    				alive = thread_alive;
    				count = thread_run_counter;
    			pthread_mutex_unlock(&data_access_mutex);
    		}
    		
    		printf("stopped the thread - attempt number %d\n", count);fflush(stdout);
    
    		pthread_mutex_lock(&data_access_mutex);
    			thread_started = false;
    			thread_alive = false;
    			thread_needs_to_exit = false;
    		pthread_mutex_unlock(&data_access_mutex);
    	}
    }
    
    
    int ThreadAttempt::count()
    {
    	int tmp=0;
    	pthread_mutex_lock(&data_access_mutex);
    		tmp = thread_run_counter;
    	pthread_mutex_unlock(&data_access_mutex);
    	return tmp;
    }
    
    void 	*ThreadAttempt::thread_init(void *arg){ ((ThreadAttempt *) arg)->thread_function(); }
    
    int		ThreadAttempt::thread_function()
    {
    	bool 	run_flag = true;
    	bool	tick = true;
    
    	while( run_flag )
    	{
    		pthread_mutex_lock(&data_access_mutex);
    			if( thread_needs_to_exit ) run_flag = false;
    		pthread_mutex_unlock(&data_access_mutex);
    //		usleep((int)(0.01*1000000));
    //		if( tick ) { tick=false; printf("tick "); fflush(stdout); }
    //		else { tick=true; printf("tock "); fflush(stdout); }
    	}
    
    	printf("\n"); fflush(stdout);
    
    	pthread_mutex_lock(&data_access_mutex);
    		thread_started = false;
    		thread_alive = false;
    		thread_needs_to_exit = false;
    	pthread_mutex_unlock(&data_access_mutex);
    
    	pthread_exit(NULL);
    }
    
    
    
    int main(void)
    {
    	ThreadAttempt *trial = new ThreadAttempt();
    	int	countdown = 1000000;
    
    	while( countdown )
    	{
    		trial->start();
    		usleep((int)(0.1*1000000));
    		trial->stop();
    		countdown-=1;
    	}
    	return 0;
    }
    Makefile
    Code:
    all: thread_trial
    
    CPPFLAGS = -x c++ -O0 -c
    CFLAGS = -x c -O0 
    
    
    # add -v for verbose
    # add -pthread for thread support
    
    LFLAGS = -static -O0 -pthread 
    
    CC = g++ 
    
    
    thread_attempts.o: thread_attempts.cpp
    	$(CC) $(CPPFLAGS) thread_attempts.cpp 
    
    thread_trial: thread_attempts.o
    		$(CC) $(LFLAGS) thread_attempts.o -o example_compiled
    
    
    
    clena:	clean
    claen:  clean
    celan:  clean
    clean: 
    	rm -rf ./*.o
    	rm -f example_compiled
    Output
    Code:
    stopped the thread - attempt number 374
    started the thread - attempt number 375
    
    stopped the thread - attempt number 375
    started the thread - attempt number 376
    
    stopped the thread - attempt number 376
    started the thread - attempt number 377
    
    stopped the thread - attempt number 377
    started the thread - attempt number 378
    
    stopped the thread - attempt number 378
    started the thread - attempt number 379
    
    stopped the thread - attempt number 379
    started the thread - attempt number 380
    
    stopped the thread - attempt number 380
    started the thread - attempt number 381
    
    stopped the thread - attempt number 381
    started the thread - attempt number 382
    
    stopped the thread - attempt number 382
    started the thread - attempt number 383
    Top
    Code:
    top - 06:42:06 up 2 days,  3:18,  1 user,  load average: 1.28, 1.47, 1.56
    Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
    Cpu(s): 27.8%us,  0.2%sy,  0.0%ni, 71.9%id,  0.0%wa,  0.0%hi,  0.1%si,  0.0%st
    Mem:   2072172k total,   992692k used,  1079480k free,    75704k buffers
    Swap:  2008116k total,    56028k used,  1952088k free,   611792k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
     4349 roman     20   0 3058m 3364  212 R  100  0.2  51:37.78 example_compile
    Please advice.....................
    Last edited by diefast9; 10-20-2009 at 07:22 AM. Reason: Added compiler details

  2. #2
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    are you locking the same mutex twice from the same thread?

  3. #3
    Registered User
    Join Date
    Oct 2009
    Posts
    7
    I do.

    In my original code, it could have been locked twice or three times during a run. However, the threads were asleep most of the time. I lock the mutex and unlock right away.

  4. #4
    Registered User
    Join Date
    Oct 2009
    Posts
    7
    I think that the error is due to memory or fd max limit. I don't know, if there are resources that I fail to release. I see almost identical case, every time I run the code.

    I see the same count for this code and a similar case with a more complicated driver. I used lsof and /proc filesystem details but can't make the ends out of it.....

  5. #5
    Registered User
    Join Date
    Oct 2009
    Posts
    7
    I experience a hang most of the time. Sometimes I get a Seg error. However,occasionally I get this

    Code:
    pthread_mutex_lock.c:289: __pthread_mutex_lock: Assertion `(-(e)) != 3 || !robust' failed
    Please help.........

  6. #6
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    i haven't seen any potential deadlock scenarios in your code, but this looks dangerous

    Code:
    void 	*ThreadAttempt::thread_init(void *arg){ ((ThreadAttempt *) arg)->thread_function(); }
    defininte segfault potential there.

  7. #7
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    You are leaking the thread handle. You have to do one of the following: 1) create the thread detached, 2) call pthread_join on the attached thread handle, 3) call pthread_detach on the attached thread handle.

    Other issues:

    Because thread_init() doesn't have extern "C" linkage, it's not guaranteed to work with pthread_create on all compilers/platforms. On some compilers C linkage and C++ linkage functions have different calling conventions and/or name-mangling rules.

    You have to be careful when taking "snapshots" of shared data. For instance:
    Code:
    int shared;
    int snapshot;
    ...
    lock
    snapshot = shared;
    unlock
    ...
    Access to 'shared' is done under a lock, which is good. However, if the logic continues under the assumption that "snapshot == shared" then you can get into trouble.

    In the stop() method, 'clear_to_stop' and 'alive' are snapshots. As long as the stop()/start() methods are called in a single-thread manner then this is ok. If stop()/start() are ever called by multiple threads "at the same time", then the snapshot becomes meaningless as soon as the lock is released (because once the lock is released, thread_started/thread_alive are free to change state asynchronously).

    gg

  8. #8
    Registered User
    Join Date
    Oct 2009
    Posts
    7
    Quote Originally Posted by Codeplug View Post
    You are leaking the thread handle. You have to do one of the following: 1) create the thread detached, 2) call pthread_join on the attached thread handle, 3) call pthread_detach on the attached thread handle.

    gg
    I will run a batch later today. Thanks for your input.

  9. #9
    Registered User
    Join Date
    Oct 2009
    Posts
    7
    Quote Originally Posted by m37h0d View Post
    i haven't seen any potential deadlock scenarios in your code, but this looks dangerous

    Code:
    void 	*ThreadAttempt::thread_init(void *arg){ ((ThreadAttempt *) arg)->thread_function(); }
    defininte segfault potential there.
    Can you explain? I am not sure, what you mean. Thanks

  10. #10
    Registered User
    Join Date
    Oct 2009
    Posts
    7
    Quote Originally Posted by m37h0d View Post
    i haven't seen any potential deadlock scenarios in your code, but this looks dangerous

    Code:
    void 	*ThreadAttempt::thread_init(void *arg){ ((ThreadAttempt *) arg)->thread_function(); }
    defininte segfault potential there.
    I use MMU less chips mostly. I don't have too much experience with threads. Usually, I make my own scheduler and know everything about it. In this case, I am in the dark. Thanks for input.

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> Can you explain?
    What you're doing is ok. In C++ you can convert an object pointer to a void* and then back again.

    gg

  12. #12
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    there's nothing inherently wrong with what you're doing.

    it's just that doing away with type safety by casting pointers can lead to segfaults if you make a mistake.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  2. Issue with pointers to an abstract class and inheritance
    By bainevii in forum C++ Programming
    Replies: 2
    Last Post: 03-31-2009, 07:51 AM
  3. C++ Newbie - Derived Class Issue
    By Gipionocheiyort in forum C++ Programming
    Replies: 8
    Last Post: 08-01-2007, 12:20 AM
  4. Replies: 3
    Last Post: 11-16-2006, 04:23 AM
  5. Pointer to class itself
    By Cdumbie in forum C++ Programming
    Replies: 3
    Last Post: 10-31-2002, 12:24 PM

Tags for this Thread