Why does my manufacturer - consumer template have an unexpected result?

In this exercise (producer - consumer), 3 producers - on one thread and produce prime numbers. But when I run the program, the first prime that is consumed is not the first that it produces, so I do not get the expected result. Could you help me find and fix the error?

Here is the main template file:

#include <stdio.h> #include "oslab_lowlevel_h.h" int NextPrime( int ); #define FIFO_SIZE 10 /* Declare a structure to hold a producer starting value, * and an integer for the Producer-number (Producer 1, 2 or 3). */ struct Prod { int startvalue; int id; }; unsigned int stack1[0x400]; /* Stack for thread 1 */ unsigned int stack2[0x400]; /* Stack for thread 2 */ unsigned int stack3[0x400]; /* Stack for thread 3 */ unsigned int stack4[0x400]; /* Stack for thread 4 */ unsigned int stack5[0x400]; /* Stack for thread 5 */ /* Declare variables for the First-In-First-Out Queue */ int Fifo[FIFO_SIZE]; /* Array holding FIFO queue data. */ int rdaddr; /* Next unread entry when reading from queue. */ int wraddr; /* Next free entry when writing into queue. */ /* Declaration of semaphore variables. * * Sorry for the lack of comments, but part of the purpose of the lab * is that you should find things out by reading the actual code. */ int rdmutex = 1; int wrmutex = 1; int nrempty = FIFO_SIZE; int nrfull = 0; /* * fatal_error * * Print a message, then stop execution. * This function never returns; after printing * the message, it enters an infinite loop. */ void fatal_error( char * msg) { printf( "\nFatal error: %s\n", msg ); while( 1 ); } /* * Sleep * * Delay execution by keeping the CPU busy for a while, * counting down to zero. */ void Sleep (int n) { while (n--); } /* * Signal * * Semaphore operation: add to semaphore, * possibly allowing other threads to continue. */ void Signal( int *sem ) { /* We must disable interrupts, since the operation * *sem = *sem + 1 * will require several machine instructions on Nios2. * If we have a timer-interrupt and a thread-switch * somewhere in the middle of those machine instructions, * the semaphore will be updated twice, or not at all, or * in some other erroneous way. */ oslab_begin_critical_region(); *sem = *sem + 1; oslab_end_critical_region(); } /* * Wait * * Sempahore operation: check semaphore, and * wait if the semaphore value is zero or less. */ void Wait( int *sem ) { /* Disable interrupts. */ oslab_begin_critical_region(); while ( *sem <= 0 ) { /* If we should wait, enable interrupts again. */ oslab_end_critical_region(); // oslab_yield(); /* Perhaps we should yield here? */ /* Disable interrupts again before next iteration in loop. */ oslab_begin_critical_region(); } /* We have waited long enough - the semaphore-value is now * greater than zero. Decrease it. */ *sem = *sem - 1; /* Enable interrupts again. */ oslab_end_critical_region(); } /* * PutFifo * * Insert an integer into the FIFO queue. */ void PutFifo( int tal ) { // Wait (&nrempty); /* Wait for nrempty? */ // Wait (&wrmutex); /* Wait for wrmutex? */ Fifo[wraddr] = tal; /* Write to FIFO array. */ // printf("\nPutFifo: %d ", tal); /* Optional debug output */ // printf("\nwraddr = %d ", wraddr); /* Optional debug output. */ wraddr = wraddr + 1; /* Increase index into FIFO array, to point to the next free position. */ /* Wrap around the index, if it has reached the end of the array. */ if (wraddr == FIFO_SIZE ) wraddr = 0; // Signal (&wrmutex); /* Signal wrmutex? */ // Signal (&nrfull); /* Signal nrfull? */ } /* * GetFifo * * Extract the next integer from the FIFO queue. */ int GetFifo( void ) { int retval; /* Declare temporary for return value. */ // Wait (&nrfull); /* Wait for nrfull? */ // Wait (&rdmutex); /* Wait for rdmutex? */ retval = Fifo[rdaddr]; /* Get value from FIFO array. */ // printf("\nGetFifo: %d ", retval); /* Optional debug output */ // printf("\nrdaddr = %d ", rdaddr); /* Optional debug output */ rdaddr = rdaddr + 1; /* Increase index into FIFO array, to point to the next free position. */ /* Wrap around the index, if it has reached the end of the array. */ if (rdaddr == FIFO_SIZE ) rdaddr = 0; // Signal (&rdmutex); /* Signal rdmutex? */ // Signal (&nrempty); /* Signal nrempty? */ return (retval); /* Return value fetched from FIFO. */ } /* * NextPrime * * Return the first prime number larger than the integer * given as a parameter. The integer must be positive. * * *** NextPrime is outside the focus of this assignment. *** * The definition of NextPrime can be found at the end of this file. * The short declaration here is required by the compiler. */ int NextPrime( int ); void Producer( struct Prod * prodstruct ) { int next; /* Will hold the prime we just produced. */ int prodid; /* Tells whether we are producer 1, 2 or 3. */ next = prodstruct -> startvalue; /* Get starting value from parameter. */ prodid = prodstruct -> id;/* Get producer number from parameter. */ while( 1 ) /* Loop forever. */ { next = NextPrime (next);/* Produce a new prime. */ printf("\nNext Prime from producer %d is %d",prodid,next); /* Informational output. */ PutFifo(next); /* Write prime into FIFO. */ // oslab_yield(); /* Perhaps we should yield here? */ } } void Consumer( int * tal ) { int next; /* Will hold the prime we are to consume. */ int consid = *tal; /* Tells whether we are consumer 1 or 2. */ while( 1 ) /* Loop forever. */ { next = GetFifo(); /* Get a newly produced prime from the FIFO. */ printf("\nConsumer %d gets Prime %d ",consid, next); /* Informational output. */ Sleep(2000); /* Symbolic work. */ // oslab_yield(); /* Perhaps we 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 is created with 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 is created with 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 is created with 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 is created with 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 is created with thread-ID %d", cons2, new_thread_id); oslab_idle(); /* Must be called here! */ } /* * NextPrime * * Return the first prime number larger than the integer * given as a parameter. The integer must be positive. */ #define PRIME_FALSE 0 /* Constant to help readability. */ #define PRIME_TRUE 1 /* Constant to help readability. */ int NextPrime( int inval ) { int perhapsprime; /* Holds a tentative prime while we check it. */ int testfactor; /* Holds various factors for which we test perhapsprime. */ int found; /* Flag, false until we find a prime. */ if (inval < 3 ) /* Initial sanity check of parameter. */ { if(inval <= 0) return(1); /* Return 1 for zero or negative input. */ if(inval == 1) return(2); /* Easy special case. */ if(inval == 2) return(3); /* Easy special case. */ } else { /* Testing an even number for primeness is pointless, since * all even numbers are divisible by 2. Therefore, we make sure * that perhapsprime is larger than the parameter, and odd. */ perhapsprime = ( inval + 1 ) | 1 ; } /* While prime not found, loop. */ for( found = PRIME_FALSE; found != PRIME_TRUE; perhapsprime += 2 ) { /* Check factors from 3 up to perhapsprime/2. */ for( testfactor = 3; testfactor <= (perhapsprime >> 1) + 1; testfactor += 1 ) { found = PRIME_TRUE; /* Assume we will find a prime. */ if( (perhapsprime % testfactor) == 0 ) /* If testfactor divides perhapsprime... */ { found = PRIME_FALSE; /* ...then, perhapsprime was non-prime. */ goto check_next_prime; /* Break the inner loop, go test a new perhapsprime. */ } } check_next_prime:; /* This label is used to break the inner loop. */ if( found == PRIME_TRUE ) /* If the loop ended normally, we found a prime. */ { return( perhapsprime ); /* Return the prime we found. */ } } return( perhapsprime ); /* When the loop ends, perhapsprime is a real prime. */ } 

