Q&A for Assignment #1

(Last updated Oct. 15, 2002)

The structure of assignment1 is as following:

 

1. Q: How to compile the programs that use Pthread?

A: After you finish editing a program and save it in a file, you have to compile it before you execute it. g++ is for this purpose.

To comiple a file, say "assignment.cc", type in the command line:

  g++ -g -Wall assignment.cc -o assignment -lpthread

where:

 

-g

Produce debugging information so that debugger (such as gdb or ddd) can work with this debugging information

-Wall

Issue warnings for conditions which pertain to usage that we recommend avoiding and that we believe is easy to avoid, even in conjunction with macros.

-o file

Place output in file

-lpthread

Inform the compiler to load pthread related libraries

 

For details about how to use g++, type in command line:

  man g++

To compile multiple files together, you may consider using makefile:
Some tutorials are available:

http://www.eng.hawaii.edu/Tutor/Make/
http://oucsace.cs.ohiou.edu/~bhumphre/makefile.html

2. Q: Where can I find resources about Pthread functions?

A: There are some books and web sites introducing Pthread functions, you can find them at Professor Fleisch’s web site, http://www.cs.ucr.edu/~brett/cs160.html.

A quite good web site is Posix Threads Programming, http://www.llnl.gov/computing/tutorials/workshops/workshop/pthreads/MAIN.html

3. Q: How to start this assignment?

A: The design. First think about the data structures, functions you want to implement. Please keep the design simple and correct. Simple design makes it easy to coding and maintenance. Correct design makes it work.

The following are some data structures you may consider to implement (not mandatory):

Database, Store, Thread Manager, Work Threads,……

4. Q: Do I have to consider the conflicts between two commands, say “add” and “delete”?

A: For assignment 1, you  don't have to worry too much about the synchronization problem because all the command will be input one by one. There actually have no synchronization in the system. However, you do need to make your code highly extendable to the following assignments, in which we will mainly focus on synchronization.  

5. Q: What are worker threads?

A: Worker threads need to carry out the operations against the database. They may include some specific characteristics, such as thread_ID, thread_function (the database operation, which will be carried out by the thread), etc.

6. The problem about "List" Command

When user types "list" in shell, your program should output all the worker threads running.
In Assignment #1, all the threads almost run in sequential, and they are running too fast. It’s too difficult for TA to find the running threads
 by typing “list” in command shell. (The result is usually 0 thread running) That's way we require you to keep the log file, which record each thread's 
