c - Why does my producer - consumer pattern have unexpected output? -
in this excercise (producer - consumer), 3 producers 1 thread each , produce prime numbers. when run program, first prime number consumed not first produces, don't expected output. can please me find , correct bug?
here's main file pattern:
#include <stdio.h> #include "oslab_lowlevel_h.h" int nextprime( int ); #define fifo_size 10 /* declare structure hold producer's starting value, * , integer producer-number (producer 1, 2 or 3). */ struct prod { int startvalue; int id; }; unsigned int stack1[0x400]; /* stack thread 1 */ unsigned int stack2[0x400]; /* stack thread 2 */ unsigned int stack3[0x400]; /* stack thread 3 */ unsigned int stack4[0x400]; /* stack thread 4 */ unsigned int stack5[0x400]; /* stack thread 5 */ /* declare variables first-in-first-out queue */ int fifo[fifo_size]; /* array holding fifo queue data. */ int rdaddr; /* next unread entry when reading queue. */ int wraddr; /* next free entry when writing queue. */ /* declaration of semaphore variables. * * sorry lack of comments, part of purpose of lab * should find things out reading actual code. */ int rdmutex = 1; int wrmutex = 1; int nrempty = fifo_size; int nrfull = 0; /* * fatal_error * * print message, stop execution. * function never returns; after printing * message, enters infinite loop. */ void fatal_error( char * msg) { printf( "\nfatal error: %s\n", msg ); while( 1 ); } /* * sleep * * delay execution keeping cpu busy while, * counting down zero. */ void sleep (int n) { while (n--); } /* * signal * * semaphore operation: add semaphore, * possibly allowing other threads continue. */ void signal( int *sem ) { /* must disable interrupts, since operation * *sem = *sem + 1 * require several machine instructions on nios2. * if have timer-interrupt , thread-switch * somewhere in middle of machine instructions, * semaphore updated twice, or not @ all, or * in other erroneous way. */ oslab_begin_critical_region(); *sem = *sem + 1; oslab_end_critical_region(); } /* * wait * * sempahore operation: check semaphore, , * wait if semaphore value 0 or less. */ void wait( int *sem ) { /* disable interrupts. */ oslab_begin_critical_region(); while ( *sem <= 0 ) { /* if should wait, enable interrupts again. */ oslab_end_critical_region(); // oslab_yield(); /* perhaps should yield here? */ /* disable interrupts again before next iteration in loop. */ oslab_begin_critical_region(); } /* have waited long enough - semaphore-value * greater zero. decrease it. */ *sem = *sem - 1; /* enable interrupts again. */ oslab_end_critical_region(); } /* * putfifo * * insert integer fifo queue. */ void putfifo( int tal ) { // wait (&nrempty); /* wait nrempty? */ // wait (&wrmutex); /* wait wrmutex? */ fifo[wraddr] = tal; /* write fifo array. */ // printf("\nputfifo: %d ", tal); /* optional debug output */ // printf("\nwraddr = %d ", wraddr); /* optional debug output. */ wraddr = wraddr + 1; /* increase index fifo array, point next free position. */ /* wrap around index, if has reached end of array. */ if (wraddr == fifo_size ) wraddr = 0; // signal (&wrmutex); /* signal wrmutex? */ // signal (&nrfull); /* signal nrfull? */ } /* * getfifo * * extract next integer fifo queue. */ int getfifo( void ) { int retval; /* declare temporary return value. */ // wait (&nrfull); /* wait nrfull? */ // wait (&rdmutex); /* wait rdmutex? */ retval = fifo[rdaddr]; /* value fifo array. */ // printf("\ngetfifo: %d ", retval); /* optional debug output */ // printf("\nrdaddr = %d ", rdaddr); /* optional debug output */ rdaddr = rdaddr + 1; /* increase index fifo array, point next free position. */ /* wrap around index, if has reached end of array. */ if (rdaddr == fifo_size ) rdaddr = 0; // signal (&rdmutex); /* signal rdmutex? */ // signal (&nrempty); /* signal nrempty? */ return (retval); /* return value fetched fifo. */ } /* * nextprime * * return first prime number larger integer * given parameter. integer must positive. * * *** nextprime outside focus of assignment. *** * definition of nextprime can found @ end of file. * short declaration here required compiler. */ int nextprime( int ); void producer( struct prod * prodstruct ) { int next; /* hold prime produced. */ int prodid; /* tells whether producer 1, 2 or 3. */ next = prodstruct -> startvalue; /* starting value parameter. */ prodid = prodstruct -> id;/* producer number parameter. */ while( 1 ) /* loop forever. */ { next = nextprime (next);/* produce new prime. */ printf("\nnext prime producer %d %d",prodid,next); /* informational output. */ putfifo(next); /* write prime fifo. */ // oslab_yield(); /* perhaps should yield here? */ } } void consumer( int * tal ) { int next; /* hold prime consume. */ int consid = *tal; /* tells whether consumer 1 or 2. */ while( 1 ) /* loop forever. */ { next = getfifo(); /* newly produced prime fifo. */ printf("\nconsumer %d gets prime %d ",consid, next); /* informational output. */ sleep(2000); /* symbolic work. */ // oslab_yield(); /* perhaps should yield here? */ } } int main( void ) { int new_thread_id; /* thread id variable. */ struct prod prod1, prod2, prod3; /* producer starting-values. */ int cons1, cons2; /* consumer starting-values. */ rdaddr = 0; /* fifo initialization. */ wraddr = 0; /* fifo initialization. */ printf("\nsystem starting..."); prod1.startvalue = 2000; prod1.id = 1; prod2.startvalue = 5000; prod2.id = 2; prod3.startvalue = 8000; prod3.id = 3; cons1 = 1; cons2 = 2; new_thread_id = oslab_create_thread((void *)producer, &prod1, &(stack1[0x3ff])); if( new_thread_id < 0 ) fatal_error( "cannot start producer 1" ); printf("\nproducer %d created thread-id %d", prod1.id, new_thread_id); new_thread_id = oslab_create_thread((void *)producer, &prod2, &(stack2[0x3ff])); if( new_thread_id < 0 ) fatal_error( "cannot start producer 2" ); printf("\nproducer %d created thread-id %d", prod2.id, new_thread_id); new_thread_id = oslab_create_thread((void *)producer, &prod3, &(stack3[0x3ff])); if( new_thread_id < 0 ) fatal_error( "cannot start producer 3" ); printf("\nproducer %d created thread-id %d", prod3.id, new_thread_id); new_thread_id = oslab_create_thread((void *)consumer, &cons1, &(stack4[0x3ff])); if( new_thread_id < 0 ) fatal_error( "cannot start consumer 1" ); printf("\nconsumer %d created thread-id %d", cons1, new_thread_id); new_thread_id = oslab_create_thread((void *)consumer, &cons2, &(stack5[0x3ff])); if( new_thread_id < 0 ) fatal_error( "cannot start consumer 2" ); printf("\nconsumer %d created thread-id %d", cons2, new_thread_id); oslab_idle(); /* must called here! */ } /* * nextprime * * return first prime number larger integer * given parameter. integer must positive. */ #define prime_false 0 /* constant readability. */ #define prime_true 1 /* constant readability. */ int nextprime( int inval ) { int perhapsprime; /* holds tentative prime while check it. */ int testfactor; /* holds various factors test perhapsprime. */ int found; /* flag, false until find prime. */ if (inval < 3 ) /* initial sanity check of parameter. */ { if(inval <= 0) return(1); /* return 1 0 or negative input. */ if(inval == 1) return(2); /* easy special case. */ if(inval == 2) return(3); /* easy special case. */ } else { /* testing number primeness pointless, since * numbers divisible 2. therefore, make sure * perhapsprime larger parameter, , odd. */ perhapsprime = ( inval + 1 ) | 1 ; } /* while prime not found, loop. */ for( found = prime_false; found != prime_true; perhapsprime += 2 ) { /* check factors 3 perhapsprime/2. */ for( testfactor = 3; testfactor <= (perhapsprime >> 1) + 1; testfactor += 1 ) { found = prime_true; /* assume find prime. */ if( (perhapsprime % testfactor) == 0 ) /* if testfactor divides perhapsprime... */ { found = prime_false; /* ...then, perhapsprime non-prime. */ goto check_next_prime; /* break inner loop, go test new perhapsprime. */ } } check_next_prime:; /* label used break inner loop. */ if( found == prime_true ) /* if loop ended normally, found prime. */ { return( perhapsprime ); /* return prime found. */ } } return( perhapsprime ); /* when loop ends, perhapsprime real prime. */ }
the rest of files available here.
when run code expected output producers don't expected output consumers:
system starting... producer 1 created thread-id 1 producer 2 created thread-id 2 producer 3 created thread-id 3 consumer 1 created thread-id 4 consumer 2 created thread-id 5 #### thread yielded after using 1 tick. performing thread-switch number 1. system has been running 1 ticks. switching thread-id 0 thread-id 1. next prime producer 1 2003 putfifo: 2003 wraddr = 0 next prime producer 1 2011 putfifo: 2011 wraddr = 1 next prime producer 1 2017 putfifo: 2017 wraddr = 2 next prime producer 1 2027 putfifo: 2027 wraddr = 3 next prime producer 1 2029 putfifo: 2029 wraddr = 4 next prime producer 1 2039 putfifo: 2039 wraddr = 5 next prime producer 1 2053 putfifo: 2053 wraddr = 6 next prime producer 1 2063 putfifo: 2063 wraddr = 7 next prime producer 1 2069 putfifo: 2069 wraddr = 8 next prime producer 1 2081 putfifo: 2081 wraddr = 9 next prime producer 1 2083 putfifo: 2083 wraddr = 0 next prime producer 1 2087 putfifo: 2087 wraddr = 1 next prime producer 1 2089 putfifo: 2089 wraddr = 2 next prime producer 1 2099 putfifo: 2099 wraddr = 3 next prime producer 1 2111 putfifo: 2111 wraddr = 4 next prime producer 1 2113 putfifo: 2113 wraddr = 5 next prime producer 1 2129 putfifo: 2129 wraddr = 6 next prime producer 1 2131 putfifo: 2131 wraddr = 7 next prime producer 1 2137 putfifo: 2137 wraddr = 8 next prime producer 1 2141 putfifo: 2141 wraddr = 9 next prime producer 1 2143 putfifo: 2143 wraddr = 0 next prime producer 1 2153 putfifo: 2153 wraddr = 1 performing thread-switch number 2. system has been running 101 ticks. switching thread-id 1 thread-id 2. next prime producer 2 5003 putfifo: 5003 wraddr = 2 next prime producer 2 5009 putfifo: 5009 wraddr = 3 next prime producer 2 5011 putfifo: 5011 wraddr = 4 next prime producer 2 5021 putfifo: 5021 wraddr = 5 next prime producer 2 5023 putfifo: 5023 wraddr = 6 next prime producer 2 5039 putfifo: 5039 wraddr = 7 next prime producer 2 5051 putfifo: 5051 wraddr = 8 next prime producer 2 5059 putfifo: 5059 wraddr = 9 next prime producer 2 5077 putfifo: 5077 wraddr = 0 next prime producer 2 5081 putfifo: 5081 wraddr = 1 performing thread-switch number 3. system has been running 201 ticks. switching thread-id 2 thread-id 3. next prime producer 3 8009 putfifo: 8009 wraddr = 2 next prime producer 3 8011 putfifo: 8011 wraddr = 3 next prime producer 3 8017 putfifo: 8017 wraddr = 4 next prime producer 3 8039 putfifo: 8039 wraddr = 5 next prime producer 3 8053 putfifo: 8053 wraddr = 6 next prime producer 3 8059 putfifo: 8059 wraddr = 7 performing thread-switch number 4. system has been running 301 ticks. switching thread-id 3 thread-id 4. getfifo: 5077 rdaddr = 0 consumer 1 gets prime 5077 getfifo: 5081 rdaddr = 1 consumer 1 gets prime 5081 getfifo: 8009 rdaddr = 2 consumer 1 gets prime 8009 getfifo: 8011 rdaddr = 3 consumer 1 gets prime 8011 getfifo: 8017 rdaddr = 4 consumer 1 gets prime 8017 getfifo: 8039 rdaddr = 5 consumer 1 gets prime 8039 getfifo: 8053 rdaddr = 6 consumer 1 gets prime 8053 getfifo: 8059 rdaddr = 7 consumer 1 gets prime 8059 getfifo: 5051 rdaddr = 8 consumer 1 gets prime 5051 getfifo: 5059 rdaddr = 9 consumer 1 gets prime 5059 getfifo: 5077 rdaddr = 0 consumer 1 gets prime 5077 getfifo: 5081
could please tell me why first 30 primes overwritten when following specification , removing comments code activate instructor has prepared study? i've been unable complete exercise months since i'm not getting help. first 30 primes myseriously overwritten , program should not changed (it homework). asked instructor , didn't, said i'm using newer versions of software. try older versions of softare doesn't seem solution.
update
the strategy can think of start using debugger , inspect fifo adt during execution. don't have experience using gdb please me if can.
this code can't possibly expected work correctly if don't uncomment wait , signal calls inside putfifo , getfifo functions. if appear work under circumstances on computer, pure luck.
first off, if 1 or more of producer threads fill fifo buffer before switching 1 of consumer threads, of data going lost.
you can see in example output. producer threads have generated 38 values before first consumer thread gets chance run. , because buffer size 10, first value consumer going read 31st value produced (i.e. last value written wraddr 0).
╔═══════╦════════╦═══════╗ ║ count ║ wraddr ║ value ║ ╠═══════╬════════╬═══════╣ ║ .. ║ .. ║ .. ║ ║ 29 ║ 8 ║ 5051 ║ ║ 30 ║ 9 ║ 5059 ║ ║ 31 ║ 0 ║ 5077 ║ <- consumer starts here ║ 32 ║ 1 ║ 5081 ║ ║ 33 ║ 2 ║ 8009 ║ ║ 34 ║ 3 ║ 8011 ║ ║ 35 ║ 4 ║ 8017 ║ ║ 36 ║ 5 ║ 8039 ║ ║ 37 ║ 6 ║ 8053 ║ ║ 38 ║ 7 ║ 8059 ║ ╚═══════╩════════╩═══════╝
morever, if consumer threads read more data available in fifo buffer before switching 1 of producer threads, going end reading same values more once. again can see example output. consumer thread reads items 31 38, wraps around items 29 , 30 (the last values @ wraddr 8 , 9), before repeating item 31 again.
this isn't worst thing happen though. in true preemptive multithreading system, producer thread preempted halfway through putfifo function. imagine 1 of producer threads writing fifo buffer when wraddr 9. let's executes these 2 lines.
fifo[wraddr] = tal; /* write fifo array. */ wraddr = wraddr + 1; /* increase index fifo array,
at point wraddr 10, before function has chance check overflow (and wrap index 0), thread gets preempted 1 of producer threads. , since wraddr 10, new producer going writing past end of buffer, potentially causing app crash.
if survives, wraddr incremented again (becoming 11), still won't wrap zero, because overflow check expecting exact match fifo_size. if doesn't crash immediately, it's sure crash @ point, because wraddr continue getting bigger , bigger, overwriting more , more memory.
the bottom line if want code work going have add synchronisation calls.
Comments
Post a Comment