Other files are available here .

When I run the code, I get the expected result from the manufacturers, but I don't get the expected result for the consumers:

 System starting... Producer 1 is created with thread-ID 1 Producer 2 is created with thread-ID 2 Producer 3 is created with thread-ID 3 Consumer 1 is created with thread-ID 4 Consumer 2 is created with thread-ID 5 #### Thread yielded after using 1 tick. Performing thread-switch number 1. The system has been running for 1 ticks. Switching from thread-ID 0 to thread-ID 1. Next Prime from producer 1 is 2003 PutFifo: 2003 wraddr = 0 Next Prime from producer 1 is 2011 PutFifo: 2011 wraddr = 1 Next Prime from producer 1 is 2017 PutFifo: 2017 wraddr = 2 Next Prime from producer 1 is 2027 PutFifo: 2027 wraddr = 3 Next Prime from producer 1 is 2029 PutFifo: 2029 wraddr = 4 Next Prime from producer 1 is 2039 PutFifo: 2039 wraddr = 5 Next Prime from producer 1 is 2053 PutFifo: 2053 wraddr = 6 Next Prime from producer 1 is 2063 PutFifo: 2063 wraddr = 7 Next Prime from producer 1 is 2069 PutFifo: 2069 wraddr = 8 Next Prime from producer 1 is 2081 PutFifo: 2081 wraddr = 9 Next Prime from producer 1 is 2083 PutFifo: 2083 wraddr = 0 Next Prime from producer 1 is 2087 PutFifo: 2087 wraddr = 1 Next Prime from producer 1 is 2089 PutFifo: 2089 wraddr = 2 Next Prime from producer 1 is 2099 PutFifo: 2099 wraddr = 3 Next Prime from producer 1 is 2111 PutFifo: 2111 wraddr = 4 Next Prime from producer 1 is 2113 PutFifo: 2113 wraddr = 5 Next Prime from producer 1 is 2129 PutFifo: 2129 wraddr = 6 Next Prime from producer 1 is 2131 PutFifo: 2131 wraddr = 7 Next Prime from producer 1 is 2137 PutFifo: 2137 wraddr = 8 Next Prime from producer 1 is 2141 PutFifo: 2141 wraddr = 9 Next Prime from producer 1 is 2143 PutFifo: 2143 wraddr = 0 Next Prime from producer 1 is 2153 PutFifo: 2153 wraddr = 1 Performing thread-switch number 2. The system has been running for 101 ticks. Switching from thread-ID 1 to thread-ID 2. Next Prime from producer 2 is 5003 PutFifo: 5003 wraddr = 2 Next Prime from producer 2 is 5009 PutFifo: 5009 wraddr = 3 Next Prime from producer 2 is 5011 PutFifo: 5011 wraddr = 4 Next Prime from producer 2 is 5021 PutFifo: 5021 wraddr = 5 Next Prime from producer 2 is 5023 PutFifo: 5023 wraddr = 6 Next Prime from producer 2 is 5039 PutFifo: 5039 wraddr = 7 Next Prime from producer 2 is 5051 PutFifo: 5051 wraddr = 8 Next Prime from producer 2 is 5059 PutFifo: 5059 wraddr = 9 Next Prime from producer 2 is 5077 PutFifo: 5077 wraddr = 0 Next Prime from producer 2 is 5081 PutFifo: 5081 wraddr = 1 Performing thread-switch number 3. The system has been running for 201 ticks. Switching from thread-ID 2 to thread-ID 3. Next Prime from producer 3 is 8009 PutFifo: 8009 wraddr = 2 Next Prime from producer 3 is 8011 PutFifo: 8011 wraddr = 3 Next Prime from producer 3 is 8017 PutFifo: 8017 wraddr = 4 Next Prime from producer 3 is 8039 PutFifo: 8039 wraddr = 5 Next Prime from producer 3 is 8053 PutFifo: 8053 wraddr = 6 Next Prime from producer 3 is 8059 PutFifo: 8059 wraddr = 7 Performing thread-switch number 4. The system has been running for 301 ticks. Switching from thread-ID 3 to 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 you tell me why the first 30 primes are overwritten when everything I do just follows the specification and just removes the comments from the code to activate what the instructor has prepared for us? I have not been able to complete this exercise for several months since I am not getting any help. The first 30 primes are masked, and the program should not change (this is homework). I asked the instructor, and he did not, said that I am using newer versions of the software. I could try with older versions of softare, but that doesn't seem like a likely solution.

