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.
Related
PS: I am very new to threads.
I have a problem where i need to wait for connection requests(completely arbitrary number of times) from clients, accept a connection on a socket, create a worker thread after connection. The created thread then creates a char array, works on it and needs to pass it to the parent process.
I have been able to create the threads in a while loop like
while ((new_socket = accept(srv_sock, (struct sockaddr *)&client, &c)) != INVALID_SOCKET)
{
puts("\nConnection accepted");
_beginthreadex(0, 0, handle_client, &new_socket, 0, 0);
}
I have seen that pthread_join() can be used to pass data from thread to parent process(in unix). My question is, how can I integrate it into a loop in the main process.
I expect the following approach will result in a situation where no more than one connection can be established between client and server at a time,which is not desired.
while ((new_socket = accept(srv_sock, (struct sockaddr *)&client, &c)) != INVALID_SOCKET)
{
puts("\nConnection accepted");
_beginthreadex(0, 0, handle_client, &new_socket, 0, 0);
pthread_join(thread_id,&my_array);
}
EDIT: I would be happy to know if what I want is impossible or if there are alternatives to pthread_join(). or its windows equivalent.
EDIT: I know that pthread_join() is for Unix and have read that WaitForMultipleObjects() is its equivalent for windows. In any case I still haven't been able to figure out a solution.
I have seen that pthread_join() can be used to pass data from thread to parent process.
That is not entirely correct. You can pass a pointer when you exit a thread, and collect that pointer using pthread_join. You have to implement all the logic yourself. The API does not know (or care) what the pointer is. Threads don't have parents and children, they are siblings.
Example for a creator and a reaper:
global
struct VarLengthArray {
size_t count;
MyElem data[1];
};
exiting thread:
// allocate the result
size_t count = ...;
VarLengthArray *retval = malloc(
sizeof(VarLengthArray) +
sizeof(MyElem) * (count > 0 ? count - 1 : 0)
);
// fill the result
retval->count = count;
for (size_t i = 0; i < retval->count; ++i) {
retval->data[i] = ...;
}
pthread_exit(retval);
collecting thread:
// collect the result
void *retval_;
if (pthread_join(thread_one_id, &retval_) != 0) {
// handle error
}
VarLengthArray *retval = retval_;
// use the result
for (size_t i = 0; i < retval->count; ++i) {
printf("retval->[%u] = %s\n", (unsigned) i, retval->data[i].string_value);
}
// deallocate the result
free(retval);
A full example using a condition variable and multiple creators:
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct Datum {
struct Datum *next;
char some_data[32];
} Datum;
typedef struct SharedData {
pthread_mutex_t mutex;
pthread_cond_t cond_empty;
unsigned seed;
Datum *head, *tail;
unsigned children_alive;
} SharedData;
static void *thread_logic(void *argv_);
int main(int argc, char **argv) {
unsigned thread_count = 2;
if (argc > 1) {
if (sscanf(argv[1], " %u ", &thread_count) != 1) {
fprintf(stderr, "Usage: %s [thread_count]\n", argv[0]);
return 1;
}
}
// initialize shared data
SharedData shared_data;
pthread_mutex_init(&shared_data.mutex, NULL);
pthread_cond_init(&shared_data.cond_empty, NULL);
shared_data.seed = time(NULL);
shared_data.head = NULL;
shared_data.tail = NULL;
shared_data.children_alive = 0;
// start threads detached, so you don't have to call pthread_join
pthread_t *child_ids = malloc(sizeof(pthread_t) * thread_count);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// start the threads
pthread_mutex_lock(&shared_data.mutex);
for (unsigned i = 0; i < thread_count; ++i) {
if (pthread_create(&child_ids[i], &attr, thread_logic, &shared_data) != 0) {
perror("pthread_create");
} else {
++shared_data.children_alive;
}
}
pthread_mutex_unlock(&shared_data.mutex);
pthread_attr_destroy(&attr);
// loop until all threads are dead
while (shared_data.children_alive > 0) {
// a condition variable: wait until there is data you can read
pthread_mutex_lock(&shared_data.mutex);
while (shared_data.head == NULL) {
pthread_cond_wait(&shared_data.cond_empty, &shared_data.mutex);
}
// collect a first datum
Datum *datum = shared_data.head;
if (datum->next != NULL) {
shared_data.head = datum->next;
} else {
shared_data.head = shared_data.tail = NULL;
}
pthread_mutex_unlock(&shared_data.mutex);
// handle the data (outside of the mutex lock)
printf("Got data: %s\n", datum->some_data);
free(datum);
}
return 0;
}
static void *thread_logic(void *shared_data_) {
SharedData *shared_data = shared_data_;
while (1) {
pthread_mutex_lock(&shared_data->mutex);
// create some data
useconds_t timeout = (
(((float) (unsigned) rand_r(&shared_data->seed)) / UINT_MAX) *
1000000
);
Datum *datum = malloc(sizeof(Datum));
datum->next = NULL;
if (timeout < 1000000 / 25) {
--shared_data->children_alive;
snprintf(datum->some_data, sizeof(datum->some_data), "I'm done\n");
} else {
snprintf(
datum->some_data, sizeof(datum->some_data),
"Sleeping for %uus\n", timeout
);
}
// append the datum
if (shared_data->head) {
shared_data->tail->next = datum;
} else {
shared_data->head = datum;
pthread_cond_signal(&shared_data->cond_empty);
}
shared_data->tail = datum;
pthread_mutex_unlock(&shared_data->mutex);
// most likely it takes some time to create the data
// do lengthly tasks outside of the mutex lock
if (timeout < 1000000 / 25) {
return NULL;
} else {
usleep(timeout);
}
}
}
I am having trouble understanding the multiple producers and single consumer problem. I am working on an assignment and I am unsure on how creating two producers work.. I understand how the single producer/consumer problem works but I cannot understand how to go about multiple producers would I need to create two separate threads for each producer, if that is the case how does filling a queue up with their 'produced data' would one of the producers need to be asleep while the other producer fills say a single data item and then they switch back and forth until the queue buffer is full?
Just looking for an explanation as I do not understand exactly how this would work (before someone makes the suggestion I am looking for someone to do my homework thats not the case just looking for helpful insight to clear up my thoughts on this so I can implement it myself)
I have looked at many other questions/topics about this on this and various other websites and still haven't been able to come to a conclusion on my answer.
Thanks!
Here is my solution using just the pipe and select system calls to implement a MPSCQ. The following diagram elaborate how it works:
<producer-thread-1> {msg produced in heap}
\
\ /* only address of msg objects were sent to pipe[1] */
\
pipe[1] >>>(kernel)>>> pipe[0] <consumer-thread>:
/ 1. polling from pipe[0]
/ 2. restore msg object via address ptr
/ 3. process then delete the msg object
<producer-thread-2> {msg produced in heap}
The demo code is in C++ for encapsulating the queue into a class, and no C++11/14/17 features were used. First is the queue class, in template form:
// mpscq.hpp
#include <sys/select.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define PTR_SIZE (sizeof(void*))
template<class T> class MPSCQ { // Multi Producer Single Consumer Queue
public:
MPSCQ() {
int pipe_fd_set[2];
pipe(pipe_fd_set); // err-handler omitted for this demo
_fdProducer = pipe_fd_set[1];
_fdConsumer = pipe_fd_set[0];
}
~MPSCQ() { /* pipe close omitted for this demo */ }
int producerPush(const T* t) {
// will be blocked when pipe is full, should always return PTR_SIZE
return t == NULL ? 0 : write(_fdProducer, &t, PTR_SIZE);
}
T* consumerPoll(int timeout = 1);
private:
int _selectFdConsumer(int timeout);
private:
int _fdProducer; // pipe_fd_set[1]
int _fdConsumer; // pipe_fd_set[0]
};
template<class T> T* MPSCQ<T>::consumerPoll(int timeout) {
if (_selectFdConsumer(timeout) <= 0) { // timeout or error
return NULL;
}
char ptr_buff[PTR_SIZE];
ssize_t r = read(_fdConsumer, ptr_buff, PTR_SIZE);
if (r <= 0) {
fprintf(stderr, "consumer read EOF or error, r=%d, errno=%d\n", r, errno);
return NULL;
}
T* t;
memcpy(&t, ptr_buff, PTR_SIZE); // cast received bytes to T*
return t;
}
template<class T> int MPSCQ<T>::_selectFdConsumer(int timeout) {
int nfds = _fdConsumer + 1;
fd_set readfds;
struct timeval tv;
while (true) {
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(_fdConsumer, &readfds);
int r = select(nfds, &readfds, NULL, NULL, &tv);
if (r < 0 && errno == EINTR) {
continue;
}
return r;
}
}
Then here comes the test case: 4 producer threads emitting 1..100000, and one consumer thread sums it up.
// g++ -o mpscq mpscq.cpp -lpthread
#include "mpscq.hpp"
#include <sys/types.h>
#include <pthread.h>
#define PER_THREAD_LOOPS 25000
#define SAMPLE_INTERVAL 10000
#define PRODUCER_THREAD_NUM 4
struct TestMsg {
int _msgId; // a dummy demo member
int64_t _val; // _val < 0 is an end flag
TestMsg(int msg_id, int64_t val) :
_msgId(msg_id),
_val(val) { };
};
static MPSCQ<TestMsg> TEST_QUEUE;
void* functor_producer(void* arg) {
int* task_seg = (int*) arg;
TestMsg* msg;
for (int i = 0; i <= PER_THREAD_LOOPS; ++ i) {
int64_t id = PER_THREAD_LOOPS * (*task_seg) + i;
msg = new TestMsg(id, i >= PER_THREAD_LOOPS ? -1 : id + 1);
TEST_QUEUE.producerPush(msg);
}
delete task_seg;
return NULL;
}
void* functor_consumer(void* arg) {
int64_t* sum = (int64_t*)arg;
int msg_cnt = 0;
int stop_cnt = 0; // for shutdown gracefully
TestMsg* msg;
while (true) {
if ((msg = TEST_QUEUE.consumerPoll()) == NULL) {
continue;
}
int64_t val = msg->_val;
delete msg; // this delete is essential to prevent memory leak
if (val <= 0) {
if ((++ stop_cnt) >= PRODUCER_THREAD_NUM) {
printf("all done, sum=%ld\n", *sum);
break;
}
} else {
*sum += val;
if ((++ msg_cnt) % SAMPLE_INTERVAL == 0) {
printf("msg_cnt=%d, sum=%ld\n", msg_cnt, *sum);
}
}
}
return NULL;
}
int main(int argc, char* const* argv) {
int64_t sum = 0;
printf("PTR_SIZE: %d, target: sum(1..%d)\n", PTR_SIZE, PRODUCER_THREAD_NUM * PER_THREAD_LOOPS);
pthread_t consumer;
pthread_create(&consumer, NULL, functor_consumer, &sum);
pthread_t producers[PRODUCER_THREAD_NUM];
for (int i = 0; i < PRODUCER_THREAD_NUM; ++ i) {
pthread_create(&producers[i], NULL, functor_producer, new int(i));
}
for (int i = 0; i < PRODUCER_THREAD_NUM; ++ i) {
pthread_join(producers[i], NULL);
}
pthread_join(consumer, NULL);
return 0;
}
A sample test result:
$ ./mpscq
PTR_SIZE: 8, target: sum(1..100000)
msg_cnt=10000, sum=490096931
msg_cnt=20000, sum=888646187
msg_cnt=30000, sum=1282852073
msg_cnt=40000, sum=1606611602
msg_cnt=50000, sum=2088863858
msg_cnt=60000, sum=2573791058
msg_cnt=70000, sum=3180398370
msg_cnt=80000, sum=3768718659
msg_cnt=90000, sum=4336431164
msg_cnt=100000, sum=5000050000
all done, sum=5000050000
The MPSCQ implemented here is a message-passing pattern and let the kernel to deal with the complexity of the internal queue operations. A side-effect of the trick is when the work load is heavy, there will be too many select calls in the consumer side which will affect the performance significantly. (In this demo, every time the consumer just fetch 8 bytes. To alleviate it, the consumer should maintain an extra receive buffer.)
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.
Basically what I am trying to do is simulate multithreading on a single thread with context switching. I set up an alarm for every 10 microseconds, and I switch the context from one to another thread. The problem is that about one in 5 runs ends up with a seg fault right after the alarm finishes the swapcontext, at least that is where I traced it with gdb.
Here are my source files
main.c
#include "umt.h"
void f()
{
int x = 10;
printf("starting thread\n");
while(x)
{
printf("thread %d\n", x);
sleep(1);
x--;
}
}
int main()
{
int x = 0, y, z;
umt_init();
y = umt_thread_create(f);
printf("starting main\n");
if(y == 0)
{
printf("Problems with creating thread\n");
return;
}
x = 10;
z = 1;
while(x)
{
printf("main\n");
x--;
}
umt_thread_join(y);
printf("done waiting\n");
return 0;
}
UMT.h
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef struct _umt_thread
{
int thread_id;
ucontext_t context;
void (*handler)(void);
int hasFinished;
}umt_thread, *pumt_thread;
void umt_init();
int umt_thread_create(void (*handler)(void));
void umt_thread_join(int thr);
and umt.c
#include "umt.h"
#define MAIN_CONTEXT 0
#define STACK_SIZE 1638400
int currentThread;
char threadpool[15];
pumt_thread threads;
void signal_thread_finish();
void thread_handler()
{
threads[currentThread].handler();
signal_thread_finish();
}
void thread_scheduler();
void signal_thread_finish()
{
threads[currentThread].hasFinished = TRUE;
threadpool[currentThread] = 0;
thread_scheduler();
}
void thread_scheduler()
{
int nextThread = 0, curThread = 0;
int x = 0;
ucontext_t *con1, *con2;
nextThread = currentThread + 1;
while(1)
{
if(nextThread == 15)
nextThread = 0;
if(nextThread == currentThread)
break;
if(threadpool[nextThread] == 1)
break;
nextThread++;
}
if(nextThread == currentThread)
return;
curThread = currentThread;
currentThread = nextThread;
con1 = &(threads[curThread].context);
con2 = &(threads[nextThread].context);
x = swapcontext(con1, con2);
}
void umt_init()
{
ucontext_t context;
struct itimerval mytimer;
int i;
stack_t new_stack;
getcontext(&context);
threads = (pumt_thread)malloc(sizeof(umt_thread) * 15);
threads[MAIN_CONTEXT].thread_id = MAIN_CONTEXT;
threads[MAIN_CONTEXT].context = context;
threadpool[MAIN_CONTEXT] = 1;
for(i = 1;i<15;i++)
{
threadpool[i] = 0;
}
currentThread = 0;
new_stack.ss_sp = (char*)malloc(STACK_SIZE);
new_stack.ss_size = STACK_SIZE;
new_stack.ss_flags = 0;
i = sigaltstack(&new_stack, NULL);
if(i != 0)
{
printf("problems assigning new stack for signaling\n");
}
signal(SIGALRM, thread_scheduler);
mytimer.it_interval.tv_sec = 0;
mytimer.it_interval.tv_usec = 10;
mytimer.it_value.tv_sec = 0;
mytimer.it_value.tv_usec = 5;
setitimer(ITIMER_REAL, &mytimer, 0);
}
int umt_thread_create(void (*handler)(void))
{
ucontext_t context;
int i, pos;
for(i = 1;i<15;i++)
{
if(threadpool[i] == 0)
{
pos = i;
break;
}
}
if(i == 15)
{
printf("No empty space in the threadpool\n");
return -1;
}
if(getcontext(&context) == -1)
{
printf("Problems getting context\n");
return 0;
}
context.uc_link = 0;//&(threads[MAIN_CONTEXT].context);
context.uc_stack.ss_sp = (char*)malloc(STACK_SIZE);
if(context.uc_stack.ss_sp == NULL)
{
printf("Problems with allocating stack\n");
}
context.uc_stack.ss_size = STACK_SIZE;
context.uc_stack.ss_flags = 0;
makecontext(&context, thread_handler, 0);
threads[pos].thread_id = pos;
threads[pos].context = context;
threads[pos].handler = handler;
threads[pos].hasFinished = FALSE;
threadpool[pos] = 1;
printf("Created thread on pos %d\n", pos);
return pos;
}
void umt_thread_join(int tid)
{
while(!threads[tid].hasFinished)
{
}
}
I tried a lot of combinations and tried tracing by instruction but could not arrive to a conclusion or idea as to what might cause this seg fault. Thanks
Few issues I see (some are related to segfault + some other comments)
You scheduler (thread_scheduler) should be in a critical section, e.g. you should block any alarm signals (or ignore them) so that the handing of the threadpool is done in a way that doesn't corrupt it. you can either use sigprocmask or a volatile boolean variable that will silence the alarm (note this is not the same as the user threads mutex, just an internal synchronization to your scheduling logic)
your clock ticks way too fast IMHO, this is in micro seconds, not milliseconds, so 1000 microseconds for tv_usec might make more sense for testing purposes.
small stack sizes might also cause a seg fault but it seems your stack is big enough.
p.s. there is a better way to handle join, you currently waste lot's of CPU cycles on it, why not simply avoid switching to a thread that called join, untill the thread that it's waiting for has terminated?
I use sigsetjmp/siglongjmp to change the progame stack. This is the demo:
#include <stdio.h>
#include <stddef.h>
#include <setjmp.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#define POOLSIZE 4096
int active = 0;
int total = 0;
struct thread
{
int tid;
sigjmp_buf env;
char buf[4096];
int state;
ssize_t size;
};
struct thread *thread_pool = 0L;
char* anchor_beg = 0L;
char* anchor_end = 0L;
void(*new_thread)(int) = 0L;
void sig_call(int sig)
{
char anchor;
anchor_end = &anchor;
if(sigsetjmp(thread_pool[active].env, 0) == 0)
{
thread_pool[active].size = anchor_beg - anchor_end;
memcpy(thread_pool[active].buf, anchor_end, thread_pool[active].size);
siglongjmp(thread_pool[0].env, 1);
}
else
{
memcpy(anchor_beg - thread_pool[active].size, thread_pool[active].buf, thread_pool[active].size);
}
}
void thread_new(void(*pfn)(int))
{
alarm(0);
new_thread = pfn;
thread_pool[0].state = 2;
// printf("create new thread:%d\n", total + 1);
raise(SIGUSR1);
}
void test(int thread)
{
int i = 0;
for(;i != 1000000; i++)
{
}
}
void thread_main(int thread)
{
int i = 0;
for(i = 0; i < 4000; i++)
thread_new(test);
}
void call(void(*pfn)(int))
{
active = ++ total;
thread_pool[active].tid = active;
thread_pool[active].state = 1;
ualarm(500, 0);
pfn(active);
thread_pool[active].state = 0;
}
void dispatcher()
{
thread_pool = (struct thread*)malloc(sizeof(struct thread) * POOLSIZE);
char anchor;
anchor_beg = &anchor;
thread_pool[0].tid = 0;
thread_pool[0].state = 1;
if(sigsetjmp(thread_pool[0].env, 0) == 0)
{
signal(SIGUSR1, sig_call);
signal(SIGALRM, sig_call);
call(thread_main);
}
else if(thread_pool[0].state == -1)
{
return;
}
else if(thread_pool[0].state == 2)
{
thread_pool[0].state = 1;
call(new_thread);
}
while(1)
{
int i, alive = 0;
for(i = 1; i <= total; i++)
{
if(thread_pool[i].state == 1)
{
alive ++;
ualarm(500, 0);
active = thread_pool[i].tid;
siglongjmp(thread_pool[i].env, 1);
}
}
if(alive == 0)
return;
}
}
int main()
{
dispatcher();
}
Is there any problem here? And when i want to call some third party interface, and maybe it is a block I/O there, can i do something to change another context to execute? and How?
Unfortunately, what you're trying to do doesn't work, because (per the setjmp manual):
The longjmp() routines may not be called after the routine which called
the setjmp() routines returns.
This is because the setjmp/longjmp family of functions (including the sig variants) do not preserve the entire contents of the process stack.