FIFO implementation, deadlock - c

#include <math.h>
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define TASK_LENGHT pow(10, 5) /* 0.1s */
#define TASK_COUNT 100
#define THREADS_CAP 1
struct fifo {
size_t out;
size_t in;
size_t size;
size_t len;
pthread_spinlock_t lock;
char data[1];
};
struct fifo *
fifo_alloc(size_t size)
{
struct fifo *fifo;
fifo = malloc(offsetof(struct fifo, data) + size);
fifo->out = fifo->in = 0;
fifo->size = size;
fifo->len = 0;
pthread_spin_init(&fifo->lock, PTHREAD_PROCESS_PRIVATE);
return fifo;
}
void
fifo_free(struct fifo *fifo)
{
pthread_spin_destroy(&fifo->lock);
free(fifo);
}
size_t
fifo_in(struct fifo *fifo, const void *data, size_t len)
{
size_t total, remaining, l;
pthread_spin_lock(&fifo->lock);
total = remaining = MIN(len, fifo->size - fifo->len);
while (remaining > 0) {
l = MIN(remaining, fifo->size - fifo->in);
memcpy(fifo->data + fifo->in, data, l);
fifo->in = (fifo->in + l) % fifo->size;
fifo->len += l;
data = (char *)data + l;
remaining -= l;
}
pthread_spin_unlock(&fifo->lock);
return total;
}
size_t
fifo_out(struct fifo *fifo, void *data, size_t len)
{
size_t total, remaining, out, l;
pthread_spin_lock(&fifo->lock);
total = remaining = MIN(len, fifo->len);
while (remaining > 0) {
l = MIN(remaining, fifo->size - fifo->out);
memcpy(data, fifo->data + fifo->out, l);
fifo->out = (fifo->out + l) % fifo->size;
data = (char *)data + l;
remaining -= l;
}
fifo->len -= total;
pthread_spin_unlock(&fifo->lock);
return total;
}
int
fifo_is_empty(struct fifo *fifo)
{
int empty;
pthread_spin_lock(&fifo->lock);
empty = fifo->len == 0;
pthread_spin_unlock(&fifo->lock);
return empty;
}
void *
process_task(void *datap)
{
struct fifo *tasks = datap;
int data;
while (1) {
puts("it blocks there");
if (!fifo_is_empty(tasks)) {
puts("this code never execute");
fifo_out(tasks, &data, sizeof(data));
if (data == -1) pthread_exit(0);
printf("%lu => %d\n", pthread_self(), data);
usleep(TASK_LENGHT);
}
puts("this code never execute");
}
}
int
main(void) {
pthread_t threads[THREADS_CAP];
struct fifo *tasks;
int data;
size_t i;
tasks = fifo_alloc(128);
for (i = 0; i < THREADS_CAP; i++)
pthread_create(threads + i, NULL, process_task, &tasks);
for (i = 0; i < TASK_COUNT; i++)
fifo_in(tasks, &i, sizeof(i));
for (i = 0; i < THREADS_CAP; i++) {
data = -1;
fifo_in(tasks, &data, sizeof(i));
}
puts("wait for threads to process all tasks");
for (i = 0; i < THREADS_CAP; i++) {
pthread_join(threads[i], NULL);
}
fifo_free(tasks);
}
I debugged my program and it turned out that it blocks at the pthread_spin_lock in fifo_is_empty. I was trying to recreate this problem with the minimum code needed like this:
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define THREADS_CAP 10000
struct test {
pthread_spinlock_t lock;
};
pthread_barrier_t barrier;
int counter = 0;
void *
foo(void *data) {
struct test *test = data;
pthread_barrier_wait(&barrier);
pthread_spin_lock(&test->lock);
counter++;
pthread_spin_unlock(&test->lock);
return 0;
}
int main() {
struct test *test;
pthread_t threads[THREADS_CAP];
size_t i;
test = malloc(sizeof(test));
pthread_barrier_init(&barrier, NULL, THREADS_CAP);
pthread_spin_init(&test->lock, PTHREAD_PROCESS_PRIVATE);
for (i = 0; i < THREADS_CAP; i++)
pthread_create(threads + i, NULL, foo, test);
for (i = 0; i < THREADS_CAP; i++)
pthread_join(threads[i], NULL);
printf("%d\n", counter);
pthread_spin_destroy(&test->lock);
pthread_barrier_destroy(&barrier);
return 0;
}
but i was not able to. I just works for some reason in contrast to my previous code.
Does anyone have an idea what is causing there problem and how to fix it?