Update

The strategy I can think of is to start using the debugger and check the FIFO ADT at runtime. I don't have much experience using gdb, so please help me if you can.

+4
source share
3 answers

This code cannot be expected to work correctly unless you uncomment the Wait and Sign calls inside the PutFifo and GetFifo functions. If it really works under certain circumstances on a particular computer, this is just luck.

First of all, if one or more producer flows fill the FIFO buffer before switching to one of the consumer flows, then some of this data will obviously be lost.

You can clearly see this in your output example. Manufacturer flows generate 38 values ​​before the first consumer stream gets a chance to start. And since the size of your buffer is 10, the first value that the consumer will read is actually the 31st value (i.e., the last value written to 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 streams will read more data than is available in the FIFO buffer before switching to one of the producer streams, then they will eventually read the same values ​​more than once. Again you can see this in your example. The consumer stream reads items 31 through 38, then wraps around items 29 and 30 (the last values ​​in wraddr 8 and 9) before repeating item 31 again.

This is not the worst thing that could have happened. In a true proactive multithreaded system, a producer stream can be unloaded halfway through the PutFifo function. So imagine that one of the producer threads writes to the FIFO buffer when wraddr is 9. Let it be said that it executes these two lines.

 Fifo[wraddr] = tal; /* Write to FIFO array. */ wraddr = wraddr + 1; /* Increase index into FIFO array, 

At this point, wraddr is 10, but before the function has the ability to check the overflow (and wrap the index back to 0), the thread will be superseded by another producer thread. And since wraddr is 10, this new producer will write through the end of the buffer, which could lead to a crash of the application.

If it survives, wraddr will be increased again (becomes 11), but it still will not be reset to zero, because the overflow check expects an exact match with FIFO_SIZE. Thus, even if it doesn’t crash right away, it will certainly work at some point, because wraddr will continue to do more and more, overwriting more and more memory.

The bottom line is that if you want this code to work, you will have to add synchronization calls.

+1
source

Original Notes 2013-05-05

In a comment, I noted:

You have wrapper control, so when the writer reaches the end of the array, the next record is placed at the beginning, but you have no control over the filling, so if producers produce faster than consumers consume, producers overwrite unread data. You must ensure that the Fifo array Fifo not full.

Nick Rosencrantz remarked:

You are correct, increasing the FIFO size to 100 corrects the error.

In fact, increasing the FIFO size does not correct the error; he just avoids the mistake longer. You need to keep track of whether the record pointer (index) will catch up with the read pointer, and either not add or delay the addition of a new number until one of the consumers reads the number on the readable pointer so that the space again.


Actual code testing

The code in the question is the code from the exercise verbatim. I'm not sure what the hardware is, but screen shots show that Windows is enabled. I have a Mac. Superficially, which makes code testing tough - one of the files is assembler. However, the code can be easily supplemented by implementations of Unix primitives (POSIX pthread). In fact, the laboratory is well designed; it was very easy to make a simulation. However, you should accept any of the results that I report with a corresponding pinch of salt - the Mac is a completely different machine.

  • I prefer functions declared before they are defined or used.

     #include <unistd.h> extern void fatal_error(char * msg); extern void Sleep (int n); extern void Signal(int *sem); extern void Wait(int *sem); extern void PutFifo(int tal); extern int GetFifo(void); extern void Producer(struct Prod * prodstruct); extern void Consumer(int * tal); 
  • while (1); in fatal_error() - wait; I would rather use pause() . However, this has never been done, so it may not matter.

  • The necessary oslab_*() primitives can be modeled using POSIX pthreads trivially:

     /* pthread implementation */ #include <pthread.h> #include <time.h> static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; void oslab_begin_critical_region(void) { pthread_mutex_lock(&mtx); } void oslab_end_critical_region(void) { pthread_mutex_unlock(&mtx); } int oslab_create_thread(int (*thread_function)(void *), void *data, unsigned int *stack) { typedef void *(*ThreadMain)(void *); static int threadnum = 0; pthread_t pth; pthread_attr_t pat; pthread_attr_init(&pat); pthread_attr_setdetachstate(&pat, PTHREAD_CREATE_DETACHED); if (pthread_create(&pth, &pat, (ThreadMain)thread_function, data) != 0) { char buffer[128]; sprintf(buffer, "Failed to create thread with stack %p\n", stack); fatal_error(buffer); } return ++threadnum; } void oslab_idle(void) { pause(); } void oslab_yield(void) { struct timespec rqtp = { .tv_sec = 0, .tv_nsec = 1000000 }; // 1 millisecond if (nanosleep(&rqtp, 0) != 0) fatal_error("nanosleep failed\n"); } /* end pthread implementation */ 
  • All messages are in the annoying format "\nMessage" ; This is a formatted format. Rewrite them all so that the new line is at the end, where the "end of line" character should always be.

  • When debug printing is enabled, lines should be erased. I used fflush(0) (the equivalent, in this context, fflush(stdout) ) after debugging the prints. An alternative (better) way to do this would be to call setvbuf() in main() to set line buffering.

     char buffer[BUFSIZ]; setvbuf(stdout, buffer, _IOLBF, BUFSIZ); 

results

With these changes in place (leaving the synchronization primitives - the Wait() , Signal() and oslab_yield() - commented out), all hell breaks:

 System starting... Producer 1 is created with thread-ID 1 Producer 2 is created with thread-ID 2 Producer 3 is created with thread-ID 3 Consumer 1 is created with thread-ID 4 Consumer 2 is created with thread-ID 5 Consumer 1 gets Prime 0 Next Prime from producer 1 is 2003 Consumer 2 gets Prime 0 Next Prime from producer 2 is 5003 Next Prime from producer 3 is 8009 Consumer 1 gets Prime 0 Next Prime from producer 1 is 2011 Consumer 2 gets Prime 0 Next Prime from producer 2 is 5009 Consumer 1 gets Prime 0 Next Prime from producer 1 is 2017 Consumer 2 gets Prime 0 Next Prime from producer 3 is 8011 Next Prime from producer 2 is 5011 Consumer 1 gets Prime 0 Next Prime from producer 1 is 2027 Consumer 2 gets Prime 0 Consumer 1 gets Prime 0 Next Prime from producer 3 is 8017 Next Prime from producer 2 is 5021 Next Prime from producer 1 is 2029 Consumer 2 gets Prime 0 Consumer 1 gets Prime 2003 Consumer 2 gets Prime 2029 Next Prime from producer 1 is 2039 Next Prime from producer 3 is 8039 Next Prime from producer 2 is 5023 Consumer 1 gets Prime 8009 Consumer 2 gets Prime 2011 

However, if you turn on synchronization primitives (but don’t turn off the debugging code), you get reasonable behavior: the consumer does not try to read the queue before there is data in the queue, and the authors do not try to write to the queue when there is no space in the queue.

 System starting... Producer 1 is created with thread-ID 1 Producer 2 is created with thread-ID 2 Producer 3 is created with thread-ID 3 Consumer 1 is created with thread-ID 4 Consumer 2 is created with thread-ID 5 Next Prime from producer 1 is 2003 Next Prime from producer 2 is 5003 Next Prime from producer 3 is 8009 Consumer 1 gets Prime 2003 Consumer 2 gets Prime 5003 Next Prime from producer 1 is 2011 Next Prime from producer 3 is 8011 Next Prime from producer 2 is 5009 Next Prime from producer 2 is 5011 Consumer 1 gets Prime 8009 Consumer 2 gets Prime 2011 Next Prime from producer 1 is 2017 Next Prime from producer 3 is 8017 Consumer 1 gets Prime 8011 Consumer 2 gets Prime 5009 Next Prime from producer 2 is 5021 Next Prime from producer 1 is 2027 Next Prime from producer 3 is 8039 Next Prime from producer 2 is 5023 Consumer 1 gets Prime 5011 Consumer 2 gets Prime 2017 Next Prime from producer 3 is 8053 Next Prime from producer 1 is 2029 Next Prime from producer 2 is 5039 

This is to be expected. If you have true multi-core streaming (Intel Core i7), then without synchronization you get all kinds of behavior with an odd ball. With synchronization, everything is calm. I allowed the code to run with the output going to the file. When the results are analyzed, you see one occurrence of each of the numbers 2003..4999, two occurrences of each of the numbers 5003..7993 and three occurrences of each of the prime numbers from 8009 up, which was to be expected.

If you enable the debugging code, you will see more results:

 System starting... Producer 1 is created with thread-ID 1 Next Prime from producer 1 is 2003 Producer 2 is created with thread-ID 2 PutFifo: 2003 wraddr = 0 Producer 3 is created with thread-ID 3 Consumer 1 is created with thread-ID 4 GetFifo: 2003 rdaddr = 0 Consumer 2 is created with thread-ID 5 Next Prime from producer 2 is 5003 Next Prime from producer 3 is 8009 Consumer 1 gets Prime 2003 PutFifo: 5003 wraddr = 1 GetFifo: 5003 rdaddr = 1 Consumer 1 gets Prime 5003 Next Prime from producer 1 is 2011 PutFifo: 2011 wraddr = 2 GetFifo: 2011 rdaddr = 2 Consumer 2 gets Prime 2011 PutFifo: 8009 wraddr = 3 Next Prime from producer 2 is 5009 PutFifo: 5009 wraddr = 4 GetFifo: 8009 rdaddr = 3 Consumer 1 gets Prime 8009 Next Prime from producer 3 is 8011 GetFifo: 5009 PutFifo: 8011 rdaddr = 4 Next Prime from producer 1 is 2017 wraddr = 5 Consumer 2 gets Prime 5009 Next Prime from producer 2 is 5011 PutFifo: 5011 wraddr = 6 PutFifo: 2017 wraddr = 7 GetFifo: 8011 rdaddr = 5 Consumer 2 gets Prime 8011 GetFifo: 5011 rdaddr = 6 Next Prime from producer 3 is 8017 Consumer 1 gets Prime 5011 PutFifo: 8017 wraddr = 8 Next Prime from producer 2 is 5021 PutFifo: 5021 wraddr = 9 Next Prime from producer 1 is 2027 PutFifo: 2027 wraddr = 0 GetFifo: 2017 rdaddr = 7 Consumer 1 gets Prime 2017 

It shows a wraddr wrap from 9 to 0. Otherwise, it is verbose and uninteresting.

Launch gdb

It is not clear that you can run GDB in a program in the source environment. You are using streaming using homebrew (if you are using the original source oslab_lowlevel_c.c and oslab_lowlevel_asm.s ) and GDB will not know multithreading.

When using the POSIX thread that I used, it would be possible to debug the code using GDB.

+3
source

The easiest way is to use a "special value", which cannot be a real produced value, and so that the manufacturer only puts the data in the slot that has this special value, if it is not there, it will sleep. The consumer will consume any value that is not a special value, and sets this position for a special value. If there is no data that is not of particular importance, the consumer will sleep.

+1
source

Source: https://habr.com/ru/post/1479196/


All Articles