Various C++ Examples (including IPC)  Version: 1.0.0
threadDeath3.cc
Go to the documentation of this file.
1 
25 #include <iostream>
26 #include <stack>
27 #include <map>
28 #include <algorithm>
29 #include <cstring>
30 #include <pthread.h>
31 
60 class ThreadMgr {
61 private:
64  {
66  void *(*func)(void *);
67 
69  void (*cancel_func)(void *);
70 
72  void *arg;
73 
76  };
77 
78 public:
81  {
86  //the data mutex
87  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
88  m_mutex = &mutex;
89 
90  //the condition variable mutex
91  static pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER;
92  m_cond_mutex = &cond_mutex;
93 
94  //the condition variable
95  static pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
96  m_cond_var = &cond_var;
97  }
98 
100  int cancel_thread(pthread_t *tid)
101  {
126  //cancel a thread
127  int ret = 0;
128  pthread_mutex_lock(m_mutex); //lock the data mutex
129  m_ids.erase(*tid); //remove from id map
130 
131  //cancel the thread
132  ret = pthread_cancel(*tid);
133 
134  pthread_mutex_unlock(m_mutex); // unlock data mutex
135 
136  return ret;
137  }
138 
140  int condWait(void **thread_return_val)
141  {
156  int ret = 0;
157 
158  //condition variable mutex lock
159  pthread_mutex_lock(m_cond_mutex);
160 
161  //check predicate
162  while(no_threads_terminated())
163  {
164  //wait on condition variable
165  pthread_cond_wait(m_cond_var, m_cond_mutex);
166  }
167 
168 
169  //remove the thread from the terminated list (handle join)
170  ret = removeTerminated(thread_return_val);
171 
172  //unlock
173  pthread_mutex_unlock(m_cond_mutex);
174 
175  //return join status
176  return ret;
177  }
178 
180  //int createThread( void *(*thread_func)(void *), void *arg)
181  pthread_t createThread( void *(*thread_func)(void *), void *arg)
182  {
203  pthread_t tid = 0; // Id of thread
204  int ret_val; // return value
205 
206  //arguments for the function
207  struct func_arguments *arguments = new func_arguments;
208 
209  arguments->func = thread_func; // users function
210  arguments->cancel_func = NULL; // NOT IMPLIMENTED
211  arguments->arg = arg; // users argument
212 
213  //this is the this pointer (so far, every thread get's one -eek!)
214  arguments->thisObject = this;
215 
216  //create a new thread
217  /*
218  default attributes (for now),
219  internal function, func(), calls users function,
220  arguments contain other info + user's argument.
221  */
222  ret_val = pthread_create(&tid, (pthread_attr_t *) NULL, func, (void *)arguments);
223 
224  if(ret_val == 0)
225  {
226  //register the thread and arguments in the ids map
227  addID(tid, arguments);
228 
229  //return thread id
230  return tid;
231  }
232  else
233  std::cout << "pthread_create FAIL" << std::endl;
234 
235 
236  //return 0 on error
237  return 0;
238  }
239 
242  {
243  pthread_mutex_lock(m_mutex);
244  int ret = m_ids.size();
245  pthread_mutex_unlock(m_mutex);
246 
247  return ret;
248  }
249 
252  {
255  pthread_mutex_lock(m_mutex);
256  bool ret = m_terminated.empty();
257  pthread_mutex_unlock(m_mutex);
258  return ret;
259  }
260 
261 
262 
263 protected:
265  static void *func(void *arg)
266  {
280  //convenience this object
281  ThreadMgr *thisObject = ((struct func_arguments *)arg)->thisObject;
282 
283  //return argument from user function
284  void *tmpArg = NULL;
285 
286  //basic cancel stuff -should be more robust
287  struct func_arguments *cancel_arg = new func_arguments;
288  cancel_arg->cancel_func = shutdown_thread;
289  cancel_arg->func = NULL;
290  cancel_arg->arg = NULL;
291  cancel_arg->thisObject = thisObject;
292 
293  //set the cleanup function
294  pthread_cleanup_push(shutdown_thread, (void *)cancel_arg);
295 
296  //call the user's function
297  tmpArg = ((struct func_arguments *)arg)->func( ((struct func_arguments *)arg)->arg );
298  //std::cout << "func() passing \"" << *(std::string *)tmpArg << "\" to pthread_exit()" << std::endl;
299 
300  //delete (struct func_arguments *)arg;
301 
302  //pop the cleanup handler off the cleanup stack
303  //delete the arg variable list from the createThread function.
304  pthread_cleanup_pop(1);
305 
306  //add this thread to the terminated list
307  thisObject->addTerminated(thisObject);
308 
309  //exit this thread
310  pthread_exit(tmpArg);
311 
312  //we will never get here
313  return NULL;
314  }
315 
317  static void *addTerminated(ThreadMgr *arg)
318  {
319  //pthread_mutex_lock(((ThreadMgr *)arg)->m_mutex);
320  pthread_mutex_lock(arg->m_mutex);
321 
322  //add self to list of stuff to be terminated (joined)
323  arg->m_terminated.push(pthread_self());
324 
325  //broadcast to threads waiting on the condition variable to notify
326  //them to wake up
327  pthread_cond_broadcast(arg->m_cond_var);
328 
329  pthread_mutex_unlock(arg->m_mutex);
330 
331  //return NULL -blah
332  return NULL;
333  }
334 
336  int removeTerminated(void **return_val)
337  {
345  int ret = 0;
346  pthread_t tempID;
347 
348  //lock critical section
349  pthread_mutex_lock(m_mutex);
350 
351  //make sure we have something
352  if(!m_terminated.empty())
353  {
354  //get id from stack
355  tempID = m_terminated.top();
356 
362  //join with the terminated thread
363  if( (ret = pthread_join(tempID, return_val)) == 0)
364  {
365  //delete the arguments (created in createThread)
366  //delete m_ids.find(tempID)->second;
367 
368  //get rid of the ID from active list
369  m_ids.erase(tempID);
370 
371  //remove id from stack
372  m_terminated.pop();
373  }
374  else
375  std::cout << "pthread_join() = " << ret << std::endl;
376  }
377 
378  //unlock critical section
379  pthread_mutex_unlock(m_mutex);
380 
381  //return value of pthread_join
382  return ret;
383  }
384 
386  void addID(pthread_t id, func_arguments *arg)
387  {
388  //lock the data mutex
389  pthread_mutex_lock(m_mutex);
390 
391  //add the thread to the std::map
392  m_ids.insert(std::make_pair(id, arg));
393 
394  //unlock the data mutex
395  pthread_mutex_unlock(m_mutex);
396  }
397 
399  static void shutdown_thread(void *arg)
400  {
403  delete ((struct func_arguments *)arg);
404  return;
405  }
406 
407 
408 private:
410  pthread_mutex_t *m_mutex;
411 
413  pthread_mutex_t *m_cond_mutex;
414 
416  pthread_cond_t *m_cond_var;
417 
419  std::map<pthread_t, ThreadMgr::func_arguments *> m_ids;
420 
422  std::stack<pthread_t> m_terminated;
423 };
424 
425 
426 //################## PROTOTYPES
428 void waitStringThreads(ThreadMgr *mgr, void **return_value);
429 
431 void *myfunc0(void *arg);
432 
434 void *myfunc1(void *arg);
435 
437 void *myfunc2(void *arg);
438 
440 void *myStringFunc(void *arg);
441 
443 template <class T>
444 void TwaitStringThreads(ThreadMgr *mgr, T return_value);
445 
446 //################## MAIN
448 int main(int argc, char *argv[])
449 {
450  void **return_val;
451  void **ret;
452 
453  //string literal for later use
454  const char *pc = "987654321";
455 
456  //string object of later use (example 2)
457  std::string *str = new std::string("a string from main");
458 
459  //some thread IDs to track
460  pthread_t pret, pret2;
461 
462  //common variable
463  int i = 0;
464 
465  //the thread manager class
466  ThreadMgr m;
467 
468 
469  //##########################################################
470  std::cout << "Example 1:" << std::endl;
471  //##########################################################
472 
482  //test the functionality
483  pret = m.createThread((void *(*)(void *))myfunc0, NULL);
484  pret = m.createThread((void *(*)(void *))myfunc2, (void *)pc);
485  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
486  pret2 = m.createThread((void *(*)(void *))myfunc2, (void *)pc);
487  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
488  pret2 = m.createThread((void *(*)(void *))myfunc2, (void *)pc);
489  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
490  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
491 
492  while(m.threadsActive())
493  {
494  /*do, while there are active threads or there are threads being
495  terminated.
496  */
497 
498  //on the third itteration...
499  if(i++ == -1)
500  {
501  //cancel a thread (may lose data this way though!)
502  std::cout << "CANCELING THREAD:" << pret2 << std::endl;
503  m.cancel_thread(&pret2);
504 
505  //create another thread for fun and profit
506  pret = m.createThread((void *(*)(void *))myfunc2, (void *)pc);
507  }
508  else
509  {
510  //wait on the thread
511  m.condWait(return_val);
512  if(*return_val != NULL)
513  {
514  std::cout << "#######################main:" << ((char *)(char *)*return_val) << std::endl;
515 
516  /* This cast is not entirely proper. See the next
517  example for proper casting when deleting this pointer.
518  */
519  delete (char *)*return_val;
520  }
521  }
522  }
523 
524 
525  //##########################################################
526  std::cout << "\n" << "Example 2:" << std::endl;
527  //##########################################################
528 
534  //test the functionality
535  pret = m.createThread((void *(*)(void *))myfunc0, NULL);
536  pret = m.createThread((void *(*)(void *))myfunc2, (void *)pc);
537  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
538  pret2 = m.createThread((void *(*)(void *))myfunc2, (void *)pc);
539  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
540  pret2 = m.createThread((void *(*)(void *))myfunc2, (void *)pc);
541  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
542  pret = m.createThread((void *(*)(void *))myfunc1, NULL);
543 
544  while(m.threadsActive())
545  {
546  /*do, while there are active threads or there are threads being
547  terminated.
548  */
549 
550  //wait on the thread
551  m.condWait(return_val);
552 
553  /* check return_val: meanwhile other threads may be dieing
554  */
555  if(*return_val != NULL)
556  {
557  /* We should get here 3 times from our usage of myfunc2
558  above */
559  std::cout << "#######################main:" << ((char *)(char *)*return_val) << std::endl;
560 
561  //delete the pointer since it was valid (not NULL)
562  delete ((char *)(char *)*return_val);
563  }
564  }
565 
566 
567  //##########################################################
568  std::cout << "\n" << "Example 3:" << std::endl;
569  //##########################################################
570 
587  //void **ret;
588 
589  //try to pass an object and get one back
590  m.createThread(myStringFunc, (void *)str);
591 
592  //while(m.threadsActive() || !m.no_threads_terminated())
593  while(m.threadsActive())
594  {
595  //wait on the thread
596  m.condWait(ret);
597  if(*ret != NULL)
598  {
599  //cast the return_val -contents of a std::string pointer
600  std::cout << *((std::string *)(std::string *)*ret) << std::endl;
601 
602  //delete the memory at the pointer
603  delete ((std::string *)(std::string *)*ret);
604  }
605 
606  }//end while()
607 
608  std::cout << "threads Active:" << m.threadsActive() << std::endl;
609  std::cout << "no_threads_terminated:" << m.no_threads_terminated() << std::endl;
610 
611  //cleanup
612  delete str;
613 
614  //##########################################################
615  std::cout << "\n" << "Example 4:" << std::endl;
616  //##########################################################
617 
626  //let's try another instance in tandum
627  ThreadMgr m1;
628 
629  //create a couple of strings
630  std::string *str1 = new std::string("string1 from main");
631  std::string *str2 = new std::string("string2 from main");
632 
633  //create a thread under m1 instance of ThreadMgr
634  m1.createThread(myStringFunc, (void *)str1);
635 
636  //create a thread under m instance of ThreadMgr
637  m.createThread(myStringFunc, (void *)str2);
638 
639 
640  //manage threads from m instance of ThreadMgr
641  waitStringThreads(&m, ret);
642 
643  //manage threads from m1 instance of ThreadMgr
644  TwaitStringThreads(&m1, (std::string **)ret);
645 
646 
647  //cleanup
648  delete str1;
649  delete str2;
650 
651  //exit normally
652  return(0);
653 }
654 
656 void waitStringThreads(ThreadMgr *mgr, void **return_value)
657 {
686  //void **return_value; // doesn't work !!!!
687 
688  while(mgr->threadsActive())
689  {
690  //wait on the thread
691  mgr->condWait((void **)return_value);
692 
693  if(*return_value != NULL)
694  {
695  //cast the return_val -contents of a std::string pointer
696  std::cout << "waitStringThreads:" << *((std::string *)(std::string *)*return_value) << std::endl;
697 
698  //delete the memory at the pointer
699  delete ((std::string *)(std::string *)*return_value);
700  }
701  }//end while()
702 }
703 
722 template <class T>
723 void TwaitStringThreads(ThreadMgr *mgr, T return_value)
724 {
725  while(mgr->threadsActive())
726  {
727  //wait on the thread
728  mgr->condWait((void **)return_value);
729 
730  if(*return_value != NULL)
731  {
732  //cast the return_val -contents of a T pointer
733 
734  /* if we pass in a string ** then this translates to:
735  **(string **)return_value for the call to cout. remember, if
736  return_value is a pointer to an object then we want the
737  contents of the pointed to item of the pointer (it's a pointer
738  to a pointer to a pointer to something)
739  */
740  std::cout << "TwaitStringThreads:" << **((T)return_value) << std::endl;
741 
742  //delete the memory at the pointer
743  delete *return_value;
744  }
745  }//end while()
746 }
747 
758 void *myStringFunc(void *arg)
759 {
760  //copy the string using casts
761  //std::string *a = ((std::string *)arg);
762  //std::cout << "myStringFunc printing:" << *a << std::endl;
763 
764  //print the contents of arg as an std::string pointer
765  std::cout << "myStringFunc printing:" << *(std::string *)arg << std::endl;
766 
767  //create a new string to be returned to the calling thread
768  std::string *s = new std::string("a string from myStringFunc");
769  std::cout << "myStringFunc returning:" << *s << std::endl;
770 
771  //cast the object as a void pointer and return
772  return ((void *)s);
773 
774  //return ((void *)NULL);
775 }
776 
781 void *myfunc0(void *arg)
782 {
783  std::cout << "got here: myfunc0:BEGIN" << std::endl;
784 
785  for(int i=10000; i > 0; i--)
786  for(int i=10000; i > 0; i--);
787 
788  std::cout << "got here: myfunc0:END" << std::endl;
789 
790  return NULL;
791 }
792 
798 void *myfunc1(void *arg)
799 {
800  std::cout << "got here: myfunc1:BEGIN" << std::endl;
801 
802  for(int i=1000; i > 0; i--)
803  for(int i=10000; i > 0; i--);
804 
805  std::cout << "got here: myfunc1:END" << std::endl;
806 
807  return NULL;
808 }
809 
819 void *myfunc2(void *arg)
820 {
821  std::cout << "got here: myfunc2:BEGIN" << std::endl;
822 
823  //print the value of arg from main
824  std::cout << "|arg = " << (char *)arg << std::endl;
825 
826  //create a new char and add data to the memory area
827  char *tmp = new char[10];
828  char x[10] = {"123456789"};
829 
830  //cheezy, but whatever...
831  memcpy(tmp, x, 10);
832 
833  std::cout << "myfunc2:" << tmp << std::endl;
834 
835  for(int i=10000; i > 0; i--)
836  for(int i=10000; i > 0; i--);
837 
838  std::cout << "got here: myfunc2:END" << std::endl;
839 
844  return tmp;
845 }
void * arg
user arguments for user function
Definition: threadDeath3.cc:72
ThreadMgr * thisObject
the this object -one per thread...EEEEK!
Definition: threadDeath3.cc:75
static void * func(void *arg)
internal thread function
void * myfunc2(void *arg)
Example Thread function.
void * myfunc0(void *arg)
Example Thread function.
void addID(pthread_t id, func_arguments *arg)
add a thread id to the id vector
void * myStringFunc(void *arg)
Example Thread function returning an object.
int removeTerminated(void **return_val)
remove a terminated thread LIFO
int cancel_thread(pthread_t *tid)
cancel a thread
pthread_cond_t * m_cond_var
condition variable
void TwaitStringThreads(ThreadMgr *mgr, T return_value)
Example template -waits for thread and prints object.
int threadsActive()
return the number of active threads
static void * addTerminated(ThreadMgr *arg)
add a thread id to the m_terminated stack
std::map< pthread_t, ThreadMgr::func_arguments * > m_ids
map of all thread ids and function attributes
static void shutdown_thread(void *arg)
shutdown a thread from pthread_cleanup_pop().
pthread_t createThread(void *(*thread_func)(void *), void *arg)
attempt to create a new thread and register it
pthread_mutex_t * m_mutex
mutex for data access (m_ids, m_terminated)
A basic thread management class.
Definition: threadDeath3.cc:60
bool no_threads_terminated()
answers the question "are there no theads terminated?"
void * myfunc1(void *arg)
Example Thread function.
void waitStringThreads(ThreadMgr *mgr, void **return_value)
generic wait for string-centric threads
int main(int argc, char *argv[])
the main function
void *(* func)(void *)
pointer to user defined function
Definition: threadDeath3.cc:66
structure for static wrapper function arguments
Definition: threadDeath3.cc:63
ThreadMgr()
constructor
Definition: threadDeath3.cc:80
pthread_mutex_t * m_cond_mutex
mutex for condition variable
int condWait(void **thread_return_val)
wait on a condition variable for a thread to terminate
std::stack< pthread_t > m_terminated
stack of terminated thread ids
void(* cancel_func)(void *)
function to call when we are canceled
Definition: threadDeath3.cc:69