Multiple Producer single consumer - c

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.)

Related

Synchronization of Multithreading in C

I am new to MultiThreading in C. I am trying to write a code where we use multithreading. The code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <windows.h>
#include <time.h>
#include <math.h>
int finish = 0;
int mess_size=15;
double exponGenerator(double myLamda)
{
double pv = 0.0;
while (1)
{
pv = (double)rand() / (double)RAND_MAX;
if (pv != 1)
break;
}
pv = (-1.0 / myLamda) * log(1 - pv);
return pv;
}
struct packet{
int val;
time_t start_time;
double service_time;
};
struct queue{
struct packet arr_pac[10000];
int start;
int end;
int size;
};
double lambda = 5;
double servicerate = 5;
struct queue q1 ;
struct queue q2;
struct queue initialiseQueue(){
struct queue q ;
q.start = 0;
q.end = 0;
q.size=0;
return q;
}
struct process1{
int method;
double lambda;
struct queue Q1;
struct queue Q2;
};
struct process2{
struct queue q;
double u;
};
struct process1 queueenv(int method)
{
struct process1 temp ;
temp.method = method;
temp.lambda = lambda;
temp.Q1 = q1;
temp.Q2 = q2;
return temp;
}
struct process2 serverenv(double u, struct queue q)
{
struct process2 temp;
temp.u = u;
temp.q = q;
return temp;
}
int enque(struct queue q){
if (q.size < 10)
{
struct packet temp ;
temp.start_time = time(NULL);
temp.val = 1;
q.arr_pac[q.end] = temp;
q.end = q.end +1;
q.size = q.end - q.start;
//printf(" %d",q.arr_pac[0].end);
return q.size ;
}
else{
return -1;
}
}
struct packet deque(struct queue q) {
struct packet temp ;
printf(" %d ",q.end);
if(q.size >0){
printf("entered");
temp=q.arr_pac[q.start];
temp.service_time = difftime(time(NULL),temp.start_time);
q.start=q.start +1;
q.size = q.end - q.start;
return temp;
}
else{
printf("entered 2");
temp.service_time=0 ;
return temp;
}
}
int randomSelection(){
if(rand()%2 ==0){
return enque(q1);
}
else{
return enque(q2);
}
}
int minQueue(){
if(q1.size > q2.size){
return enque(q2);
}
else{
return enque(q1);
}
}
void queueprocess(struct process1 params){
double blockCounter = 0.0;
double blockPro = 0.0;
int queLenCounter = 0;
int averageQueueLen = 0;
int i = 0;
if (params.method == 0)
{
printf(" this is the %d =0",params.method);
for (i = 0; i < mess_size ; i++)
{
double interval = exponGenerator(params.lambda);
sleep(interval);
int res = randomSelection();
if (res == -1)
{
blockCounter++;
queLenCounter = queLenCounter +10;
}
else{
queLenCounter = queLenCounter + res;
//printf(" %d ",queLenCounter);
}
}
}
else if (params.method == 1)
{
printf(" this is the %d =1 ",params.method);
for (i = 0; i < mess_size ; i++)
{
double interval = exponGenerator(params.lambda);
sleep(interval);
int res = minQueue();
printf(" %d mn",q1.end);
if (res == -1)
{
blockCounter++;
queLenCounter = queLenCounter +10;
}
else{
queLenCounter = queLenCounter +res;
}
}
}
blockPro = blockCounter/mess_size;
averageQueueLen = queLenCounter/mess_size;
printf("Blocking Probability is : %f",&blockPro);
printf("Average Que Length is : %d",averageQueueLen);
finish = 1;
return;
}
void serverprocess(struct process2 serverparams)
{
struct packet processed_arr[10000];
int i=0,j;
while(1)
{
if (finish == 1 && serverparams.q.size == 0)
{
break;
}
else
{
double interval = exponGenerator(serverparams.u);
sleep(interval);
struct packet k = deque(serverparams.q);
if(!k.service_time){
processed_arr[i]=k;
i++;
}
}
}
float sourjanCounter=0;
float sourjan;
for(j=0;j<i;j++){
printf(" %f",processed_arr[j].service_time);
sourjanCounter = sourjanCounter+ processed_arr[j].service_time;
}
sourjan = sourjanCounter/(i-1);
printf("Average Sourjan Time is : %f ", &sourjan);
}
DWORD WINAPI threadone(){
printf(" thread one ");
struct process1 queueparams = queueenv(1);
queueprocess(queueparams);
}
DWORD WINAPI threadtwo(){
struct process2 server1params = serverenv(servicerate, q1);
serverprocess(server1params);
}
DWORD WINAPI threadthree(){
struct process2 server2params = serverenv(servicerate, q2);
serverprocess(server2params);
}
int main(int argc, char * argv[])
{
HANDLE hThrds[3];
int i = 0;
/*if (argc != 3)
{
printf("Incorrect parameters!\n");
return 0;
}
double lambda = atof(argv[1]);
double servicerate = atof(argv[2]);*/
for (i = 0; i < 1; i++)
{
DWORD thread1,thread2,thread3;
hThrds[2]=CreateThread(NULL,0, threadone, NULL,0,&thread1);
hThrds[0]=CreateThread(NULL,0, threadtwo, NULL,0,&thread2);
hThrds[1]=CreateThread(NULL,0, threadthree, NULL,0,&thread3);
WaitForMultipleObjects(3, hThrds,TRUE, INFINITE);
}
}
In my code I have a function called enque, which is called from a function called queueprocess. q1 is a structure and is defined globally, It is accessed by multiple functions at the same time. I am trying to change the value of q1 inside function enque and when I print its value inside the function enque it's value is correct, but if I try to print its value inside function queueprocess its value is 0. As a result of this my code is not moving forward. I have read about mutex, but I don't know what is mutex equivalent to win32. I don't know why mutex should be used for my program as well, since I am just accessing q1 from another function but I am not trying to change its value.
Can anyone please explain me why I am facing the problem mentioned above and what would be the possible solution of it?
From Microsoft documentation:
You can use a mutex object to protect a shared resource from
simultaneous access by multiple threads or processes. Each thread must
wait for ownership of the mutex before it can execute the code that
accesses the shared resource. For example, if several threads share
access to a database, the threads can use a mutex object to permit
only one thread at a time to write to the database.
The following example uses the CreateMutex function to create a mutex
object and the CreateThread function to create worker threads.
When a thread of this process writes to the database, it first
requests ownership of the mutex using the WaitForSingleObject
function. If the thread obtains ownership of the mutex, it writes to
the database and then releases its ownership of the mutex using the
ReleaseMutex function.
This example uses structured exception handling to ensure that the
thread properly releases the mutex object. The __finally block of code
is executed no matter how the __try block terminates (unless the __try
block includes a call to the TerminateThread function). This prevents
the mutex object from being abandoned inadvertently.
If a mutex is abandoned, the thread that owned the mutex did not
properly release it before terminating. In this case, the status of
the shared resource is indeterminate, and continuing to use the mutex
can obscure a potentially serious error. Some applications might
attempt to restore the resource to a consistent state; this example
simply returns an error and stops using the mutex. For more
information, see Mutex Objects.
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 2
HANDLE ghMutex;
DWORD WINAPI WriteToDatabase( LPVOID );
int main( void )
{
HANDLE aThread[THREADCOUNT];
DWORD ThreadID;
int i;
// Create a mutex with no initial owner
ghMutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (ghMutex == NULL)
{
printf("CreateMutex error: %d\n", GetLastError());
return 1;
}
// Create worker threads
for( i=0; i < THREADCOUNT; i++ )
{
aThread[i] = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE) WriteToDatabase,
NULL, // no thread function arguments
0, // default creation flags
&ThreadID); // receive thread identifier
if( aThread[i] == NULL )
{
printf("CreateThread error: %d\n", GetLastError());
return 1;
}
}
// Wait for all threads to terminate
WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
// Close thread and mutex handles
for( i=0; i < THREADCOUNT; i++ )
CloseHandle(aThread[i]);
CloseHandle(ghMutex);
return 0;
}
DWORD WINAPI WriteToDatabase( LPVOID lpParam )
{
// lpParam not used in this example
UNREFERENCED_PARAMETER(lpParam);
DWORD dwCount=0, dwWaitResult;
// Request ownership of mutex.
while( dwCount < 20 )
{
dwWaitResult = WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
switch (dwWaitResult)
{
// The thread got ownership of the mutex
case WAIT_OBJECT_0:
__try {
// TODO: Write to the database
printf("Thread %d writing to database...\n",
GetCurrentThreadId());
dwCount++;
}
__finally {
// Release ownership of the mutex object
if (! ReleaseMutex(ghMutex))
{
// Handle error.
}
}
break;
// The thread got ownership of an abandoned mutex
// The database is in an indeterminate state
case WAIT_ABANDONED:
return FALSE;
}
}
return TRUE;
}

how to create undefined number of threads and use WaitForMultipleObjects() in c on windows

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);
}
}
}

libgps for extract data from the gpsd daemon

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.

unable to reacquire mutex and pass values correctly between threads

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 ...

Segmentation fault after swapcontext in alarm handler

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?

Resources