I'm trying to solve a small problem of synchronization. but when I join the threads i get segment fault of the fifth iteration! If i only create 4 threads works perfect.
Here I leave the code with some basics of what to do the thread.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
sem_t HackersEmploy_Counter;
int hackerOnBoat, employOnBoat, B, b, hackerResagado, employResagado;
sem_t Board;
int onBoatId[4];
char onBoatType[4];
sem_t Bote;
typedef struct{
FILE* log;
int ID;
}param;
void* HackerArrive(void* para){
param* var = (param*) para;
printf("Create Hacker %i\n", var->ID-1);
pthread_exit(0);
}
void* EmployeeArrive(void* para){
param* var = (param*) para;
printf("Create Employee %i\n", var->ID-1);
pthread_exit(0);
}
int main(int argc, char **argv) {
sem_init(&HackersEmploy_Counter,0,1);
sem_init(&Bote,0,4);
sem_init(&Board,0,1);
FILE* log;
log = fopen("result_simulacion.txt", "w");
int E, e=1, H, h=1, i, r;
hackerOnBoat=0; employOnBoat=0; b=1; hackerResagado=0; employResagado=0;
for (i=1; i<argc; i++){
if (strcmp(argv[i],"-h")==0){
i++;
H = atoi(argv[i]);
}
if (strcmp(argv[i],"-e")==0){
i++;
E = atoi(argv[i]);
}
if (strcmp(argv[i],"-b")==0){
i++;
B = atoi(argv[i]);
}
}
pthread_t* bank = (pthread_t*) malloc( (E+H) * sizeof (pthread_t*));
param* var = (param*) malloc( (E+H) + sizeof (param*));
for (i=0; i<H+E; i++){
r = rand() % 2;
if (r==0){
if (h<=H){
var[i].log = log;
var[i].ID = h;
pthread_create(&bank[i], NULL, HackerArrive, (void*) &var[i]);
h++;
}else{
var[i].log = log;
var[i].ID = e;
pthread_create(&bank[i], NULL, EmployeeArrive, (void*) &var[i]);
e++;
}
}else{
if (e<=E){
var[i].log = log;
var[i].ID = e;
pthread_create(&bank[i], NULL, EmployeeArrive, (void*) &var[i]);
e++;
}else{
var[i].log = log;
var[i].ID = h;
pthread_create(&bank[i], NULL, HackerArrive, (void*) &var[i]);
h++;
}
}
}
for (i=0; i<E+H; i++){
pthread_join(bank[i], NULL);
printf("join %i\n", i);
}
return 0;
}
run with: ./work -h 4 -e 0 -b 1
them run with: ./work -h 5 -e 0 -b 1
if they increase the value of "-h" more than 4 receive segmentation fault
Why this?
The allocation for your array of pthread_ts isn't quite right. You want to allocate storage for an array of pthread_t instances but are currently only allocating space for pointers.
By not allocating space for the correct type, you risk writing beyond the end of your allocated memory when you later write to this array. This has undefined consequences; in this case you're likely to over-write memory used by other parts of your program. This may well cause a segfault.
You should change the allocation as follows
pthread_t* bank = malloc((E+H) * sizeof(*bank));
The same issue applies to the param array var which should be allocated like
param* var = malloc((E+H) + sizeof(*var));
Note that it'd be good practice for you to free these arrays later in your program
for (i=0; i<E+H; i++){
pthread_join(bank[i], NULL);
printf("join %i\n", i);
}
free(bank);
free(var);
Please be more careful reading your code. This:
param* var = malloc((E+H) + sizeof(*var));
should be this:
param* var = malloc((E+H) * sizeof(*var));
// ^
Your code works (or at least, doesn't crash) for me after this change:
paul#local:~/src/c/scratch$ ./thread -h 2 -e 2 -b 2
Create Employee 0
Create Hacker 1
Create Employee 1
Create Hacker 0
join 0
join 1
join 2
join 3
paul#local:~/src/c/scratch$
Using a tool like valgrind would have helped you track this one down in minutes.
As mentioned in my other comment, you should check the return every single time you call functions like malloc(), fopen(), pthread_create(), pthread_join(), and so on, otherwise you have absolutely no idea whether you're ignoring important errors your functions are trying to tell you about.
Related
I have a hard time figuring out how to pass an array of structs with strings in them through a pipe to a child process.
I created two demos to show my problem.
demo_int.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t child;
int pfd[2];
if (pipe(pfd) == -1)
{
exit(1);
}
child = fork();
if (child < 0)
{
exit(1);
}
if (child == 0)
{
close(pfd[1]);
int *arr = malloc(10 * sizeof(int));
if (arr == NULL)
{
exit(1);
}
read(pfd[0], arr, 10 * sizeof(int));
close(pfd[0]);
printf("child process read:\n");
for (int i = 0; i < 10; ++i)
{
printf("%d\n", arr[i]);
}
free(arr);
exit(0);
}
else
{
int *arr = malloc(10 * sizeof(int));
if (arr == NULL)
{
exit(1);
}
for (int i = 0; i < 10; ++i)
{
arr[i] = i;
}
printf("array to be written:\n");
for (int i = 0; i < 10; ++i)
{
printf("%d\n", arr[i]);
}
close(pfd[0]);
write(pfd[1], arr, 10 * sizeof(int));
close(pfd[1]);
free(arr);
printf("parent process done\n");
wait(NULL);
}
}
I created this, so I can be sure that the problem is not with the "dynamic array" part, but with the "structs" part, and maybe more specifically the "string in a struct" part.
This produces the expected result:
array to be written:
0
1
2
3
4
5
6
7
8
9
parent process done
child process read:
0
1
2
3
4
5
6
7
8
9
With valgrind reporting no errors or leaks.
However when I try the same with the problematic structs:
demo_person.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
typedef struct Person
{
char *name;
int age;
} Person;
int main()
{
pid_t child;
int pfd[2];
if (pipe(pfd) == -1)
{
exit(1);
}
child = fork();
if (child < 0)
{
exit(1);
}
if (child == 0)
{
close(pfd[1]);
Person *arr = malloc(10 * sizeof(Person));
if (arr == NULL)
{
exit(1);
}
read(pfd[0], arr, 10 * sizeof(Person));
close(pfd[0]);
printf("child process read:\n");
for (int i = 0; i < 10; ++i)
{
printf("%s %d\n", arr[i].name, arr[i].age);
}
for (int i = 0; i < 10; ++i)
{
free(arr[i].name);
}
free(arr);
exit(0);
}
else
{
Person *arr = malloc(10 * sizeof(Person));
if (arr == NULL)
{
exit(1);
}
for (int i = 0; i < 10; ++i)
{
char *name = malloc(8 * sizeof(char));
if (name == NULL)
{
exit(1);
}
sprintf(name, "%s%d", "Person", i);
arr[i].name = malloc(8 * sizeof(char));
if (arr[i].name == NULL)
{
exit(1);
}
strcpy(arr[i].name, name);
arr[i].age = i;
free(name);
}
printf("array to be written:\n");
for (int i = 0; i < 10; ++i)
{
printf("%s %d\n", arr[i].name, arr[i].age);
}
close(pfd[0]);
write(pfd[1], arr, 10 * sizeof(Person));
close(pfd[1]);
for (int i = 0; i < 10; ++i)
{
free(arr[i].name);
}
free(arr);
printf("parent process done\n");
wait(NULL);
}
}
The output is:
array to be written:
Person0 0
Person1 1
Person2 2
Person3 3
Person4 4
Person5 5
Person6 6
Person7 7
Person8 8
Person9 9
parent process done
child process read:
0
1
2
3
4
5
6
7
8
9
free(): invalid pointer
With valgrind reporting loads of errors (as expected after this output).
I found similiar looking questions, but none of the answers seemed to help.
EDIT:
Thanks to the answer I now understand that the problem is with the dynamically allocated string and only the mallocing process can access it, but the real program in which I encountered this problem has been populated (kind of) like this, as in it already uses these dinamically allocated strings.
Is there a way to pass the strings like this, or do I have to solve it somehow with new char[N] arrays?
The memory you allocate with malloc and the pointer it returns are only valid in the process you do the call to malloc.
When you write the structure through the pipe you only write the (current process unique) pointer, not the memory it points to.
The quick and simple solution is to use an actual array instead:
typedef struct Person
{
char name[10];
int age;
} Person;
What you've stumbled upon is commonly solved using what's known as "serialization," which allows you to reliably send and receive data over a wire (pipe, network socket, file, etc). A popular serialization format is JSON, for its wide support and easy readability, but there's nothing stopping you from creating your own serialization format, and just using that!
A common way to pack binary data reliably is to use a header-payload format, where the header contains information about what kind of data is in the payload, and also how long the payload is. From there, it's as simple as reading in a fixed size header, parsing it, then reading the payload on the receiving end.
Something like this may work for you:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct simple_header {
char kind; /* I arbitrarily chose a char, which could use something like 's' for string, 'i' for int, etc. Commonly you will see an enum used here */
int length; /* You could use a negative length to indicate errors of some kind, or just use a simple size_t */
};
struct simple_payload {
unsigned char *data;
};
int serialize_string(int fd, const char *payload) {
// Automatically find the size, for convenience
size_t length = strlen(payload);
// Set aside a header and populate it
struct simple_header header;
header.kind = 's';
header.length = (int) length; // This could be checked better, but also just a simple example
// Send the header over the wire, doing minimal error checking
int ret = write(fd, &header, sizeof(header));
if(ret < 0) return ret;
// Send the payload
ret = write(fd, payload, length);
return ret;
}
int deserialize(int fd, struct simple_payload *destination) {
struct simple_header received_header;
int ret = read(fd, &received_header, sizeof(received_header));
if(ret < 0) return ret;
// This solution totally ignores endianness, which you will need to consider if sending and receiving on different computers
// Always work with zeroed buffers when you can, leave room for NULL bytes
destination->data = calloc(received_header.length + 1, 1);
ret = read(fd, destination->data, received_header.length);
if(ret < 0) {
free(destination->data);
return ret;
}
switch(received_header.kind) {
case 's':
/* do something special for strings */
;
default:
return -1; /* unsupported format */
}
return ret;
}
If this is anything more than a pet project, though, I'd recommend looking into serialization formats and their libraries (header-only will be easiest to integrate). With serialization, the devil really is in the details, the unhandled errors and endianness considerations can lead to data corruption, so if you value the data you're sending, please use a library! My included example does not cover:
when the header lies about payload length
payloads that exceed the length of whats in the header
Failed reads/writes, leading you to think you're reading a header when actually you're reading a payload
Error detection/correction (CRC, Reed-Solomon etc)
Struct alignment issues (packed vs unpacked)
EDIT: I did use free(), ignore the title.
The gist is that every time malloc() is called, the address 0x8403620
is returned, which I found out using Gdb.
tellers[i] = create_teller(0, i, NULL);
I first use malloc() on line 72 to create 3 teller structures. The first addressed returned, visible through Gdb, is 0x84003620. The second is
0x84033a0, the third 0x84034e0. Everything seems fine.
clients[i] = create_client(0, i, -1, -1);
Then I use malloc() on line 77 with the create_client() function to
create 100 clients. The first address, assigned to client[0], is ...
0x8403620. The same as tellers[0]. It gets worse. The next address
returned from malloc() is 0x8403620 again for when i = 1, and so
on for i = 3, 4, ..., 99.
It isn't inherently the create_client() or the create_teller() functions, but
instead the malloc() function itself.
This is simply a very odd situation.
Now, I'd like to ask: Am I using malloc() wrong? Or is my version of malloc() bugged and should I somehow reinstall whatever it is? It's most likely my code since it works for creating the tellers, just not for the clients.
Here is the full code:
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <assert.h>
typedef struct teller teller_t;
typedef struct client client_t;
teller_t * create_teller (pthread_t thread_id, int id, client_t *assigned_client);
client_t * create_client (pthread_t thread_id, int id, int operation, int amount);
void * run_teller (void *arg);
void * run_client (void *arg);
/* types of operations */
#define DEPOSIT 0
#define WITHDRAW 1
#define NUM_TELLERS 3
#define NUM_CLIENTS 100
struct client {
pthread_t thread_id;
int id;
int operation;
int amount;
};
struct teller {
pthread_t thread_id;
int id;
bool available;
client_t *assigned_client;
};
client_t *clients[100];
teller_t *tellers[3];
/* only 2 tellers at a time can access */
sem_t safe;
/* only 1 teller at a time can access */
sem_t manager;
/* amount of tellers available, at most 3 */
sem_t line; /* rename to available? */
/* each teller waiting for a client to be assigned to them */
sem_t wait_for_client[3];
int
main (int argc, char **argv) {
(void) argc;
(void) argv;
srand(time(NULL));
/* This also tells us how many clients have been served */
int client_index = 0;
sem_init(&safe, 0, 2);
sem_init(&manager, 0, 1);
sem_init(&line, 0, 0);
for (int i = 0; i < 3; i++)
sem_init(&wait_for_client[i], 0, 0);
for (int i = 0; i < NUM_TELLERS; i++) {
tellers[i] = create_teller(0, i, NULL);
pthread_create(&tellers[i]->thread_id, NULL, run_teller, (void *) tellers[i]);
}
for (int i = 0; i < NUM_CLIENTS; i++) {
clients[i] = create_client(0, i, -1, -1);
pthread_create(&clients[i]->thread_id, NULL, run_client, (void *) clients[i]);
}
/* DEBUG
for (int i = 0; i < NUM_CLIENTS; i++) {
printf("client %d has id %d\n", i, clients[i]->id);
}
*/
// No threads should get past this point!!!
// ==------------------------------------==
// Should all of this below be handled by the clients instead of main?
while (1) {
if (client_index >= NUM_CLIENTS) {
// TODO:
// tell tellers that there are no more clients
// so they should close, then then close the bank.
break;
}
sem_wait(&line);
for (int i = 0; i < 3; i++) {
if (tellers[i]->available) {
int client_id = clients[client_index]->id;
//printf("client_index = %d\n", client_index); // DEBUG
tellers[i]->assigned_client = clients[client_index++];
tellers[i]->available = false;
printf(
"Client %d goes to Teller %d\n",
client_id,
tellers[i]->id
);
sem_post(&wait_for_client[i]);
break;
}
}
//sem_post(&line); // Is this needed?
}
return EXIT_SUCCESS;
}
teller_t *
create_teller (pthread_t thread_id, int id, client_t *assigned_client) {
teller_t *t = (teller_t *) malloc(sizeof(teller_t));
if (t == NULL) {
printf("ERROR: Unable to allocate teller_t.\n");
exit(EXIT_FAILURE);
}
t->thread_id = thread_id;
t->id = id;
t->available = true;
t->assigned_client = assigned_client;
return t;
}
/* TODO: Malloc returns the same address everytime, fix this */
client_t *
create_client (pthread_t thread_id, int id, int operation, int amount) {
client_t *c = malloc(sizeof(client_t));
if (c == NULL) {
printf("ERROR: Unable to allocate client_t.\n");
exit(EXIT_FAILURE);
}
c->thread_id = thread_id;
c->id = id;
c->operation = operation;
c->amount = amount;
return c;
}
void *
run_teller (void *arg) {
teller_t *t = (teller_t *) arg;
printf("Teller %d is available\n", t->id);
while (1) {
/* tell the line that a teller is available */
sem_post(&line);
/* pass when the line assignes a client to this teller */
sem_wait(&wait_for_client[t->id]);
assert(t->assigned_client != NULL);
if (t->assigned_client->operation == WITHDRAW) {
}
else {
}
}
free(arg);
pthread_cancel(t->thread_id);
return NULL;
}
void *
run_client (void *arg) {
client_t *c = (client_t *) arg;
c->operation = rand() & 1;
printf(
"Client %d waits in line to make a %s\n",
c->id,
((c->operation == DEPOSIT) ? "Deposit" : "Withdraw")
);
free(arg);
pthread_cancel(c->thread_id);
return NULL;
}
Then I use malloc() on line 77 with the create_client() function to create 100 clients.
Not exactly, you create one object, then you spawn a thread that manages that object, run_client() and then repeat. But run_client() basically does nothing except free() your client object! So malloc is totally right returning the same address again, as it is now free memory.
It just happens that your client threads are faster than your main one. Your problem here is that you are freeing the objects from secondary threads while leaving the dangling pointers in the global pointer array. If you use that array for debugging purposes, then nothing is actually wrong here, but if you want to use the client objects somewhen in the future, then you should not free your clients in the first place.
I'm trying to implement a shuffling technique based on a random time so that it is possible for one message to overtake the next message and becomes shuffled. I have an original message that is separated into chunks per thread. Each thread sleeps and prints out the chunk afterwards.
original_message > grab a slice of original_message > deal with it in another thread > grab another slice > deal with it in another NEW thread while the other thread may/may not be running > and repeat until all slice is taken.
However, sometimes the output is the same and that they do not output the after the sleep one by one e.g. the one with 0 seconds sleep comes out first.... How can I create a new thread with its own stack so that it does not share those variables. I'm not sure mutex lock will suffice because it may affect the sleep timings. What can I do to fix it?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
struct argsparam {
int sleep;
char split_message[5];
};
void* sleep_shuffle(void *arguments){
struct argsparam *args = arguments;
sleep(args->sleep);
printf("shuffled message: %s\n", args->split_message);
}
int main(){
struct argsparam args;
pthread_t sleeppt[4];
time_t t;
srand((unsigned) time(&t));
char original_message[20] = "1234567890abcdefghij";
for(int i=0; i<4; i++){
memcpy(args.split_message , original_message+(i*5), 5);
args.split_message[5] = '\0'; //null terminator
args.sleep = rand() % 5;
printf("message: %s and time: %d\n", args.split_message, args.sleep);
if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)&args) != 0)
printf("Error in creating thread!");
}
for(int i=0; i<4; i++){
pthread_join(sleeppt[i], NULL);
}
}
You're passing the same copy of args to each thread, overwriting the previous value before calling the next thread, so what each thread reads is dependent on the timing of each thread.
Create an array of struct argsparam, one for each thread, then pass in the pointer to the appropriate instance. That way they won't interfere with each other.
int main(){
// an array of args
struct argsparam args[4];
pthread_t sleeppt[4];
time_t t;
srand((unsigned) time(&t));
char original_message[20] = "1234567890abcdefghij";
for(int i=0; i<4; i++){
// assign to a specific array instance for each thread
memcpy(args[i].split_message , original_message+(i*5), 5);
args[i].split_message[5] = '\0'; //null terminator
args[i].sleep = rand() % 5;
printf("message: %s and time: %d\n", args[i].split_message, args[i].sleep);
if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)&args[i]) != 0)
printf("Error in creating thread!");
}
for(int i=0; i<4; i++){
pthread_join(sleeppt[i], NULL);
}
}
Sample output (yours may vary):
message: 12345 and time: 0
message: 67890 and time: 3
message: abcde and time: 2
message: fghij and time: 1
shuffled message: 12345
shuffled message: fghij
shuffled message: abcde
shuffled message: 67890
Also, your split_message array isn't big enough to hold the string you're copying in. You need a total of 6 bytes, for for each of 5 characters and one for the null terminator.
struct argsparam {
int sleep;
char split_message[6];
};
You're "lucky" that you're not stepping on memory you shouldn't due to structure padding.
First, the size of the "split_message[5] of struct argsparam" array is incorrect.
Second, why not use "args" as an array?
--- orig.c 2017-06-14 03:18:25.569775282 +0900
+++ dest.c 2017-06-14 03:16:52.989777492 +0900
## -6,7 +6,7 ##
struct argsparam {
int sleep;
- char split_message[5];
+ char split_message[6];
};
void* sleep_shuffle(void *arguments){
## -16,7 +16,7 ## void* sleep_shuffle(void *arguments){
}
int main(){
- struct argsparam args;
+ struct argsparam args[4];
pthread_t sleeppt[4];
time_t t;
int i;
## -25,12 +25,12 ## int main(){
char original_message[20] = "1234567890abcdefghij";
for(i=0; i<4; i++){
- memcpy(args.split_message , original_message+(i*5), 5);
- args.split_message[5] = '\0'; //null terminator
- args.sleep = rand() % 5;
+ memcpy(args[i].split_message , original_message+(i*5), 5);
+ args[i].split_message[5] = '\0'; //null terminator
+ args[i].sleep = rand() % 5;
- printf("message: %s and time: %d\n", args.split_message, args.sleep);
- if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)&args) != 0)
+ printf("message: %s and time: %d\n", args[i].split_message, args[i].sleep);
+ if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)(args + i)) != 0)
printf("Error in creating thread!");
}
I want to modify some (not all) fields of an array (or structs) in multiple threads, with out blocking the rest of the array as the rest of it is being modified in other threads. How is this achieved? I found some answers, but they are for C++ and I want to do it in C.
Here is the code I got so far:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define ARRAYLENGTH 5
#define TARGET 10000
int target;
typedef struct zstr{
int* array;
int place;
int run;
pthread_mutex_t* locks;
}zstr;
void *countup(void *);
int main(int argc, char** args){
int al;
if(argc>2){
al=atoi(args[1]);
target=atoi(args[2]);
}else{
al=ARRAYLENGTH;
target=TARGET;
}
printf("%d %d\n", al, target);
zstr* t=malloc(sizeof(zstr));
t->array=calloc(al, sizeof(int));
t->locks=calloc(al, sizeof(pthread_mutex_t));
int* rua=calloc(al, sizeof(int));
pthread_t id[4*al];
for(int i=0; i<al; i++)
pthread_mutex_init(&(t->locks[i]), NULL);
for(int j=0; j<4*al; j++){
int st=j%al;
t->run=rua[st]++;
t->place=st;
pthread_create(&id[j], NULL, &countup, t);
}
for(int k=0; k<4*al; k++){
pthread_join(id[k], NULL);
}
for(int u=0; u<al; u++)
printf("%d\n", t->array[u]);
free(rua);
free(t->locks);
free(t->array);
return 0;
}
void *countup(void* table){
zstr* nu=table;
if(!nu->run){
pthread_mutex_lock(nu->locks + nu->place);
}else{
pthread_mutex_trylock(nu->locks + nu->place);
}
while(nu->array[nu->place]<target)
nu->array[nu->place]++;
pthread_mutex_unlock(nu->locks + nu->place);
return NULL;
}
Sometimes this works just fine, but then calculates wrong values and for quiet sort problems (like the default values), it takes super long (strangely it worked once when I handed them in as parameters).
There isn't anything special about part of an array or structure. What matters is that the mutex or other synchronization you apply to a given value is used correctly.
In this case, it seems like you're not checking your locking function results.
The design of the countup function only allows a single thread to ever access the object, running the value all the way up to target before releasing the lock, but you don't check the trylock result.
So what's probably happening is the first thread gets the lock, and subsequent threads on the same mutex call trylock and fail to get the lock, but the code doesn't check the result. Then you get multiple threads incrementing the same value without synchronization. Given all the pointer dereferences the index and increment operations are not guaranteed to be atomic, leading to problems where the values grow well beyond target.
The moral of the story is to check function results and handle errors.
Sorry, don't have enough reputation to comment, yet.
Adding to Brad's comment of not checking the result of pthread_mutex_trylock, there's a misconception that shows many times with Pthreads:
You assume, that pthread_create will start immediately, and receive the values passed (here pointer t to your struct) and it's content read atomically. That is not true. The thread might start any time later and will find the contents, like t->run and t->place already changed by the next iteration of the j-loop in main.
Moreover, you might want to read David Butenhof's book "Programming with Posix Threads" (old, but still a good reference) and check on synchronization and condition variables.
It's not that good style to start that many threads in the first place ;)
As this has come up a few times and might come up again, I have restructured that a bit to issue work_items to the started threads. The code below might be amended by a function, that maps the index into array to always the same area_lock, or by adding a queue to feed the running threads with further work-item...
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
/*
* Macros for default values. To make it more interesting, set:
* ARRAYLENGTH != THREADS
* INCREMENTS != TARGET
* NUM_AREAS != THREADS
* Please note, that NUM_AREAS must be <= ARRAY_LENGTH.
*/
#define ARRAYLENGTH 10
#define TARGET 100
#define INCREMENTS 10
#define NUM_AREAS 2
#define THREADS 5
/* These variables are initialized once in main, then only read... */
int array_len;
int target;
int num_areas;
int threads;
int increments;
/**
* A long array that is going to be equally split into number of areas.
* Each area is covered by a lock. The number of areas do not have to
* equal the length of the array, but must be smaller...
*/
typedef struct shared_array {
int * array;
int num_areas;
pthread_mutex_t * area_locks;
} shared_array;
/**
* A work-item a thread is assigned to upon startup (or later on).
* Then a value of { 0, any } might signal the ending of this thread.
* The thread is working on index within zstr->array, counting up increments
* (or up until the target is reached).
*/
typedef struct work_item {
shared_array * zstr;
int work_on_index;
int increments;
} work_item;
/* Local function declarations */
void * countup(void *);
int main(int argc, char * argv[]) {
int i;
shared_array * zstr;
if (argc == 1) {
array_len = ARRAYLENGTH;
target = TARGET;
num_areas = NUM_AREAS;
threads = THREADS;
increments = INCREMENTS;
} else if (argc == 6) {
array_len = atoi(argv[1]);
target = atoi(argv[2]);
num_areas = atoi(argv[3]);
threads = atoi(argv[4]);
increments = atoi(argv[5]);
} else {
fprintf(stderr, "USAGE: %s len target areas threads increments", argv[0]);
exit(-1);
}
assert(array_len >= num_areas);
zstr = malloc(sizeof (shared_array));
zstr->array = calloc(array_len, sizeof (int));
zstr->num_areas = num_areas;
zstr->area_locks = calloc(num_areas, sizeof (pthread_mutex_t));
for (i = 0; i < num_areas; i++)
pthread_mutex_init(&(zstr->area_locks[i]), NULL);
pthread_t * id = calloc(threads, sizeof (pthread_t));
work_item * work_items = calloc(threads, sizeof (work_item));
for (i = 0; i < threads; i++) {
work_items[i].zstr = zstr;
work_items[i].work_on_index = i % array_len;
work_items[i].increments = increments;
pthread_create(&(id[i]), NULL, &countup, &(work_items[i]));
}
// Let's just do this one work-item.
for (i = 0; i < threads; i++) {
pthread_join(id[i], NULL);
}
printf("Array: ");
for (i = 0; i < array_len; i++)
printf("%d ", zstr->array[i]);
printf("\n");
free(id);
free(work_items);
free(zstr->area_locks);
free(zstr->array);
return 0;
}
void *countup(void* first_work_item) {
work_item * wi = first_work_item;
int inc;
// Extract the information from this work-item.
int idx = wi->work_on_index;
int area = idx % wi->zstr->num_areas;
pthread_mutex_t * lock = &(wi->zstr->area_locks[area]);
pthread_mutex_lock(lock);
for (inc = wi->increments; inc > 0 && wi->zstr->array[idx] < target; inc--)
wi->zstr->array[idx]++;
pthread_mutex_unlock(lock);
return NULL;
}
I am trying to write a program which gets input files and prints included items into the screen by threads. However, last thread doesn't give any output unexpectedly. I couldn't figure out what the problem is. I am waiting for your help.
Each thread gets 3 parameters :inputFile1, inputFile2 and targetBuf(lets say it is sequence number.) Files are sorted, I just simply try to print their union in order. Only positive numbers are printed.
Command line : merge N outfile file1 file2 file3 .... fileN
N is the number of input files.
If I give 2 input files (means 1 thread), it works. If I give 4 files, then 1st thread give output, 2nd one does not. If I give 6 input files, then 1st and 2nd threads give output but 3rd doesn't.
There are two structs in header file. I pasted them below.
//thread information
struct threadInfo{
pthread_t tid;
pthread_attr_t attr;
};
//Beginning thread input
struct beginThreadInput{
FILE **inputFile1, **inputFile2;
int targetBuf;
};
typedef struct threadInfo THREADINFO;
typedef struct beginThreadInput BEGINT_INPUT;
Main File Code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h> //for O_ constants
#include <sys/stat.h>
#include <pthread.h> /* thread library */
#include "merge.h"
int main(int argc, char** argv){
int i, k, N;
/***FILE INITIALIZATION***/
FILE *output;
N = atoi(argv[1]);
output = fopen(argv[2], "w");
FILE **inputFileList = ((FILE**)malloc (N * sizeof(FILE*)));
printf("int N is %d\n", N);
for(i = 0; i<N; i++){
inputFileList[i] = fopen(argv[i + 3], "r");
}
//START THREADS
BEGINT_INPUT **threadInputList = ((BEGINT_INPUT**) malloc ( (N/2)* sizeof(BEGINT_INPUT*)));
THREADINFO **threadInfoList = ((THREADINFO**) malloc ( (N/2) * sizeof(THREADINFO*)));
for(i = 0; i< N/2 ; i++){
threadInputList[i] = (BEGINT_INPUT *) malloc (sizeof(BEGINT_INPUT));
threadInfoList[i] = (THREADINFO *) malloc (sizeof(THREADINFO));
}
pthread_t tid;
pthread_attr_t attr;
for(i = 0, k = 0; i < (N/2); i++){
threadInputList[i]->inputFile1 = &(inputFileList[k]);
threadInputList[i]->inputFile2 = &(inputFileList[++k]);
threadInputList[i]->targetBuf = i;
pthread_attr_init(&(threadInfoList[i]->attr));
pthread_create(&(threadInfoList[i]->tid), &(threadInfoList[i]->attr), runBeginningThreads, (void*)threadInputList[i]);
}
pthread_join((threadInfoList[[(N/2)-1]]->tid), NULL);
for(i = 0; i<N; i++){
fclose(inputFileList[i]);
}
}
void *runBeginningThreads(void *input){
BEGINT_INPUT *myInput = (BEGINT_INPUT *)input;
int first = -1, second = -1, iseof;
printf("Thread number %d\n", myInput->targetBuf);
while((second > -2) && (first > -2)){
//read integer values from files
if(first == -1){
iseof = fscanf(*(myInput->inputFile1), "%d", &first);
if(iseof == EOF){
first = -2; //means end of file
}
else if(first < 0)
first = -1; //means waiting for an integer
}
if(second == -1){
iseof = fscanf(*(myInput->inputFile2), "%d", &second);
if(iseof == EOF){
second = -2;
}
else if(second < 0)
second = -1;
}
//print smaller one
if((first != -1) && (second != -1)){
if(((first < second) || (second == -2)) && (first != -2)){
printf("%d\n", first);
first = -1;
}
else if(second != -2){
printf("%d\n", second);
second = -1;
}
}
}
}
The main problem might be that you only wait for one thread to finish, before you close all files and then exit from the program.
There are a few other extra complications in your program, that could probably be simplified. I mean that you use extra pointer indirection when you don't really need it.
Here's a little simpler version, not using extra pointer-to-pointers where none are needed:
struct beginThreadInput{
FILE *inputFile1, *inputFile2;
int targetBuf;
};
/* ... */
int main(int argc, char** argv){
/* ... */
BEGINT_INPUT *threadInputList = malloc ( (N/2)* sizeof(BEGINT_INPUT));
THREADINFO *threadInfoList = malloc ( (N/2) * sizeof(THREADINFO));
pthread_t tid;
for(i = 0, k = 0; i < (N/2); i++){
threadInputList[i].inputFile1 = inputFileList[k];
threadInputList[i].inputFile2 = inputFileList[++k];
threadInputList[i].targetBuf = i;
pthread_create(&(threadInfoList[i].tid), NULL, runBeginningThreads, &(threadInputList[i]));
}
for(i = 0; i< N/2 ; i++){
pthread_join((threadInfoList[i]->tid), NULL);
}
/* ... */
/* While not strictly needed, you should make it a good
habit of freeing all you allocate */
free(threadInfoList);
free(threadInputList);
return 0; /* You missed this */
}
I solve my question in the following way. As it is seen above, I open the files in main function, then pass them into corresponding threads. I have changed this way and passed the names of the files into threads and open related files in them.
Additionally, while passing files, I was making mistakes, its code is below
threadInputList[i]->inputFile1 = &(inputFileList[k]);
threadInputList[i]->inputFile2 = &(inputFileList[++k]);
This lines are run in a loop. This causes an input sequence like the following ([0,1], [1,2], [2,3], [3,4] ...) becuase I increment k only one time.However, each thread takes two of inputFiles. So the correct code is this:
threadInputList[i]->inputFile1 = &(inputFileList[k++]);
threadInputList[i]->inputFile2 = &(inputFileList[k++]);
I want to point out an issue. I fixed this code for file names, not FILE pointer variables as it is posted there. But to make it clear, I fixed the bug on a code posted there. Thanks for your help, everybody