Friday, October 10, 2008

POSIX Thread

A nice posix thread tutorial.
Also google a pdf pthread tutorial by Peter Chapin.

Here I am trying to do a very trivial UVa problem: 11172 Relational Operators using multi-threading.
But UVa compiler does not do g++ -pthread. We just play here instead of submission. :)
Two threads:
  1. the first thread read fron stdin
  2. the second thread compare the two numbers and output
  3. both thread access the two buffers using semaphore to synchronize

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <errno.h>

struct MYDATA{
int a;
int b;
};

MYDATA data[2];

struct sembuf p1 = {0, -1, 0}; // request buf 0
struct sembuf p2 = {1, -1, 0}; // request buf 1
struct sembuf v1 = {0, 1, 0}; // release buf 0
struct sembuf v2 = {1, 1, 0}; // release buf 1

int ndata;
int semid;


void* read(void*) {
int c = ndata;
while(1) {
scanf("%d %d ", &data[0].a, &data[0].b);
//printf("read %d %d, c %d \n", data[0].a, data[0].b, c);
if(c-- == 0) break;
semop(semid, &v1, 1);

semop(semid, &p2, 1);
scanf("%d %d ", &data[1].a, &data[1].b);
//printf("read %d %d, c %d \n", data[1].a, data[1].b, c);
if(c--==0) break;
semop(semid, &v2, 1);
semop(semid, &p1, 1);
}
//printf("read thread end\n");
pthread_exit((void*)0);
}

inline void cmp(int a, int b) {
if(a > b) printf(">\n");
else if(a < b) printf("<\n");
else printf("=\n");
}

void* compare(void*) {
int c = ndata;
while(1) {
//semop(semid, &p2, 1);
//printf("compare %d %d \n", data[1].a, data[1].b);
cmp(data[1].a, data[1].b);
if(c-- == 0) break;
semop(semid, &v2, 1);

semop(semid, &p1, 1);
//printf("compare %d %d \n", data[0].a, data[0].b);
cmp(data[0].a, data[0].b);
if(c--==0) break;
semop(semid, &v1, 1);
semop(semid, &p2, 1);
}
pthread_exit((void*)0);
}

int main() {
pthread_attr_t attr;

/* create threads */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

scanf("%d ", &ndata);
scanf("%d %d ", &data[1].a, &data[1].b);
ndata --;

if((semid = semget(0x3332, 2, 0600 |IPC_CREAT)) == -1) {
perror("semget");
exit(1);
}

if(semctl(semid, 0, SETVAL, 0) == -1) {
perror("semctl");
exit(1);
}

if(semctl(semid, 1, SETVAL, 0) == -1) {
perror("semctl");
exit(1);
}

pthread_t readThd, compareThd;
pthread_create(&readThd, &attr, read, (void*)0);
pthread_create(&compareThd, &attr, compare, (void*)0);


void * status;
pthread_join(readThd, &status);
pthread_join(compareThd, &status);

pthread_exit(NULL);
}

The IPC shared memory version is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


struct databuf {
int a1;
int b1;
int a2;
int b2;
};

inline void cmp(int a, int b) {
if(a > b) printf(">\n");
else if(a < b) printf("<\n");
else printf("=\n");
}

int main(int argc, char *argv[])
{
key_t key = 0x18875;
int shmid;
int semid;
int mode;

/* make the key: */
/*
if ((key = ftok("plotter.c", 'R')) == -1) {
perror("ftok");
exit(1);
}*/


/* connect to (and possibly create) the segment: */
if ((shmid = shmget(key, sizeof(databuf), 0600 |IPC_CREAT)) == -1) {
perror("shmget");
exit(1);
}
/* attach to the segment to get a pointer to it: */
databuf* data = (databuf*)shmat(shmid, (void *)0, 0);

if (data == (databuf *)(-1)) {
perror("shmat");
exit(1);
}
//memset(data, 0, sizeof(data));

struct sembuf p1 = {0, -1, 0}; /* set to allocate resource */
struct sembuf p2 = {1, -1, 0};
struct sembuf v1 = {0, 1, 0}; /* free resource */
struct sembuf v2 = {1, 1, 0};

/*
if ((key = ftok("fibfreeze.c", 'J')) == -1) {
perror("ftok");
exit(1);
}*/


/* create a semaphore set with 1 semaphore: */
if ((semid = semget(0x1543, 2, 0666 | IPC_CREAT)) == -1) {
perror("semget");
exit(1);
}
//printf("sem id %d\n", semid);

/* initialize semaphore #0 to 1: */
if (semctl(semid, 0, SETVAL, 0) == -1) {
perror("semctl");
exit(1);
}
if (semctl(semid, 1, SETVAL, 0) == -1) {
perror("semctl");
exit(1);
}

int ndata;
scanf("%d ", &ndata);
scanf("%d %d ", &(data->a2), &(data->b2));

pid_t kid = fork();
if(kid == 0) {
while(1) {
if(feof(stdin)) {
semop(semid, &v1, 1);
break;
}
scanf("%d %d ", &data->a1, &data->b1);
//printf("read %d %d \n", data->a1, data->b1);

semop(semid, &v1, 1);
semop(semid, &p2, 1);
if(feof(stdin)) {
semop(semid, &v2, 1);
break;
}
scanf("%d %d ", &data->a2, &data->b2);
//printf("read %d %d \n", data->a2, data->b2);

semop(semid, &v2, 1);
semop(semid, &p1, 1);
}
//printf("kid exit\n");
exit(0);
} else {
int c = ndata;
while(1) {
//printf("cmp %d %d, c %d\n", data->a2, data->b2, c);
cmp(data->a2, data->b2);
if(--c ==0) break;
semop(semid, &v2, 1);
semop(semid, &p1, 1);

//printf("cmp %d %d, c %d\n", data->a1, data->b1, c);
cmp(data->a1, data->b1);
if(--c ==0) break;
semop(semid, &v1, 1);
semop(semid, &p2, 1);
}
}

/* remove semaphore */
if (semctl(semid, 0, IPC_RMID, 0) == -1) {
perror("semctl");
exit(1);
}
//printf("free semaphores done\n");

/* detach from the segment: */
if (shmdt(data) == -1) {
perror("shmdt");
exit(1);
}
/* remove shared memory */
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl shmid");
exit(1);
}
//printf("free memory segment done\n");
return 0;
}