I have written a code for real-time logging. Here's the pseudo-code:
initialize Q; //buffer structure stores values to be printed
log(input)
{
push input to Q;
}
printLog() //infinte loop
{
loop(1)
{
if(Q is not empty)
{
values = pop(Q);
msg = string(values); //formating values into a message string
print(msg);
}
}
}
mainFunction()
{
loop(1)
{
/*
insert operations to be performed
*/
log(values); //log function called
}
}
main()
{
Create 4 threads; //1 mainFunction and 3 printLog
Bind them to CPUs;
}
I'm using atomic operations instead of locks.
When I print the output to the console, I see that each thread prints consecutively for a while. This must mean that once a thread enters printLog(), the other threads are inactive for a while.
What I want instead is while one thread is printing, another thread formats the next value popped from Q and prints it right after. How can this be achieved?
EDIT: I've realized the above information isn't sufficient. Here are some other details.
Buffer structure Q is a circular array of fixed size.
Pushing information to Q is faster than popping+printing. So by the time the Buffer structure is full, I want most of the information to be printed.
NOTE: mainFunction thread shouldn't wait to fill Buffer when it is full.
I'm trying to utilize all the threads at a given time. Currently, after one thread prints, the same thread reads and prints the next value (this means the other 2 threads are inactive).
Here's the actual code:
//use gcc main.c -o run -pthread
#define _GNU_SOURCE
#include <unistd.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#define N 3
/* Buffer size */
#define BUFFER_SIZE 1000
struct values
{
uint64_t num;
char msg[20];
};
struct values Q[BUFFER_SIZE];
int readID = -1;
int writeID = -1;
int currCount = 0;
void Log(uint64_t n, char* m)
{
int i;
if (__sync_fetch_and_add(&currCount,1) < BUFFER_SIZE)
{
i = __sync_fetch_and_add(&writeID,1);
i = i%BUFFER_SIZE;
Q[i].num = n;
strcpy(Q[i].msg, m);
}
else __sync_fetch_and_add(&currCount,-1);
}
void *printLog(void *x)
{
int thID = *((int*)(x));
int i;
while(1)
{
if(__sync_fetch_and_add(&currCount,-1)>=0)
{
i = __sync_fetch_and_add(&readID,1);
i = i%BUFFER_SIZE;
printf("ThreadID: %2d, count: %10d, message: %15s\n",thID,Q[i].num,Q[i].msg);
}
else __sync_fetch_and_add(&currCount,1);
}
}
void *mainFunction()
{
uint64_t i = 0;
while(1)
{
Log(i,"Custom Message");
i++;
usleep(50);
}
}
int main()
{
/* Set main() Thread CPU */
cpu_set_t cpusetMain;
CPU_ZERO(&cpusetMain);
CPU_SET(0, &cpusetMain);
if(0 != pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpusetMain))
printf("pthread_setaffinity_np failed for CPU: 0\n");
int LogThID[N+1];
pthread_t LogThreads[N+1];
/* Create Threads */
if (pthread_create(&LogThreads[0], NULL, &mainFunction, NULL) != 0){return 0;}
for(int i=1; i<N+1 ; i++)
{
LogThID[i] = i;
if (pthread_create(&LogThreads[i], NULL, &printLog, &LogThID[i]) != 0){return i;}
}
/* Set CPUs */
cpu_set_t cpuset[N+1];
for(int i=0; i<N+1; i++)
{
CPU_ZERO(&cpuset[i]);
CPU_SET(i+1, &cpuset[i]);
if(0 != pthread_setaffinity_np(LogThreads[i], sizeof(cpu_set_t), &cpuset[i]))
printf("pthread_setaffinity_np failed for CPU: %d\n", i+1);
}
struct sched_param param[N+1];
for(int i=0; i<N+1; i++)
{
param[i].sched_priority = 91;
if(0 != pthread_setschedparam(LogThreads[i],SCHED_FIFO,¶m[i]))
printf("pthread_setschedparam failed for CPU: %d\n", i);
}
/* Join threads */
for(int i=0; i<N+1; i++)
{
pthread_join(LogThreads[i], NULL);
}
return 0;
}
Related
I've been trying to implement thread synchronization on C. However, I keep getting the segmentation fault when my invoke the function that I want the thread to execute. So anyone can suggest the solution on for this problem?
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#define N 5
#define M 3
#define LEFT (robot_id - 1) % N
#define RIGHT (robot_id + 1) % N
pthread_t robots_id[N];
sem_t simulations[M];
pthread_mutex_t sever_mutex;
void Learning(int robot_id)
{
printf("learning robot = %d\n", robot_id);
}
void *robotAct(void *id)
{
int *robot_id = id;
printf("robot id = %d\n", robot_id);
Learning(*robot_id);
}
int main(int argc, char *argv[])
{
int E, T;
E = atoi(argv[1]);
T = atoi(argv[2]);
printf("Initializing Robot!\n");
//Initializes the simulations
for (int i = 0; i < M; i++)
{
sem_init(&simulations[i], 0, 0);
}
//Initializes the robots
for (int i = 0; i < N; i++)
{
printf("Robot %d is created\n", i + 1);
pthread_create(&robots_id[i], NULL, robotAct, (void *)i + 1);
}
sleep(T);
printf("Terminating Robots\n");
for (int i = 0; i < N; i++)
{
pthread_cancel(robots_id[i]);
}
printf("Termination is completed!\n");
printf("-------Report-------------\n");
//getReport();
return 0;
}
Here is my result that I keep getting
Initializing Robot!
Robot 1 is created
Robot 2 is created
Robot 3 is created
robot id = 1
robot id = 2
Robot 4 is created
robot id = 3
[1] 54477 segmentation fault ./project 5 10
The main issue is explained in my comment:
You're not passing a valid pointer to the thread function. You sort of, mostly, almost get away with the misuse of it in the printf() call in robotAct(); you emphatically do not get away with it in the call to Learning() where you dereference the invalid non-pointer.
A solution is to create an array of integers in the main program which holds robot ID numbers (int id[N];). Then, initialize each element and pass &id[i] to pthread_create().
You should not print addresses with the %d format (even though it works on 32-bit systems; it does not work on 64-bit systems). The correct technique is to use %p to format the address. Or, in this case, print the integer and not the address using *robot_id.
The code that follows has minimal adaptations to the original code and has not been compiled or tested (there could be problems outside the lines changed):
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#define N 5
#define M 3
#define LEFT (robot_id - 1) % N
#define RIGHT (robot_id + 1) % N
pthread_t robots_id[N];
sem_t simulations[M];
pthread_mutex_t sever_mutex;
void Learning(int robot_id)
{
printf("learning robot = %d\n", robot_id);
}
void *robotAct(void *id)
{
int *robot_id = id;
printf("robot id = %d\n", *robot_id); // Changed
Learning(*robot_id);
return 0; // Added
}
int main(int argc, char *argv[])
{
int E, T;
int id[N]; // Added
E = atoi(argv[1]);
T = atoi(argv[2]);
printf("Initializing Robot!\n");
//Initializes the simulations
for (int i = 0; i < M; i++)
{
sem_init(&simulations[i], 0, 0);
}
//Initializes the robots
for (int i = 0; i < N; i++)
{
printf("Robot %d is created\n", i + 1);
id[i] = i + 1; // Added
pthread_create(&robots_id[i], NULL, robotAct, &id[i]); // Changed
}
sleep(T);
printf("Terminating Robots\n");
for (int i = 0; i < N; i++)
{
pthread_cancel(robots_id[i]);
}
printf("Termination is completed!\n");
printf("-------Report-------------\n");
//getReport();
return 0;
}
Avoid using pthread_cancel() for ending the threads; the threads should terminate under control. For example, there might be a flag that you set in the main thread to indicate that the threads should cease, and they'd check that periodically. Normally, pthread_join() is used to clean up the completed threads.
For future posts, please read about how to create an MCVE (Minimal, Complete, Verifiable Example). There are parts of the code shown that are not relevant to the problem — the mutex and the semaphores, for example, are not really used.
For some reason I am getting a segmentation fault before any of my code is actually executed in the main() function. I have tried following the line of execution by putting in printfs but nothing is actually executed. I don't see anything in my program that would be causing a stack overflow, as I hardly even use memory.
If someone has better eyes than me and can spot this error it would be very much appreciated!
Main:
#include "../inc/protos.h"
HistogramData *histogram_data;
bool signal_caught = false;
sem_t *semaphore_id;
int letter_count[kLetterCount] = { 0 };
int wait_time = 0;
int main(void)
{
int shared_memory_id = 0;
key_t shared_memory_key = 0;
char buffer[kBufferLength] = { 0 };
int heads = 0;
int tails = 0;
printf("1");
histogram_data->signal_caught = false;
signal(SIGINT, signal_handler);
printf("2");
//Get the key to the allocated shared memory
shared_memory_key = ftok("/tmp", 'M');
if(shared_memory_key == -1)
{
printf("(CONSUMER) Cannot allocate key.\n");
return 1;
}
printf("3");
//Look for shared memory every 10 seconds until it finds it
while(true)
{
if((shared_memory_id = shmget(shared_memory_key, sizeof(histogram_data), 0)) == -1)
{
printf("4");
printf("(CONSUMER) Shared Memory does not exist. Please run the Producer program.\n");
sleep(kSleepTime);
}
else
{
printf("5");
break;
}
}
printf("(CONSUMER) Our Shared Memory ID is %d.\n", shared_memory_id);
//Attach the structure to the shared memory
histogram_data = (HistogramData*) shmat(shared_memory_id, NULL, 0);
if(histogram_data == NULL)
{
printf("(CONSUMER) Cannot attach to Shared Memory.\n");
return 3;
}
semaphore_id = sem_open("/HISTOGRAM_SEM", O_CREAT, S_IRUSR | S_IWUSR, 1);
signal(SIGALRM, alarm_handler);
//Set the watchdog timer to 2 seconds.
alarm(kAlarmSeconds);
//Detach from shared memory
shmdt(histogram_data);
return 0;
}
void signal_handler(int signal_number)
{
printf ("(CONSUMER) Received a signal. SIGINT ID is %d\n", signal_number);
histogram_data->signal_caught = true;
// Send SIGINT to Producer2
kill(histogram_data->producer2_pid, SIGINT);
// Send SIGINT to Producer1
kill(histogram_data->producer1_pid, SIGINT);
}
void print_line(int num)
{
int hundreds = num / 100;
num = num % 100;
int tens = num / 10;
num = num % 10;
int ones = num;
int i = 0;
for(i = 0; i < hundreds; i++)
{
printf("*");
}
for(i = 0; i < tens; i++)
{
printf("+");
}
for(i = 0; i < ones; i++)
{
printf("-");
}
printf("\n");
}
void display_histogram(int letter_count[])
{
int i = 0;
printf("\n********** HISTOGRAM **********\n");
for(i = 0; i < kLetterCount; i++)
{
printf("%c-%03d ", i + 65, letter_count[i]);
print_line(letter_count[i]);
}
}
void alarm_handler(int signal_number)
{
int wait_time = 0;
sem_wait(semaphore_id);
int i = 0;
for(i = 0; i < kDCReads; i++)
{
int* read_index = &histogram_data->read_index;
if(histogram_data->circular_buffer[*read_index] != 0)
{
int read_data = histogram_data->circular_buffer[*read_index];
histogram_data->circular_buffer[*read_index] = 0;
++letter_count[read_data - 65];
if(*read_index == kCircleBufferSize)
{
*read_index = 0;
}
if(*read_index == histogram_data->write_index)
{
break;
}
}
}
if(signal_caught == true)
{
//Read and write indexes from the histogram data structure
int* read_index = &histogram_data->read_index;
int* write_index = &histogram_data->write_index;
//Read data from buffer
while(*read_index != *write_index)
{
if(histogram_data->circular_buffer[*read_index])
{
//Data read in from the circular buffer
int read_data = histogram_data->circular_buffer[*read_index];
//Mark element as read
histogram_data->circular_buffer[*read_index] = 0;
++letter_count[read_data - 65];
//Increment the elements
(*read_index)++;
if(*read_index == 256)
{
*read_index = 0;
}
if(*read_index == *write_index)
{
break;
}
}
}
//Display a histogram listing
display_histogram(letter_count);
return;
}
wait_time++;
if(wait_time >= 5)
{
wait_time = 0;
display_histogram(letter_count);
}
//Release semaphore lock
sem_post(semaphore_id);
//Set the alarm for the watchdog to be two seconds
alarm(kAlarmSeconds);
//Reactivate watchdog signal
signal(signal_number, alarm_handler);
}
protos.h:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <signal.h>
#include <semaphore.h>
#define kCircleBufferSize 256
#define kBufferLength 126
#define kLetterCount 20
#define kDCReads 60
#define kAlarmSeconds 2
#define kSleepTime 10
typedef struct HistogramData HistogramData;
struct HistogramData
{
int read_index;
int write_index;
int is_wrap_around;
pid_t producer1_pid;
pid_t producer2_pid;
char circular_buffer[kCircleBufferSize];
bool signal_caught;
};
void signal_handler(int signal_number);
void print_line(int num);
void display_histogram(int letter_count[]);
void alarm_handler(int signal_number);
For some reason I am getting a segmentation fault before any of my code is actually executed in the main() function.
One of your preloaded data structures is likely to be causing overflow in the stack. You also have a lot of buffering going on to the output and, additionally, you have several places where you use printf() but do not append the newline \nto flush the console buffer. Alternatively, you can follow #sabbahillel's comment by putting fflush() after your printf() statements.
You create histogram_data as a pointer to HistogramData, but don't create a HistogramData object. Then, when you call histogram_data->signal_caught = false in main, you program dereferences a NULL pointer.
Instead, allocate memory for HistogramData before using the pointer (for example, histogram_data = malloc(sizeof *histogram_data);). Don't forget to free it later, too.
I wanted to use libgps to interface with gpsd daemon. That's why I've implemented a little testing application in order to extract a value from a specific satellite.
The documentation on its HOWTO page tells us that
The tricky part is interpreting what you get from the blocking read.
The reason it’s tricky is that you’re not guaranteed that every read
will pick up exactly one complete JSON object from the daemon. It may
grab one response object, or more than one, or part of one, or one or
more followed by a fragment.
As recommended the documentation, the PACKET_SET mask bit is checked before doing anything else.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <gps.h>
#include <pthread.h>
pthread_t t_thread;
struct t_args {
unsigned int ID;
};
unsigned int status = 0;
int elevation;
int p_nmea(void *targs);
void start_test(void)
{
struct t_args *args = malloc(sizeof *args);
status = 1;
args->ID = 10;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0)
{
perror("create: \n");
}
}
int test_result(int * Svalue)
{
int res;
if(status == 1)
{
void * t_res;
if(pthread_tryjoin_np(t_thread, &t_res) != 0)
{
status = 1;
}
else
{
if((int)t_res == 1)
{
res = 3;
*Svalue = elevation;
elevation = 0;
}
else
{
res = 4;
}
}
}
return res;
}
int p_nmea(void *targs)
{
struct t_args *thread_args = targs;
struct gps_data_t gpsdata;
int ret = -1;
int count = 10;
int i,j;
if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0)
{
(void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno));
return (-1);
}
else
{
(void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
do
{
if(!gps_waiting(&gpsdata, 1000000))
{
(void)gps_close(&gpsdata);
}
else
{
if(gps_read(&gpsdata) == -1)
{
return (-1);
}
else
{
if(gpsdata.set & PACKET_SET)
{
for (i = 0; i < MAXCHANNELS; i++)
{
for (j = 0; j < gpsdata->satellites_visible; j++)
{
if(gpsdata->PRN[i] == thread_args.ID)
{
elevation = (int)gpsdata->elevation[i];
ret = 1;
break;
}
}
if(gpsdata->PRN[i] == thread_args.ID)
{
break;
}
}
}
}
}
--count;
}while(count != 0);
}
(void)gps_stream(&gpsdata, WATCH_DISABLE, NULL);
(void)gps_close(&gpsdata);
(void)free(thread_args);
(void)pthread_exit((void*) ret);
}
As recommended in the documentation too, I had a look at cgps and gpxlogger for example codes, but the subtleties of libgps escape me. A while loop has been added before gps_waiting() in order to get, at least, one entire response object. Before introducing pthread, I noted that call the function test_result() just after start_test() take few seconds before returning an answer. By using a thread I thought that 3 would be imediately returned, then 3 or 4 .. but it's not ! I am still losing few seconds. In addition, I voluntarily use pthread_tryjoin_np() because its man page says
The pthread_tryjoin_np() function performs a nonblocking join with the thread
Can anybody give me his help, I guess that I understand something wrongly but I am not able to say about which part yet? Basically, why I come into the do while loop at least four times before returning the first value ?
EDIT 1 :
After reading the documentation HOWTO again I highlight the lines :
The fact that the data-waiting check and the read both block means that, if your application has to deal with other input sources than the GPS, you will probably have to isolate the read loop in a thread with a mutex lock on the gps_data structure.
I am a little bit confusing. What does it really mean ?
Your loop is executing multiple times before returning a full packet because you do not have a sleep condition. Therefore each time the daemon registers a packet (even when not a full NMEA message), the gps_waiting() function returns. I'd recommend sleeping at least as long as it takes your GPS to register a full message.
For example, if you expect GPPAT messages, you could reasonably expect to have 12 characters in the message. Thus at 9600 baud, that would take 1/17.5 seconds or about 57 ms. In this case, your code could look like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <gps.h>
#include <pthread.h>
pthread_t t_thread;
struct t_args {
unsigned int ID;
};
unsigned int status = 0;
int elevation;
int p_nmea(void *targs);
void start_test(void)
{
struct t_args *args = malloc(sizeof *args);
status = 1;
args->ID = 10;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0)
{
perror("create: \n");
}
}
int test_result(int * Svalue)
{
int res;
if(status == 1)
{
void * t_res;
if(pthread_tryjoin_np(t_thread, &t_res) != 0)
{
status = 1;
}
else
{
if((int)t_res == 1)
{
res = 3;
*Svalue = elevation;
elevation = 0;
}
else
{
res = 4;
}
}
}
return res;
}
int p_nmea(void *targs)
{
struct t_args *thread_args = targs;
struct gps_data_t gpsdata;
int ret = 0;
int count = 10;
int i,j;
if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0)
{
(void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno));
return (-1);
}
else
{
(void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
do
{
ret = 0; // Set this here to allow breaking correctly
usleep(50000); // Sleep here to wait for approx 1 msg
if(!gps_waiting(&gpsdata, 1000000)) break;
if(gps_read(&gpsdata) == -1) break;
if(gpsdata.set & PACKET_SET)
{
for (i = 0; i < MAXCHANNELS && !ret; i++)
{
for (j = 0; j < gpsdata.satellites_visible; j++)
{
if(gpsdata.PRN[i] == thread_args.ID)
{
elevation = (int)gpsdata.elevation[i]; // Be sure to not deref structure here
ret = 1;
break;
}
}
}
--count;
}while(count != 0);
}
(void)gps_stream(&gpsdata, WATCH_DISABLE, NULL);
(void)gps_close(&gpsdata);
(void)free(thread_args);
(void)pthread_exit((void*) ret);
}
Alternatively, you could just set your count higher and wait for the full message.
I am trying to implement a code to practice synchronization, so might not be best design or approach but goal is as below
Main thread
Creates a payload of 100 integers and waits for any thread to be available
When it gets signal from a thread its available - it unlocks the payload for copying and proceeds to create another payload
Worker thread
on creation of it makes itself available for data processing and sends signal that its available
Tries to lock the data payload from main thread and copy it to local array
( observing bug here - not able to access data properly)
Turn off the sign of available
( unable to turn off available state to off)
Keep processing data through local copy
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#define WORKERS 2
#define ARRAY_ELEMENTS 100
#define MAX 1000
pthread_mutex_t mutex_bucket1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_signal = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_go = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_busy = PTHREAD_COND_INITIALIZER;
static int value = 0;
bool available = false;
void *worker_thread(void *pbucket)
{
sleep(5);
while(1)
{
unsigned int count = 0;
int local_array[ARRAY_ELEMENTS];
int *ptbucket = (int*)pbucket;
setbuf(stdout, NULL);
pthread_mutex_lock(&mutex_signal);
printf(" -------------- \n chainging state to available \n --------- ");
available = true;
printf(" -------------- \n from thread sending go signal \n --------- ");
pthread_cond_signal(&cond_go);
pthread_mutex_unlock(&mutex_signal);
pthread_mutex_lock(&mutex_bucket1);
printf(" -------------- \n data part locked in thread for copying \n --------- ");
while(count < ARRAY_ELEMENTS)
{
printf(" %d - \n", ptbucket[count]); /***incorrect values***/
local_array[count] = ptbucket[count];
count++;
}
pthread_mutex_unlock(&mutex_bucket1);
/*Never able to acquire mutex_signal and change state to not available*/ **BUG**
pthread_mutex_lock(&mutex_signal);
printf(" -------------- \n chainging state to not available \n --------- ");
available = false;
pthread_mutex_unlock(&mutex_signal);
count = 0;
while(count < ARRAY_ELEMENTS)
{
printf(" %d - \n", local_array[count]);
count++;
}
printf(" -------------- \n about to sleep for 5secs \n --------- ");
sleep(5);
}
}
int main(void)
{
pthread_t thread_id[WORKERS];
unsigned int* pbucket1 = (int*) malloc(sizeof(int) * ARRAY_ELEMENTS);
unsigned int* pbucket;
for(int i = 0; i < WORKERS - 1; i++)
{
pthread_create(&thread_id[i], NULL, worker_thread, (void *) pbucket);
}
for(int i = 0; i < MAX; i++)
{
unsigned int count = 0;
pbucket = pbucket1;
// Make the payload ready
pthread_mutex_lock(&mutex_bucket1);
printf(" -------------- creating data payload --------- \n");
while(count < ARRAY_ELEMENTS)
{
pbucket1[count] = i;
i++;
count++;
}
printf(" -------------- \n waiting for go signal \n --------- ");
while(!available)
{
pthread_cond_wait(&cond_go, &mutex_signal);
}
pthread_mutex_unlock(&mutex_bucket1);
/*I believe after we unlock variable "available" can be mutexed
again by other thread but seems thinking is flawed */
printf(" -------------- \n Main thread sleep for 3 seconds \n --------- ");
sleep(3);
}
for(int i = 0; i < WORKERS; i++)
{
pthread_join(thread_id[i], NULL);
}
return 0;
}
I think some of your idea is backwards; It shouldn't be the main context that is waiting, it should be the worker threads waiting for data ...
The job of the main thread should be to keep populating the payload and waking one thread at a time to process it.
So here's some scribbled code that is a little more sensible, I think:
/**
file: answer.c
compile: gcc -o answer answer.c -pthread
usage: answer [numThreads] [numElements]
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define STATE_WAIT 1
#define STATE_READY 2
void *routine(void*);
typedef struct _shared_t {
pthread_mutex_t m;
pthread_cond_t c;
unsigned char state;
int *payload;
size_t numElements;
pthread_t *threads;
size_t numThreads;
} shared_t;
static inline void shared_init(shared_t *shared, size_t numThreads, size_t numElements) {
memset(shared, 0, sizeof(shared_t));
pthread_mutex_init(&shared->m, NULL);
pthread_cond_init(&shared->c, NULL);
shared->state = STATE_WAIT;
shared->numThreads = numThreads;
shared->numElements = numElements;
{
int it = 0;
shared->threads = (pthread_t*) calloc(shared->numThreads, sizeof(pthread_t));
while (it < shared->numThreads) {
if (pthread_create(&shared->threads[it], NULL, routine, shared) != 0) {
break;
}
it++;
}
}
}
static inline void shared_populate(shared_t *shared) {
if (pthread_mutex_lock(&shared->m) != 0) {
return;
}
shared->payload = (int*) calloc(shared->numElements, sizeof(int));
{
int it = 0,
end = shared->numElements;
while (it < end) {
shared->payload[it] = rand();
it++;
}
}
shared->state = STATE_READY;
pthread_cond_signal(&shared->c);
pthread_mutex_unlock(&shared->m);
}
static inline void shared_cleanup(shared_t *shared) {
int it = 0,
end = shared->numThreads;
while (it < end) {
pthread_join(shared->threads[it], NULL);
}
pthread_mutex_destroy(&shared->m);
pthread_cond_destroy(&shared->c);
free(shared->threads);
}
void* routine(void *arg) {
shared_t *shared = (shared_t*) arg;
int *payload;
do {
if (pthread_mutex_lock(&shared->m) != 0) {
break;
}
while (shared->state == STATE_WAIT) {
pthread_cond_wait(&shared->c, &shared->m);
}
payload = shared->payload;
shared->state = STATE_WAIT;
pthread_mutex_unlock(&shared->m);
if (payload) {
int it = 0,
end = shared->numElements;
while (it < end) {
printf("Thread #%ld got payload %p(%d)=%d\n",
pthread_self(), payload, it, payload[it]);
it++;
}
free(payload);
}
} while(1);
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
shared_t shared;
int numThreads = argc > 1 ? atoi(argv[1]) : 1;
int numElements = argc > 2 ? atoi(argv[2]) : 100;
shared_init(&shared, numThreads, numElements);
do {
shared_populate(&shared);
} while (1);
shared_cleanup(&shared);
return 0;
}
Obviously, the code above is not very tolerant of errors, and is not easy to shutdown cleanly ... it's illustration only.
Let's first look at main so that we know what the flow of the main program is going to be:
int main(int argc, char *argv[]) {
shared_t shared;
int numThreads = argc > 1 ? atoi(argv[1]) : 1;
int numElements = argc > 2 ? atoi(argv[2]) : 100;
shared_init(&shared, numThreads, numElements);
do {
shared_populate(&shared);
} while (1);
shared_cleanup(&shared);
return 0;
}
It keeps a shared_t on the stack:
typedef struct _shared_t {
pthread_mutex_t m;
pthread_cond_t c;
unsigned char state;
int *payload;
size_t numElements;
pthread_t *threads;
size_t numThreads;
} shared_t;
Mostly self explanatory, mutex, condition and state are required for synchronization.
First of all the shared_t must be initialized with mutex, condition, state and threads using the provided options:
static inline void shared_init(shared_t *shared, size_t numThreads, size_t numElements) {
memset(shared, 0, sizeof(shared_t));
pthread_mutex_init(&shared->m, NULL);
pthread_cond_init(&shared->c, NULL);
shared->state = STATE_WAIT;
shared->numThreads = numThreads;
shared->numElements = numElements;
{
int it = 0;
shared->threads = (pthread_t*) calloc(shared->numThreads, sizeof(pthread_t));
while (it < shared->numThreads) {
if (pthread_create(&shared->threads[it], NULL, routine, shared) != 0) {
break;
}
it++;
}
}
}
When the worker threads are created by this routine, they are forced into a waiting state.
The first call to shared_populate in the loop awakens the first thread after setting the payload to some random numbers:
static inline void shared_populate(shared_t *shared) {
if (pthread_mutex_lock(&shared->m) != 0) {
return;
}
shared->payload = (int*) calloc(shared->numElements, sizeof(int));
{
int it = 0,
end = shared->numElements;
while (it < end) {
shared->payload[it] = rand();
it++;
}
}
shared->state = STATE_READY;
pthread_cond_signal(&shared->c);
pthread_mutex_unlock(&shared->m);
}
Note the use of pthread_cond_signal over pthread_cond_broadcast, because we only want to wake the first thread.
void* routine(void *arg) {
shared_t *shared = (shared_t*) arg;
int *payload;
do {
if (pthread_mutex_lock(&shared->m) != 0) {
break;
}
while (shared->state == STATE_WAIT) {
pthread_cond_wait(&shared->c, &shared->m);
}
payload = shared->payload;
shared->state = STATE_WAIT;
pthread_mutex_unlock(&shared->m);
if (payload) {
int it = 0,
end = shared->numElements;
while (it < end) {
printf("Thread #%ld got payload %p(%d)=%d\n",
pthread_self(), payload, it, payload[it]);
it++;
}
free(payload);
}
} while(1);
pthread_exit(NULL);
}
So we wake up in routine at the call to pthread_cond_wait, the state has changed, so we break out of the loop, we save the pointer to the payload, reset the state to WAIT, and release the mutex.
At this point main can repopulate the payload and awaken the next thread, meanwhile the current worker thread can process, and then free the payload.
Some advice:
Always use as few mutex and condition variables as possible (KISS)
Research the atomic nature of condition variables
Always follow the basic rules regarding acquisition and release of mutex and signaling of condition variables:
If you locked it, unlock it.
Only ever wait for something: predicated wait loops are absolutely required, all the time.
If you can't reproduce what I done, then take the code and try to expand upon it; The first thing you need to do is be able to shutdown the process gracefully (enter shared_cleanup), maybe you need a variable sized payload, or some other requirement not mentioned in the original question.
Note about printf ... appending to a stream is not guaranteed to be atomic, it so happens that most of the time on *nix it is ... since we are just doing show and tell, we don't need to care about that ... ordinarily, do not rely on atomicity for any stream operations ...
I'm doing some really heavy and slow scientific simulations and I was wondering if it was possible to make my program show some information about its status when the user presses a key.
It doesn't need to be portable, just for linux.
Currently, I have a code of this kind:
main()
{
//SOME GLOBAL INITIALIZATIONS
//this loop is parallelized
for(i=0;i<max;i++)
{
//Some particular to i initializations
for(j=0;max2;j++)
{
// Here are all the slow and ugly calculations
// the status should be able to be shown when
// my program is here
}
}
// Final calculations and final data
}
Not sure if it is even possible, but sometimes one may think that the code is stopped or doing nothing, and this could be very helpful.
Also, if it is not possible to do this and use at the same time omp, I can use a more "manual" parallelization.
Thanks in advance!
You can use the pthreads library: Have one thread waiting for the key or whatever signal and when it is triggered. If you need to do some expensive computations to show the output variables, use a lock to stop execution of the simulation, print the variables and release the lock. Don't know if that can get to work with openmp thought.
If you just need to print the vars, there shouldn't be a problem in mixing pthreads and openmp.
You can use ncurses' getch()
#include <ctype.h>
#include <ncurses.h>
#include <stdio.h>
int keywaiting(int *key) {
int ch;
ch = getch();
if ((ch != ERR) && key) *key = ch;
return (ch != ERR);
}
int yourprogram(void) {
int max = 1000, max2 = 1000;
int i, j;
int ch;
/* SOME GLOBAL INITIALIZATIONS */
/* this loop is parallelized */
for (i = 0; i < max; i++) {
/* Some particular to i initializations */
for (j = 0; j < max2; j++) {
/* Here are all the slow and ugly calculations */
/* the status should be able to be shown when */
/* my program is here */
printf("%03d%03d\r", i, j);
if (keywaiting(&ch)) {
printf("\r\n\r\nAborted # i = %d; j = %d\r\n", i, j);
printf("Keypress %d ('%c')\r\n\n", ch, isprint((unsigned char)ch) ? ch : '.');
printf("Press any key ...");
refresh();
i = max;
j = max2;
}
}
}
/* Final calculations and final data */
return 0;
}
int main(void) {
/* initialize ncurses */
initscr();
nodelay(stdscr, TRUE);
noecho();
keypad(stdscr, TRUE);
curs_set(0);
yourprogram();
getchar();
/* done with ncurses */
clear();
refresh();
endwin();
return 0;
}
Don't forget to link with the curses library.
here's an example that uses signals (but partially written in c++, the signal handling is not):
//ctrl+c to output status, ctrl+z to quit
//#include <iostream> c++ syntax
#include <stdio.h>
#include <signal.h>
volatile sig_atomic_t showStatus = 0;
void signalHandler(int sig){
if (sig==SIGINT){
showStatus=1;
}
if (sig==SIGTSTP){
showStatus=2;
}
}
int main(int argc, char** argv){
signal(SIGINT,signalHandler);
signal(SIGTSTP,signalHandler);
while(true){
if (showStatus==1){
showStatus=0;
//std::cout << "status" << std::endl; c++ syntax
printf("status\n");
}else if(showStatus>1){
break;
}
}
return 0;
}
Ok, thanks for all your sugestions. I have managed to get something similar to what I wanted, It's not the same but will serve (^_^)
Here is the code, it just spawns a thread that will wait for a character, then 'switches' the report array from 0 to 1. When each omp thread starts each 'j' iteration they will check for the report and will report if needed.
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <pthread.h>
#include <ncurses.h>
unsigned char *report; // this will store whether a thread has to report or not
int max_threads;
void *check_keyboard(void *whatever)
{
int c;
int i;
newterm(NULL,stdin,stdout); // I want to still see the terminal :)
noecho();
cbreak();
while(1)
{
if(getch())
{
for(i = 0; i < max_threads; i++)
report[i]=1;
}
}
return NULL;
}
int main(int argc, const char *argv[])
{
int i,j;
int tid;
pthread_t thread0;
max_threads = omp_get_max_threads();
report = malloc(max_threads*sizeof(unsigned char));
pthread_create(&thread0,NULL,check_keyboard,NULL);
#pragma omp parallel private(i,j,tid) shared(report)
{
tid = omp_get_thread_num();
#pragma omp for
for(i = 0; i < 10; i++)
{
for(j = 0; j < 10; j++)
{
#pragma omp critical
{
if(report[tid])
{
fprintf(stderr,"Hello World from thread %d\n\r",tid);
fprintf(stderr,"This is iteration i: %d, j: %d\n\r",i,j);
report[tid] = 0;
}
}
sleep(1); // This 'is' a test for the long and ugly code
}
}
}
pthread_cancel(thread0);
pthread_exit(NULL);
return 0;
}