The problem took me a while to sort out, but I arrived there through working on a minimal reproducible example. I ultimately cut out everything related to managing the FIFO data, some unneeded functions, and all of the looping, but I found that the failure behavior did not manifest if I changed the definition of struct fifo.
Finally, I realized that the problem is here:
struct fifo *tasks;
// ...
pthread_create(threads + i, NULL, process_task, &tasks);
in conjunction with
void *process_task(void *datap) {
struct fifo *tasks = datap;
Bottom line: you are passing the address of your tasks pointer, a pointer to a pointer, but then your thread function tries to interpret it as a single pointer. Undefined behavior results.
The correction is to pass tasks itself to the thread, not &tasks:
pthread_create(threads + i, NULL, process_task, tasks);
Your program has other problems, but that resolves the one you asked about.

Related

Multi-threading using Merge sort

I am newly starting using Linux in Ubuntu and am trying to code a multi-threading merge sort but some kind of errors are showing on Windows terminal.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
struct Params
{
int *start;
size_t len;
int depth;
};
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
void *merge_sort_thread(void *pv);
void merge(int *start, int *mid, int *end)
{
int *res = malloc((end -start)*sizeof(*res));
int *lhs = start, *rhs = mid, *dst = res;
while (lhs != mid && rhs != end)*dst++ = (*lhs <= *rhs) ? *lhs++ : *rhs++;
while (lhs != mid)*dst++ = *lhs++;
while (rhs != end)*dst++ = *rhs++;
memcpy(start, res, (end -start)*sizeof(*res));free(res);
}
void merge_sort_mt(int *start, size_t len, int depth)
{
if (len < 2)return;
if (depth <= 0 || len < 4)
{
merge_sort_mt(start, len/2, 0);
merge_sort_mt(start+len/2, len-len/2, 0);
}
else{
struct Params params = { start, len/2, depth/2 };
pthread_t thrd;pthread_mutex_lock(&mtx);
printf("Starting subthread...\n");
pthread_mutex_unlock(&mtx);
pthread_create(&thrd, NULL, merge_sort_thread, &params);
merge_sort_mt(start+len/2, len-len/2, depth/2);
pthread_join(thrd, NULL);
pthread_mutex_lock(&mtx);
printf("Finished subthread.\n");
pthread_mutex_unlock(&mtx);
}
merge(start, start+len/2, start+len);
}
void *merge_sort_thread(void *pv)
{
struct Params *params = pv;
merge_sort_mt(params->start, params->len, params->depth);
return pv;
}
void merge_sort(int *start, size_t len)
{
merge_sort_mt(start, len, 4);
}
int main()
{
static const unsigned int N = 2048;
int *data = malloc(N * sizeof(*data));
unsigned int i;
srand((unsigned)time(0));
for (i=0; i<N; ++i)
{
data[i] = rand() % 1024;
printf("%4d ", data[i]);
if ((i+1)%8 == 0)
printf("\n");
}
printf("\n");
merge_sort(data, N);
for (i=0; i<N; ++i)
{
printf("%4d ", data[i]);
if ((i+1)%8 == 0)
printf("\n");
}
printf("\n");
free(data);
return 0;
}
Here, is the .c code of multithreadind but not complete the program due to errors.
Here below, is the errors..
/tmp/ccTNp3Yz.o: In function `merge_sort_mt':
OS.c:(.text+0x213): undefined reference to `pthread_create'
OS.c:(.text+0x269): undefined reference to `pthread_join'
collect2: error: ld returned 1 exit status
If any library or any correction in code please tell and please add some important libraries which is usually used in terminal like sudo
As was stated at the comment above, you need to link your program with pthread library.
add -lpthread to your link command.

Multiple threads to find prime factors of integers, segmentation fault

I can't figure out what I am doing wrong with my pointers. It is causing a segmentation fault. I am convinced the problem is rooted in my use of the array of pointers I have and the pthread_join I am using.
The goal is to read multiple integers into a gcc compiler, then print out the integer with all its factors, like this, 12: 2 2 3
I created a struct containing an int array to store the factors of each integer as the factor function pulls it apart and a counter(numfact) to store how many factors there are stored in the array.
I commented out the section at the bottom that prints out the factors.
I think the problem is how I am trying to store the output from the pthread_join in the pointer array, ptr[]. Whenever I comment it out, it does not get the segmentation error.
Either I have my pointers screwed up in a way I don't understand or I can't use an array of pointers. Either way, after many hours, I am stuck.
Please help.
#include <stdio.h>
#include <pthread.h>
#include <math.h>
#include <stdlib.h>
struct intfact
{
long int factors[100];
int numfact;
};
struct intfact *factor(long int y)
{
struct intfact threadfact;
threadfact.numfact = 0;
// Store in struct the number of 2s that divide y
while (y % 2 == 0)
{
threadfact.factors[threadfact.numfact] = 2;
threadfact.numfact++;
y = y/2;
}
// Store in struct the odds that divide y
for (int i = 3; i <= floor(sqrt(y)); i = i+2)
{
while (y % i == 0)
{
threadfact.factors[threadfact.numfact] = i;
threadfact.numfact++;
y = y/i;
}
}
// Store in struct the primes > 2
if (y > 2)
{
threadfact.factors[threadfact.numfact] = y;
threadfact.numfact++;
}
struct intfact *rtnthred = &threadfact;
return rtnthred;
}
/* Trial Division Function */
void *divde(void *n)
{
long int *num = (long int *) n;
struct intfact *temp = factor(*num);
return temp;
}
/* Main Function */
int main(int argc, char *argv[])
{
pthread_t threads[argc-1];
void *ptr[argc-1];
/* loop to create all threads */
for(int i=0; i < argc; i++)
{
long temp = atol(argv[i+1]);
pthread_create(&threads[i], NULL, divde, (void *) temp);
}
/* loop to join all threads */
for(int i=0; i < argc; i++)
{
pthread_join(threads[i],(void *) ptr[i]); //THIS POINTER IS THE PROBLEM
}
/* loops to print results of each thread using pointer array*/
//for(int i = 0; i < argc; i++)
//{
// printf("%s: ", argv[i+1]); /* print out initial integer */
// struct intfact *temp = (struct intfact *) ptr[i]; //cast void pointer ptr as struct intfact pointer
// printf("%d", temp->numfact);
//for(int j = 0; j < temp->numfact; j++) /*(pull the numfact(count of factors) from the struct intfact pointer??)*/
//{
// printf("%d ", temp->factors[j]); /* print out each factor from thread struct */
//}
}
}
In my Linux) terminal this code is stored in p3.c
"./p3 12" should yeild "12: 2 2 3"
For starters:
Here
long temp = atol(argv[i+1]);
pthread_create(&threads[i], NULL, divde, (void *) temp);
you define a long int and pass it as argument to the thread. For example 12
Inside the thread function then
void *divde(void *n)
{
long int *num = (long int *) n;
you treat the long int passed in as pointer to long int.
And then here dereference it
... = factor(*num);
So this *num for example would become *12. That is referencing memory address 12 to read out its content and pass it to factor). Aside the fact that this mostly likely is an invalid address, there would be nothing relevant store, at least nothing your code defined.
To (more or less fix) this do
void *divde(void *n)
{
long int num = (long int) n;
... = factor(num);
The second issues is mentioned in the comment: Multiple threads to find prime factors of integers, segmentation fault
The problem you are trying to solve is a special case of parallel programming, namely that the tasks to be run in parallel are completely independent. In such cases it makes sense to give each task its own context. Here such a context would include the
thread-id,
the thread specific input
as well as its specific output.
In C grouping variables can be done using structures, as your implementation already comes up with for the output of the tasks:
struct intfact
{
long int factors[100];
int numfact;
};
So what is missing is thread-id and input. Just add those for example like this.
/* group input and output: */
struct inout
{
long int input;
struct intfact output;
};
/* group input/output with thread-id */
struct context
{
pthread_t thread_id;
struct inout io;
};
Now before kicking off the threads define as many contexts as needed:
int main(int argc, char *argv[])
{
size_t num_to_process = argv - 1;
struct context ctx[num_to_process];
then create the threads passing in what is needed, that is input along with space/memory for the output:
for (size_t i = 0; i < num_to_process ; i++)
{
ctx[i].io.input = atol(argv[i]);
pthread_create(&ctx[i].thread_id, NULL, divide, &ctx[i].io);
}
Inside the thread function convert the void-pointer received back to its real type:
void *divide(void * pv)
{
struct inout * pio = pv; /* No cast needed in C. */
Define the processing function to take a pointer to the context specific input/output variables:
void factor(struct inout * pio) /* No need to return any thing */
{
/* Initialise the output: */
pio->output.numfact = 0;
/* set local copy of input: */
long int y = pio->input; /* One could also just use pio->input directly. */
Replace all other occurrences of threadfact by pio->output.
Use
return;
}
to leave the processing function.
Then inside the thread function call the processing function:
factor(pio);
Use
return NULL;
}
to leave the thread function.
In main() join without expecting any result from the threads:
/* loop to join all threads */
for (size_t i = 0; i < num_to_process; i++)
{
pthread_join(ctx[i].thread_id, NULL);
}
Putting this all together:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <math.h>
struct intfact
{
long int factors[100];
size_t numfact;
};
/* group input and output: */
struct inout
{
long int input;
struct intfact output;
};
/* group input/output with thread-id */
struct context
{
pthread_t thread_id;
struct inout io;
};
void factor(struct inout * pio)
{
/* Initialise the output: */
pio->output.numfact = 0;
/* set local copy of input: */
long int y = pio->input; /* One could also just use pinout->input directly. */
if (0 == y)
{
return; /* Nothing to do! */
}
// Store in struct the number of 2s that divide y
while (y % 2 == 0)
{
pio->output.factors[pio->output.numfact] = 2;
pio->output.numfact++;
y = y/2;
}
// Store in struct the odds that divide y
for (int i = 3; i <= floor(sqrt(y)); i = i+2)
{
while (y % i == 0)
{
pio->output.factors[pio->output.numfact] = i;
pio->output.numfact++;
y = y/i;
}
}
// Store in struct the primes > 2
if (y > 2)
{
pio->output.factors[pio->output.numfact] = y;
pio->output.numfact++;
}
return;
}
void *divide(void * pv)
{
struct inout * pio = pv; /* No cast needed in C. */
factor(pio);
return NULL;
}
int main(int argc, char *argv[])
{
size_t num_to_process = argc - 1;
struct context ctx[num_to_process];
for (size_t i = 0; i < num_to_process; i++)
{
ctx[i].io.input = atol(argv[i+1]);
if (!ctx[i].io.input)
{
fprintf(stderr, "COnversion to integer failed or 0 for '%s'\n", argv[i]);
}
pthread_create(&ctx[i].thread_id, NULL, divide, &ctx[i].io);
}
/* loop to join all threads */
for (size_t i=0; i < num_to_process; i++)
{
pthread_join(ctx[i].thread_id, NULL);
}
/* loops to print results of each thread using pointer array*/
for(size_t i = 0; i < num_to_process; i++)
{
printf("%ld: ", ctx[i].io.input); /* print out initial integer */
printf("%zu factors --> ", ctx[i].io.output.numfact);
for(size_t j = 0; j < ctx[i].io.output.numfact; j++)
{
printf("%ld ", ctx[i].io.output.factors[j]); /* print out each factor from thread struct */
}
putc('\n', stdout);
}
}

Writing a parallel quick sort in c

I need to write a parallel quick sort in c using pthreads. This is what I did so far.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h> // sleep()
#include <stdio.h>
#include <stdlib.h> // EXIT_SUCCESS
#include <string.h> // strerror()
#include <errno.h>
#define SIZE_OF_DATASET 6
void* quickSort( void* data);
int partition( int* a, int, int);
struct info {
int start_index;
int* data_set;
int end_index;
};
int main(int argc, char **argv)
{
int a[] = { 7, 12, 1, -2,8,2};
pthread_t thread_id;
struct info *info = malloc(sizeof(struct info));
info->data_set=malloc(sizeof(int)*SIZE_OF_DATASET);
info->data_set=a;
info->start_index=0;
info->end_index=SIZE_OF_DATASET-1;
if (pthread_create(&thread_id, NULL, quickSort, info)) {
fprintf(stderr, "No threads for you.\n");
return 1;
}
pthread_join(thread_id, NULL);
printf("\n\nSorted array is: ");
int i;
for(i = 0; i < SIZE_OF_DATASET; ++i)
printf(" %d ", info->data_set[i]);
return 0;
}
void* quickSort( void *data)
{
struct info *info = data;
int j,l,r;
l = info->start_index;
r = info->end_index;
pthread_attr_t attr;
pthread_t thread_id1;
pthread_t thread_id2;
pthread_attr_init(&attr);
if( l < r )
{
j = partition( info->data_set, l, r);
info->start_index=l;
info->end_index=j-1;
if(info->end_index<0)info->end_index=0;
if (pthread_create(&thread_id1, NULL, quickSort, info)) {
fprintf(stderr, "No threads for you.\n");
return NULL;
}
info->start_index=j+1;
info->end_index=r;
if (pthread_create(&thread_id2, NULL, quickSort, info)) {
fprintf(stderr, "No threads for you.\n");
return NULL;
}
pthread_join(thread_id1, NULL);
pthread_join(thread_id2, NULL);
}
return NULL;
}
int partition( int* a, int l, int r) {
int pivot, i, j, t;
pivot = a[l];
i = l; j = r+1;
while( 1)
{
do ++i; while( a[i] <= pivot && i <= r );
do --j; while( a[j] > pivot );
if( i >= j ) break;
t = a[i]; a[i] = a[j]; a[j] = t;
}
t = a[l]; a[l] = a[j]; a[j] = t;
return j;
}
But inside quick sort function only call first thread only. Cant understand what is been happening here.
Note : serial version of code has been tested. no issue with that
UPDATE:
This is modified version based on John Bollinger's solution. But still second half of array which is taken by newly created thread inside quicksort is not sorted.
int main(int argc, char **argv)
{
int a[] = { 7, 12, 1, -2, 0, 15, 4, 11, 9,5,3,24,5,23,3,1,56,8,4,34,23,51};
struct info *info = malloc(sizeof(struct info));
info->data_set=malloc(sizeof(int)*SIZE_OF_DATASET);
info->data_set=a;
info->start_index=0;
info->end_index=SIZE_OF_DATASET-1;
quickSort(info);
printf("\n\nSorted array is: ");
int i;
for(i = 0; i < SIZE_OF_DATASET; ++i)
printf(" %d ", info->data_set[i]);
return 0;
}
void* quickSort( void *data)
{
struct info *info = data;
struct info *info1 = data;
int j,l,r;
l = info->start_index;
r = info->end_index;
pthread_attr_t attr;
pthread_t thread_id1;
pthread_attr_init(&attr);
if( l < r )
{
j = partition( info->data_set, l, r);
info1->start_index=j+1;
info1->end_index=r;
info1->data_set = info->data_set;
if(info1->end_index<0)info1->end_index=0;
if (pthread_create(&thread_id1, NULL, quickSort, info1)) {
fprintf(stderr, "No threads for you.\n");
return NULL;
}
info->start_index=l;
info->end_index=j-1;
if(info->end_index < 0) info->end_index = 0;
quickSort(info); /* don't care about the return value */
pthread_join(thread_id1, NULL);
}
return NULL;
}
The program is incorrect because your threads all share the same struct info structure describing the sub-problem they are supposed to be working on. They run concurrently (or may do, anyway) and they modify that structure as they proceed, so the values that any particular thread sees are indeterminate.
To resolve this, each quickSort frame must create at least one new struct info, so that the two quickSort() calls it makes in different threads each has its own. As a matter of efficiency, it would also be better to spawn only one additional thread in each quickSort() call. For example:
void* quickSort( void *data)
{
struct info *info = data;
struct info other_info;
/* ... */
/* launch a new thread to handle one partition: */
other_info.start_index = j + 1;
other_info.end_index = r;
other_info.data_set = info->data_set;
if (pthread_create(&thread_id1, NULL, quickSort, &other_info)) {
fprintf(stderr, "No threads for you.\n");
return NULL;
}
/* handle the other partition in the current thread: */
info->start_index = l;
info->end_index = j - 1;
if(info->end_index < 0) info->end_index = 0;
quickSort(info); /* don't care about the return value */
/* after this thread is done, wait for the other thread to finish, too */
pthread_join(thread_id1, NULL);
/* ... */
}
Note that this does not ensure that any particular pair of threads runs concurrently, neither in a multi-core sense nor in a time-slicing sense. That's up to the OS. Certainly, however, the multi-core sense of parallelism applies only where there are in fact multiple cores available on the host machine on which the OS is willing to schedule your process.

Creating Pthreads

I'm trying to create a thread for the function *producer, but the line for creating the thread is showing in error. I starred the line, but I'm having trouble figuring out what is wrong with it...
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#define TOTALLOOPS 100 /*Num of loops run*/
#define NUMOFPAIRS 4 /*For each 1 it produces 1 consumer and 1 producer*/
typedef struct {
int q[NUMOFPAIRS];
int head;
int tail;
int full;
int empty;
pthread_mutex_t mut; /*Creates a mutex Lock*/
pthread_cond_t notFull; /*Creates conditional*/
}Queue;
int main(void)
{
Queue buf; /* Declare and initialize parts of struct */
buf.head = 0;
buf.tail = 0;
buf.full = 0;
buf.empty = 0;
pthread_mutex_init(&buf.mut, NULL);/*intitializes mutex for struct*/
//pthread_cond_init(&buf.nutFull, NULL);
pthread_t pro;
**pthread_create(&pro, NULL, producer, &buf);**
pthread_mutex_destroy(&buf.mut);
return 0;
}
void *producer(int x, Queue *buf){
int id = x;
int i;
for(i = 0; i < TOTALLOOPS; i++){
while(buf->full == 1){
//do nothing
}
mClock();
printf(" - Producer%d:\n", id);
}
}
void* consumer(int x, Queue *buf){
int id = x;
int i;
for(i = 0; i < TOTALLOOPS; i++){
while(buf->empty == 1){
//do nothing
}
mClock();
printf(" - Consumer%d:\n", id);
}
}
void addToQueue(Queue *buf, int x){
//Checks if empty flag is triggered, if so un triggers
if(buf->empty) buf->empty = 0;
buf->q[buf->tail] = x;
if(buf->tail == 3) buf->tail = 0; /*Resets to beginning if at end*/
else buf->tail += 1; /*else just moves to next*/
//Checks if full flag needs to be triggered, if so triggers
if(buf->tail == buf->head) buf->full = 1;
}
int removeFromQueue(Queue *buf){
int t; /*return value from queue*/
//Checks if full flag is triggered, if so un triggers
if(buf->full == 1)buf->full = 0;
t = buf->q[buf->head];
if(buf->head == 3) buf->head = 0; /*Resets to beginning if at end*/
else buf->head += 1; /*else just moves to next*/
//Checks if full flag needs to be triggered, if so triggers
if(buf->tail == buf->head) buf->empty = 1;
return t;
}
void mClock(){
struct timeval tv;
gettimeofday(&tv,NULL);
long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
printf("%u", time_in_micros);
}
You have to declare producer before the pthread_create call.
void *producer(int x, Queue *buf);
should show up first.
Likewise, mClock has to be declared first.
Also, the function should only take one argument
I don't see where you're calling the initializers for pthread_mutex_t mut and pthread_cond_t notFull in your structure anywhere.
PTHREAD_MUTEX
PTHREAD_COND
Also, you need to change the order of declaration of your functions, or add prototypes to the top of the file.

How do I unpack and extract data properly using msgpack-c?

I'm currently trying to use msgpack in a project written in C. I'm using msgpack for the purpose of serializing the contents of a struct, which is then to be sent over the network, and deserialized back into a corresponding struct on the other side.
Condensed version of what I'm trying to do:
#include <stdio.h>
#include <msgpack.h>
#include <stdbool.h>
typedef someStruct{
uint32_t a;
uint32_t b;
float c;
} someStruct;
int main (void){
someStruct data;
/* ... Fill 'data' with some data for test purposes ...*/
msgpack_sbuffer* buff = msgpack_sbuffer_new();
msgpack_packer* pck = msgpack_packer_new(buff, msgpack_sbuffer_write);
someStruct* structs = malloc(sizeof(someStruct) * 10);
/* ... Fill 'structs' with members containing test data ... */
// Serialize
msgpack_pack_array (pck, 10);
int i;
for(i = 0 ; i < 10 ; i++){
msgpack_pack_array (pck, 3);
msgpack_pack_uint32 (pck, structs[i].a);
msgpack_pack_uint32 (pck, structs[i].b);
msgpack_pack_float (pck, structs[i].c);
}
free(structs);
msgpack_packer_free(pck);
// Deserialize
msgpack_unpacked msg;
msgpack_unpacked_init(&msg);
bool deserialize_success = msgpack_unpack_next
(&msg, buff->data, buff->size, NULL);
if(!deserialize_success) /* Error */
msgpack_object obj = msg.data;
msgpack_object_print(stdout,obj); // This seems to work perfectly, indicating serialize / deserialize works as intended...
someStruct deserialized_data;
/* Insert code to extract and cast deserialized data to 'deserialized_data */
// Clean
msgpack_sbuffer_free(buff);
msgpack_packer_free(pck);
return 0;
}
The code listed is more or less ripped straight from here, which seems to be one of very few resources on msgpack-c.
Can anyone point me in the right direction as to a way to 'recreate' the original struct on the other side of the wire? The only way I've found to actually utilize the deserialized data, is to use the msgpack_object_print() call to print from the messagepack_object. This does, however seem to work, so I'm certain the data is there.
Do I need to somehow loop through the serialized data and use msgpack_unpack_next() with an offset to retrieve each someStruct member? Using memcpy to a local byte buffer?
Any help is greatly appreciated!
Please find below a rewritten version that illustrates how to pack / unpack your data.
The whole idea is to pack each successive field of your struct, in a contiguous fashion, and apply (of course), the same logic at unpack time.
Right after pack, you are free to use the buffer the way you want (e.g send over the network, save on-disk, etc).
#include <stdio.h>
#include <assert.h>
#include <msgpack.h>
typedef struct some_struct {
uint32_t a;
uint32_t b;
float c;
} some_struct;
static char *pack(const some_struct *s, int num, int *size);
static some_struct *unpack(const void *ptr, int size, int *num);
/* Fixtures */
some_struct ary[] = {
{ 1234, 5678, 3.14f },
{ 4321, 8765, 4.13f },
{ 2143, 6587, 1.34f }
};
int main(void) {
/** PACK */
int size;
char *buf = pack(ary, sizeof(ary)/sizeof(ary[0]), &size);
printf("pack %zd struct(s): %d byte(s)\n", sizeof(ary)/sizeof(ary[0]), size);
/** UNPACK */
int num;
some_struct *s = unpack(buf, size, &num);
printf("unpack: %d struct(s)\n", num);
/** CHECK */
assert(num == (int) sizeof(ary)/sizeof(ary[0]));
for (int i = 0; i < num; i++) {
assert(s[i].a == ary[i].a);
assert(s[i].b == ary[i].b);
assert(s[i].c == ary[i].c);
}
printf("check ok. Exiting...\n");
free(buf);
free(s);
return 0;
}
static char *pack(const some_struct *s, int num, int *size) {
assert(num > 0);
char *buf = NULL;
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer pck;
msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
/* The array will store `num` contiguous blocks made of a, b, c attributes */
msgpack_pack_array(&pck, 3 * num);
for (int i = 0; i < num; ++i) {
msgpack_pack_uint32(&pck, s[i].a);
msgpack_pack_uint32(&pck, s[i].b);
msgpack_pack_float(&pck, s[i].c);
}
*size = sbuf.size;
buf = malloc(sbuf.size);
memcpy(buf, sbuf.data, sbuf.size);
msgpack_sbuffer_destroy(&sbuf);
return buf;
}
static some_struct *unpack(const void *ptr, int size, int *num) {
some_struct *s = NULL;
msgpack_unpacked msg;
msgpack_unpacked_init(&msg);
if (msgpack_unpack_next(&msg, ptr, size, NULL)) {
msgpack_object root = msg.data;
if (root.type == MSGPACK_OBJECT_ARRAY) {
assert(root.via.array.size % 3 == 0);
*num = root.via.array.size / 3;
s = malloc(root.via.array.size*sizeof(*s));
for (int i = 0, j = 0; i < root.via.array.size; i += 3, j++) {
s[j].a = root.via.array.ptr[i].via.u64;
s[j].b = root.via.array.ptr[i + 1].via.u64;
s[j].c = root.via.array.ptr[i + 2].via.dec;
}
}
}
msgpack_unpacked_destroy(&msg);
return s;
}

Resources