creation, exit, and failure information. 
7. Should we use static function for those functions that will be spawned off? 
A: According to my test, you should use static function for those functions that will be called in pthread_create(). (If you have other solution that 
needn't using static function, please tell me and you will win an ice cream. (first come, first get : ) )
Function calls to static functions are made without referring to a particular object. Instead, the function is defined
by connecting it to the class name with the scope resolution operator.
For example, you may declare database class as this:
class Database
{
    private:
            List *List_for_products;
            char *Title;
    public:
            Database (char *Title);
            ~Database();
           static void* Load (void* Packed_Data);
           static void* Add (void* Packed_Data);
            ........
}
When you spawn threads in Command Shell or Thread Manager module, you can call like this:
pthread_t Thread1;
Database *pdb=new Database;
pthread_create(&Thread1, NULL, Database::Load, &ToPass);
//ToPass is the data you want to pass to Load()
Or:
pthread_create(&Thread1,NULL, pdb->Load, &ToPass);
The following are two sample codes for static functions. You may try them.
--------------------------------------------------------------------------------------
//Sample 1:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

class Hello{
    public:
      static void* print(void* msg);

};

void main()
{
    pthread_t thread1, thread2;
    Hello *H=new Hello;
    char *message1="Hello";
    char *message2="World";
    pthread_create(&thread1,NULL,Hello::print,(void *)message1);
    pthread_create(&thread2,NULL,Hello::print,(void *)message2);
    exit(0);

}

void * Hello::print(void* msg)
{
    char *message;
    message= (char *)msg;
    printf("%s ", message);
    return NULL;
}   
--------------------------------------------------------------------------------------
//Sample 2:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

class Hello{
    public:
      static void* print(void* msg);

};

void main()
{
    pthread_t thread1, thread2;
    Hello *H=new Hello;
    char *message1="Hello";
    char *message2="World";
    pthread_create(&thread1,NULL,H->print,(void *)message1);
    pthread_create(&thread2,NULL,H->print,(void *)message2);
    exit(0);

}

void * Hello::print(void* msg)
{
    char *message;
    message= (char *)msg;
    printf("%s ", message);
    return NULL;
}   
---------------------------------------------------------------------------------------
 
8. What is wrapper used for?
A: In pthread_create(), we can only pass one parameter to the routine inside pthread_create(). 
 If we want to pass more than one parameter, we should use wrapper to package the multiple 
parameter to a simple object. Then pass the pointer to that object to the routine we will call in 
pthread_create().
Secondly, in C++, the compiler will do the following:
 for example, you have a thread function which is a class member function.
class Auction 
{ 
     private: 
            char *Auction_Tile; 
            .. 
     public: 
            void* Mod(void *New_Title); 
}
actually, the compiler will change 
           void* Mod(void *New_Title)   to 
           void* Mod(Auction * this, void *New_Title)
 
Then, what is the problem? 
The problem is that it violates the parameter passing mechanism of the pthread_create()
function ! Here comes TWO parameters while Pthread only accepts one. The trick to solve
the problem is to use wrapper.
 
-------------------------------------------------------------------------------------
//Sample code
#include <pthread.h>
#include <stdio.h>

class ThreadClass;

struct wrapper
{
  ThreadClass* Me;
  int a;
  
};

class ThreadClass
{

  int a;
  
public:

  static void* thread_function(void* arg)
  {
    wrapper* Unpack = (wrapper*)arg;
    ThreadClass* HiddenThis = Unpack->Me;

    HiddenThis->a = Unpack->a;
    printf("I got: %d", HiddenThis->a);
    return NULL;
  }
  
};

int main()
{
  pthread_t Thread1;
  int one = 1;
  ThreadClass Test;
  wrapper ToPass;
  ToPass.Me = &Test;
  ToPass.a = one;

  pthread_create(&Thread1, NULL, Test.thread_function, &ToPass);

  pthread_join(Thread1, NULL);
  return 0;
}
-------------------------------------------------------------------------------------
 
 
9. Do I need to be concerned about the proper format of each user input?
 
A: Your program should not crash if the user input is incorrect. It's best if you can ignore user's wrong input and print a usage reminder to the user. 
 
 
10. How to use strtok_r() function? 
A: You can find the format of the function using "man strtok_r" on LINUX/UNIX.  The following is a example of strtok_r() 
 
#include <iostream.h>
#include <string.h>

int main()
{
    char message[]="I am testing strtok_r";
    char *s1=message;
    char *savept;
    for(;;)
    {
      char *result=strtok_r(s1," ",&savept);
      s1=savept;
      if(!result)
              break;
      cout<<"found a token:  "<<result<<endl;
      cout<<"s1 :  "<<s1<<endl;
    }
    return 0;
}
 
The following is the printout result:
 
$a.out 
found a token:  I
s1 : am testing strtok_r
found a token:  am
s1 : testing strtok_r
found a token:  testing
s1 : strtok_r
found a token:  strtok_r
s1 :
 
11. How to solve "Segmentation Fault" error?

segmentation fault 

An error in which a running program attempts to access memory not allocated to it and core dumps with a segmentation violation error. This is often caused by improper usage of pointers in the source code, dereferencing a null pointer, or (in C) inadvertently using a non-pointer variable as a pointer. The classic example is:
   int i;
   scanf ("%d", i);  /* should have used &i */

The general debugging method:

1. Check the pointers or addresses you used in the program

2. Comment some code which is suspected to cause the problem and recompile the program. Or think of working around to re-write part of the code.

3. Use I/O stream, i.e. stderr, printf to output some status values, which may help you understand what the program is really doing.

4. Use GDB to debug the program. 

The following are some tutorials of GDB:

GDB Tutorial 1

GDB Tutorial 2

GDB Tutorial 3

 

12. Can I thread a non static class member function? 

A: Yes. The main problem with threading class member functions is that the new thread has no information as to which instance of a class to which it belongs. If you were allowed to thread a non static function, if the thread tried accessing member objects the thread wouldn't know which instance of the class was being accessed. So to get around this, we thread a static function also passing the address of the respective class during its creation. This address turns out to be the "this" pointer of that class. If other values are required to be passed to the threaded function, a wrapper will be needed to encompass the class address pointer and the required value objects.

example 

class thread_mng 

{

         static void thread_wrapper_function( void *args ) 

        {           thread_mng *ptr; prt = ( thread_mng * ) args; 

                //now the threaded function will know which class instance 

               //he belongs too, allowing him to access member functions 

                     ptr->thread_fcuntion;

         }

void thread_function( ... ) 

        //some code which is threaded and 

       //can access member data 

void some_other_function( ... )

          //call pthread, passing the "this" poitner has the 

         //argument 

        pthread_t tid; 

       pthread_create( &tid, NULL , thread_mng::thread_wrapper_function, ( void * ) this );

 }

 };

 if you want to pass parameters to the thread function, you will need to create a wrapper object which encompasses those parameters and a void * for the "this" pointer.

say.. 

class wrapper { void *prt object parameter_1 ... object paramerer_n }; 

you'll have to set this wrapper up before threading the function. then you'll pass the address of a wrapper object to the static thread function. in the static function, use this wrapper to extract the class ptr value to launch the non static function. Not forgetting to also extract the wrapper objects to pass to your non static function. careful, because if you pass a address to a thread you'll need to create space within the new thread to hold this data. Otherwise each thread that is passed the address will be accessing the same data. So before a thread starts doing work on parameters passed within a wrapper, save them to local vars. Otherwise before the next thread is lauched, when the wrapper is taylored for the new thread, the data will be modified for all the threads pointing to this shared wrapper. Also you may need a LOCK to ensure the first thread copies the information out of the wrapper before a new thread modifies the data in the wrapper for a new thread.

Below is a sample code trying to spawn thread calling non-static member function:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

class Hello{
    public:
      void* print(void* msg);
      static void * test(void* arg); //the entry static function which calls non static member functions

};

struct pack_data{
        Hello * self;
      char * str_content;
};                                                 //the wrapper

void main()
{
    pthread_t thread1, thread2;
    Hello *H=(Hello *)malloc(sizeof(Hello));
    struct pack_data *pk1;
    struct pack_data *pk2;
    pk1=(pack_data *)malloc(sizeof(pack_data));
    pk2=(pack_data *)malloc(sizeof(pack_data));
    pk1->self=H;
    pk1->str_content=”Hello”;
    pk2->self=H;
    pk2->str_content=”World”;
    pthread_create(&thread1,NULL,H->test,(void *)pk1);
    pthread_create(&thread2,NULL,H->test,(void *)pk2);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    exit(0);

}

void * Hello::print(void* msg)              //non static function
{
    char *message;
    message= ((pack_data *)msg)->str_content;
    printf("%s ", message);
    return NULL;
}   

void * Hello::test(void *arg)              //static function
{
    Hello *ptr=(Hello *)malloc(sizeof(Hello));
    ptr= ((pack_data *)arg)->self;
    ptr->print(arg);                              //call a static function
    return NULL;
}