Reputation: 4888
When I run my code in Mac OS X, it runs fine. However on Linux (Ubuntu), I get the following output:
pmichna@pawel-mac:~/alchemists$ ./alchemists 10
Creating 10 alchemists...
TESTING_ERROR #0
alchemists: malloc.c:2369: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.
make: *** [run] Aborted (core dumped)
What's wrong? My code:
pmichna@pawel-mac:~/alchemists$ cat alchemists.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NUM_APPRENTICES 2
pthread_mutex_t *ingredients_mut;
pthread_mutex_t *pots_mut;
pthread_cond_t *ingredients_cond;
pthread_cond_t *pots_cond;
int *ingredients_count;
long num_of_pots;
long num_of_alchemists;
int *pots_usage;
// if the pot was used by the alchemist on the right: 1
// if on the left: -1
// if not yet used: 0
void lock_ingredients(long ingredients_index, long tid)
{
printf("Alchemist #%ld trying to lock ingredients from bowl #%ld...\n", tid, ingredients_index);
pthread_mutex_lock(&ingredients_mut[ingredients_index]);
if(ingredients_count[ingredients_index]>1)
{
ingredients_count[ingredients_index]--;
printf("Alchemist #%ld took an ingredient from bowl #%ld!\n", tid, ingredients_index);
}
else {
printf("Alchemist #%ld found the bowl #%ld empty. Waiting for the filling apprentice...\n", tid, ingredients_index);
pthread_cond_wait(&ingredients_cond[ingredients_index], &ingredients_mut[ingredients_index]);
}
}
void lock_pot(long pot_index, long tid, int expected_pot_value)
{
printf("Alchemist #%ld trying to lock pot #%ld...\n", tid, pot_index);
pthread_mutex_lock(&pots_mut[pot_index]);
if(pots_usage[pot_index] == 0 || pots_usage[pot_index] == expected_pot_value)
{
pots_usage[pot_index] = expected_pot_value;
printf("Alchemist #%ld locked pot #%ld!\n", tid, pot_index);
} else {
printf("Alchemist #%ld found the pot #%ld dirty. :(\n", tid, pot_index);
pthread_cond_wait(&pots_cond[pot_index], &pots_mut[pot_index]);
printf("Alchemist #%ld now can use the cleaned pot #%ld!\n", tid, pot_index);
}
}
void finish_production(int *gold_produced, long tid, long ingredients_index, long pot_index)
{
(*gold_produced)++;
printf("Alchemist #%ld produced gold!. Current status: %d\n", tid, *gold_produced);
pthread_mutex_unlock(&ingredients_mut[ingredients_index]);
printf("Alchemist #%ld released the bowl #%ld.\n", tid, ingredients_index);
pthread_mutex_unlock(&pots_mut[pot_index]);
printf("Alchemist #%ld released the pot #%ld.\n", tid, pot_index);
}
void alchemist_even_work(long tid, int *gold_produced) // has pot on the left; bowl on the right
{
long pot_index = tid/2 - 1;
long ingredients_index = tid/2;
int expected_pot_value = 1;
lock_ingredients(ingredients_index, tid);
lock_pot(pot_index, tid, expected_pot_value);
finish_production(gold_produced, tid, ingredients_index, pot_index);
}
void alchemist_odd_work(long tid, int *gold_produced) //has pot on the right; bowl on the left
{
long pot_index = tid/2;
long ingredients_index = tid/2;
int expected_pot_value = -1;
lock_ingredients(ingredients_index, tid);
lock_pot(pot_index, tid, expected_pot_value);
finish_production(gold_produced, tid, ingredients_index, pot_index);
}
void alchemist_first_work(long tid, int *gold_produced) // has pot on the left; bowl on the right
{
long pot_index = num_of_alchemists/2 - 1;
long ingredients_index = 0;
int expected_pot_value = 1;
lock_ingredients(ingredients_index, tid);
lock_pot(pot_index, tid, expected_pot_value);
finish_production(gold_produced, tid, ingredients_index, pot_index);
}
void *alchemist_work(void *id)
{
long tid = (long)id;
int sleep_time, gold_produced = 0;
printf("Alchemist #%ld started\n", tid);
while(gold_produced < 10)
{
//thinking
srand((int)tid);
sleep_time = rand()%5+1;
printf("Alchemist #%ld going to think %d seconds...\n", tid, sleep_time);
sleep(sleep_time);
printf("Alchemist #%ld is going to try to make gold!\n", tid);
if(tid == 0)
{
alchemist_first_work(tid, &gold_produced);
} else if(tid%2 == 0)
{
alchemist_even_work(tid, &gold_produced);
} else {
alchemist_odd_work(tid, &gold_produced);
}
}
printf("Alchemist #%ld going to lunch.\n", tid);
pthread_exit(NULL);
}
void *apprentice_work(void *id)
{
long tid = (long)id;
long i;
int sleep_time;
switch (tid) {
case 0:
printf("Cleaning apprentice started.\n");
break;
case 1:
printf("Ingredients apprentice started.\n");
break;
}
srand((int)tid);
while (1)
{
sleep_time = rand()%5+1;
switch (tid) {
case 0: // cleaner of pots
printf("Cleaning apprentice going to sleep %d seconds...\n", sleep_time);
sleep(sleep_time);
printf("Cleaning apprentice is now going to clean all the pots\n");
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_lock(&pots_mut[i]);
printf("Apprentice cleaning pot #%ld\n", i);
pots_usage[i] = 0;
pthread_cond_signal(&pots_cond[i]);
pthread_mutex_unlock(&pots_mut[i]);
}
break;
case 1: // ingredients
printf("Ingredients apprentice going to sleep %d seconds...\n", sleep_time);
sleep(sleep_time);
printf("Ingredients apprentice going to fill all the pots...\n");
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_lock(&ingredients_mut[i]);
printf("Apprentice filling bowl #%ld\n", i);
ingredients_count[i] = 5;
pthread_cond_broadcast(&ingredients_cond[i]);
pthread_mutex_unlock(&ingredients_mut[i]);
}
break;
}
}
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
long i;
num_of_alchemists = atol(argv[1]);;
if(num_of_alchemists%2 == 1)
{
fprintf(stderr, "Only even number of alchemists possible!\n");
exit(1);
}
num_of_pots = num_of_alchemists/2; // this variable used alse as number of bowls with ingredients
pthread_t threads_alchemists[num_of_alchemists];
pthread_t threads_apprentices[NUM_APPRENTICES];
pthread_attr_t attr;
ingredients_mut = malloc(num_of_pots*sizeof(pthread_mutex_t));
pots_mut = malloc(num_of_pots*sizeof(pthread_mutex_t));
ingredients_cond = malloc(num_of_pots*sizeof(pthread_cond_t));
ingredients_count = malloc(num_of_pots*sizeof(int));
pots_usage = malloc(num_of_pots*sizeof(int));
pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_init(&ingredients_mut[i], NULL);
pthread_mutex_init(&pots_mut[i], NULL);
pthread_cond_init(&ingredients_cond[i], NULL);
pthread_cond_init(&pots_cond[i], NULL);
ingredients_count[i] = 5;
pots_usage[i] = 0;
}
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
printf("Creating %ld alchemists...\n", num_of_alchemists);
for(i = 0; i < num_of_alchemists; i++)
{
printf("TESTING_ERROR #%ld\n", i);
pthread_create(&threads_alchemists[i], &attr, alchemist_work, (void *)i);
}
for(i = 0; i < NUM_APPRENTICES; i++)
{
pthread_create(&threads_apprentices[i], &attr, apprentice_work, (void *)i);
}
/* Wait for all threads to complete */
for (i = 0; i < num_of_alchemists; i++)
{
pthread_join(threads_alchemists[i], NULL);
}
printf ("Main(): Waited and joined with %ld alchemist threads.\n", num_of_alchemists);
// Clean up
pthread_attr_destroy(&attr);
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_destroy(&ingredients_mut[i]);
pthread_mutex_destroy(&pots_mut[i]);
pthread_cond_destroy(&ingredients_cond[i]);
pthread_cond_destroy(&pots_cond[i]);
}
free(ingredients_mut);
free(ingredients_count);
free(ingredients_cond);
free(pots_mut);
free(pots_usage);
free(pots_cond);
return 0;
}
UPDATE:
So used valgrind and the program runs, spitting out errors from time to time. What I noticed, they mostly concern conditional variables. One example which appeared just at the beginning:
pmichna@pawel-mac:~/alchemists$ valgrind --tool=memcheck ./alchemists 4
==3552== Memcheck, a memory error detector
==3552== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3552== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3552== Command: ./alchemists 4
==3552==
==3552== Invalid write of size 4
==3552== at 0x4E3FA32: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:33)
==3552== by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552== Address 0x541a2f8 is 8 bytes after a block of size 80 alloc'd
==3552== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552== by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552==
==3552== Invalid write of size 8
==3552== at 0x4E3FA41: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:40)
==3552== by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552== Address 0x541a2f0 is 0 bytes after a block of size 80 alloc'd
==3552== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552== by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552==
==3552== Invalid write of size 4
==3552== at 0x4E3FA57: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:42)
==3552== by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552== Address 0x541a2fc is 12 bytes after a block of size 80 alloc'd
==3552== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552== by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552==
Creating 4 alchemists...
TESTING_ERROR #0
TESTING_ERROR #1
Alchemist #0 started
TESTING_ERROR #2
TESTING_ERROR #3
Alchemist #1 started
Alchemist #1 going to think 4 seconds...
Alchemist #2 started
Alchemist #2 going to think 1 seconds...
Alchemist #3 started
Does it mean I use the pthread_cond_init
function not correctly? How would I fix this?
Upvotes: 0
Views: 996
Reputation: 229344
pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));
You're allocating this with a size of pthread_mutex_t
. But you want a pthread_cond_t
. Always do malloc on this form:
pots_cond = malloc(num_of_pots*sizeof *pots_cond);
The comment here is also lying:
/* Wait for all threads to complete */
for (i = 0; i < num_of_alchemists; i++)
{
pthread_join(threads_alchemists[i], NULL);
}
You only wait for threads_alchemists
, not all your &threads_apprentices
. When all the alchemists are done, you will continue to free() variables that the apprentice threads are using.
Upvotes: 3
Reputation: 16728
You're calling pthread_cond_init
on elements of pots_cond
:
pthread_cond_init(&pots_cond[i], NULL);
But the size used when allocating pots_cond
was the size of pthread_mutex_t
(as opposed to pthread_cond_t
):
pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));
On my OS X system sizeof(pthread_cond_t)
is 48, but sizeof(pthread_mutex_t)
is 64, so this won't cause a problem (as you're actually allocating more memory than you need.)
However, when I fired up an Ubuntu Linux VM to check, it turns out sizeof(pthread_mutex_t)
is only 40, in which case your code allocates less memory than you need and you will overrun pots_cond
.
Upvotes: 4
Reputation: 78973
casting integers to void*
and then back as arguments to the thread functions is fundamentally wrong. If you are on a machine where long
and void*
have different width, you can end up with wrong things in the upper half of a word.
Use a table of long[something]
instead and pass each thread the address of such a table entry.
Upvotes: 1