Reputation: 67
I am trying to get into the world of threads and am having some trouble. The code below works every once in a while but seems to be completely random. Giving it the same input keeps giving me different results and I am quite confused. Sometimes PrintHello() prints out the arguments and other times it prints garbage and sometimes it just segfaults.
#define NUM_THREADS 5
char *prompt = "% ";
struct thread_data{
int thread_id;
//int sum;
char *message;
};
void *PrintHello(void *threadarg)
{
struct thread_data *local_data;
local_data = (struct thread_data *) threadarg;
int taskid = local_data->thread_id;
const char *arguments = local_data->message;
fprintf(stderr, "Hello World! It's me, thread %s!\n", arguments);
pthread_exit(NULL);
}
PrintHello() is where I think the issue is.
int main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char *hp;
char *cp;
char *ofile;
int i;
int j, h, t, rc;
args = malloc(80 * sizeof(char *));
args2 = malloc(80 * sizeof(char *));
signal(SIGINT, SIG_IGN);
while (1) {
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (fgets(line, 80, stdin) == NULL)
break;
/* get rid of the '\n' from fgets */
if (line[strlen(line) - 1] == '\n'){
line[strlen(line) - 1] = '\0';
}
// split up the line
i = 0;
while (1) {
token = strtok((i == 0) ? line : NULL, separator);
if (token == NULL)
break;
args[i++] = token;
}
args[i] = NULL;
ofile = args[i-1];
printf("%s\n", ofile);
The stuff above this is just tokenizing the input and works fine.
struct thread_data thread_data_array[i];
pthread_t threads[i];
for(t=0; t<i; t++){
thread_data_array[t].thread_id = t;
thread_data_array[t].message = args[t];
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&thread_data_array[t]);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
}
}
Upvotes: 1
Views: 50
Reputation: 19331
There are several issues with your code, but I'll focus on the key ones that affect stability.
malloc()
. If the return value is NULL, it means the operation failed, and you have to either re-try, or start cleaning up all dynamically allocated memory from malloc()
, calloc()
, strdup()
, etc, and finish up with your program gracefully. Attempting to dereference NULL (ie: use the pointer from the failed memory allocation call) will crash your program.'\0'
, and continue.while
loop immediately executes again, and re-assigns new values to the thread[]
array. Also, the same argument is passed to them while still in use (thread_data_array[t]
), and you have no mutexes to protect them. Also, if your program's main()
exits early, then all running threads get killed off immediately, and don't get to finish.pthread_join()
on joinable threads to ensure you wait until they complete before you proceed.gcc test.c -lpthread -O0 -ggdb
, then stepped through the code via the "next" n
command after starting it in gdb with run
. It makes your life a lot easier.#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#define NUM_THREADS 5
#define BUF_LEN (80)
char *prompt = "% ";
struct thread_data{
int thread_id;
//int sum;
char *message;
};
void *PrintHello(void *threadarg)
{
struct thread_data *local_data;
local_data = (struct thread_data *) threadarg;
int taskid = local_data->thread_id;
const char *arguments = local_data->message;
fprintf(stderr, "Hello World! It's me, thread %s!\n", arguments);
pthread_exit(NULL);
}
int main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char *hp;
char *cp;
char *ofile;
int i;
int j, h, t, rc;
args = malloc(BUF_LEN * sizeof(char *)); // ISSUE: Can fail. Check return value.
args2 = malloc(BUF_LEN * sizeof(char *)); // ISSUE: Can fail.
signal(SIGINT, SIG_IGN);
while (1)
{
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (fgets(line, BUF_LEN, stdin) == NULL)
{
break;
}
/* get rid of the '\n' from fgets */
/*
if (line[strlen(line) - 1] == '\n'){
line[strlen(line) - 1] = '\0';
}
*/
for ( t = 0; t < BUF_LEN; t++ )
{
if ( line[t] == '\n' )
{
line[t] = '\0';
}
}
// split up the line
i = 0;
int numTokens = 0;
while (1) {
token = strtok((i == 0) ? line : NULL, separator);
if (token == NULL)
{
break;
}
args[i++] = token;
numTokens++;
}
// Abort if zero tokens found.
if ( numTokens == 0 )
{
continue;
}
// Exit if input is "quit"
if ( strcasecmp(line, "quit") == 0 )
{
break;
}
args[i] = NULL;
ofile = args[i-1];
printf("%s\n", ofile);
struct thread_data thread_data_array[i];
pthread_t threads[i];
for(t=0; t<i; t++)
{
thread_data_array[t].thread_id = t;
thread_data_array[t].message = args[t];
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&thread_data_array[t]);
if (rc)
{
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
// Wait for threads to complete work.
for(t=0; t<i; t++)
{
pthread_join(threads[t], NULL);
}
}
}
%
%
%
% Hello world. This is a test
test
Hello World! It's me, thread test!
Hello World! It's me, thread a!
Hello World! It's me, thread is!
Hello World! It's me, thread This!
Hello World! It's me, thread world.!
Hello World! It's me, thread Hello!
%
% 1 2 3
3
Hello World! It's me, thread 3!
Hello World! It's me, thread 2!
Hello World! It's me, thread 1!
% QUit
Upvotes: 1