In: Computer Science
The function 'make_enzyme_threads' has a memory bug. Fix this by simply re-ordering the lines in this function. It is simple fix but may take a while for you to find it. As a hint, you may want to pay attention to the addresses of the pointers that are passed to the individual enzymes
#include "enzyme.h"
int please_quit;
int use_yield;
int workperformed;
// The code executed by each enzyme.
void *run_enzyme(void *data) {
/* This function should :
1. Cast the void* pointer to thread_info_t* (defined in
enzyme.h)*/
thread_info_t *inp;
inp = (thread_info_t *)(data);
/*2. Initialize the swapcount to zero*/
int swapcount = 0;
/*3. Set the cancel type to PTHREAD_CANCEL_ASYNCHRONOUS (see pthread_setcanceltype)*/
void *cancel_type = PTHREAD_CANCEL_ASYNCHRONOUS;
/*4. If the first letter of the string is a C then call
pthread_cancel on this thread.
(see also, pthread_self)
Note, depeneding on your platform (and specifically for macOS) you
have
to replace the call to pthread_cancel with
pthread_exit(PTHREAD_CANCELED) in order to make the cancel
test
pass.*/
if (s[0]) == 'C')
pthread_cancel(pthread_self());
/*5. Create a while loop that only exits when please_quit is
nonzero
6. Within this loop: if the first character of the string has an
ascii
value greater than the second (info->string[0] >
info->string[1]) then
- Set workperformed = 1
- Increment swapcount for this thread
- Swap the two characters around
7. If "use_yield" is nonzero then call pthread_yield at the end of
the loop.
8. Return a pointer to the updated structure.
*/
       while (please_quit == 0) {
if (s[0] >s[1]) {
          
    workperformed = 1;
          
    threadswapcount++;
          
    char c;
          
    c = s[0];
          
    s[0] = s[1];
          
    s[1] = c;
}
if (use_yield != 0)
pthread_yield(pthread_self());
       }
while(0) {
pthread_yield();
};
return NULL;
}
// Make threads to sort string.
// Returns the number of threads created.
// There is a memory bug in this function.
int make_enzyme_threads(pthread_t * enzymes, char *string, void
*(*fp)(void *)) {
int i, rv, len;
thread_info_t *info;
len = strlen(string);
info = (thread_info_t *)malloc(sizeof(thread_info_t));
for (i = 0; i < len - 1; i++) {
info->string = string + i;
rv = pthread_create(enzymes + i, NULL, fp, info);
if (rv) {
fprintf(stderr,"Could not create thread %d : %s\n", i,
strerror(rv));
exit(1);
}
}
return len - 1;
}
// Join all threads at the end.
// Returns the total number of swaps.
int join_on_enzymes(pthread_t *threads, int n) {
   int i;
   int totalswapcount = 0;
   //initialize the cancelled count
   int cancelcount = 0; // just to make the code
compile
// you will need to edit the code below
for (i = 0; i<n; i++) {
       void *status;
       int rv = pthread_join(threads[i],
&status);
       /*if join status is non zero, then
cant join the threads
       Thus the condition is to check
whether rv estimated
       Above has a non zero value*/
       if (rv != 0)
       {
          
fprintf(stderr, "Can't join thread %d:%s.\n", i,
strerror(rv));
           continue;
       }
       //if status is
PTHREAD_CANCELED
       if ((void*)status ==
PTHREAD_CANCELED)
       {
           //increment the
cancelled count
          
cancelcount++;
           continue;
       }
       else if (status == NULL)
       {
           printf("Thread
%d did not return anything\n", i);
       }
       else
       {
           printf("Thread
%d exited normally: ", i);// Don't change this line
          
           
           
           
    //need to cast the status with
thread_info_t
          
           
           
           
    //and then get the swap count as thread swap
count
           int
threadswapcount = (thread_info_t *)status->swapcount;
           // Hint - you
will need to cast something.
           printf("%d
swaps.\n", threadswapcount); // Don't change this line
           totalswapcount
+= threadswapcount;// Don't change this line
       }
   }
   return totalswapcount;
}
/* Wait until the string is in order. Note, we need the
workperformed flag just
* in case a thread is in the middle of swapping characters so that
the string
* temporarily is in order because the swap is not complete.
*/
void wait_till_done(char *string, int n) {
int i;
while (1) {
pthread_yield();
workperformed = 0;
for (i = 0; i < n; i++)
if (string[i] > string[i + 1]) {
workperformed = 1;
}
if (workperformed == 0) {
break;
}
}
}
void * sleeper_func(void *p) {
sleep( (int) p);
// Actually this may return before p seconds because of
signals.
// See man sleep for more information
printf("sleeper func woke up - exiting the program\n");
exit(1);
}
int smp2_main(int argc, char **argv) {
pthread_t enzymes[MAX];
int n, totalswap;
char string[MAX];
if (argc <= 1) {
fprintf(stderr,"Usage: %s <word>\n",argv[0]);
exit(1);
}
// Why is this necessary? Why cant we give argv[1] directly to
the thread
// functions?
strncpy(string,argv[1],MAX);
please_quit = 0;
use_yield = 1;
printf("Creating threads...\n");
n = make_enzyme_threads(enzymes, string, run_enzyme);
printf("Done creating %d threads.\n",n);
pthread_t sleeperid;
pthread_create(&sleeperid, NULL, sleeper_func, (void*)5);
wait_till_done(string, n);
please_quit = 1;
printf("Joining threads...\n");
totalswap = join_on_enzymes(enzymes, n);
printf("Total: %d swaps\n", totalswap);
printf("Sorted string: %s\n", string);
exit(0);
}
Fixed Code:
#include "enzyme.h"
int please_quit;
int use_yield;
int workperformed;
// The code which each enzyme execute.
void *run_enzyme(void *data) {
   /* This function should :
   1. cast the void* pointer to thread_info_t*
   2. initialize the swapcount to zero
   3. Set the cancel type to
PTHREAD_CANCEL_ASYNCHRONOUS
   4. If the first letter of the string is a C then call
pthread_cancel on this thread.
   5. Create a while loop that only exits when
please_quit is nonzero
   6. Within this loop: if the first character of the
string has an ascii value greater than the second (s[0] >s[1])
then -
       Set workperformed=1, increment
swapcount for this thread, then swap the two characters
around
       If "use_yield" is nonzero then call
pthread_yield at the end of the loop.
   7. Return a pointer to the updated structure.
   */
   while(0) {
       sched_yield();
   };
   return NULL;
}
// Make threads to sort string.
// Returns the number of threads created.
// There is a memory bug in this function.
int make_enzyme_threads(pthread_t * enzymes, char *string, void
*(*fp)(void *)) {
   int i,rv,len;
   thread_info_t *info;
   len = strlen(string);
   info = (thread_info_t
*)malloc(sizeof(thread_info_t));
   for(i=0;i<len-1;i++) {
   info->string = string+i;
   rv = pthread_create(enzymes+i,NULL,fp,info);
   if (rv) {
   fprintf(stderr,"Could not create thread %d :
%s\n",
          
i,strerror(rv));
       exit(1);
   }
   }
   return len-1;
}
// Joins all threads at the end.
// Return the total number of swaps.
int join_on_enzymes(pthread_t *threads, int n) {
   int i;
   int totalswapcount = 0;
   int whatgoeshere=0; // just to make the code
compile
   // we will need to edit the code below
   for(i=0;i<n;i++) {
   void *status;
   int rv = pthread_join(threads[i],&status);
if(whatgoeshere) {
   fprintf(stderr,"Can't join thread
%d:%s.\n",i,strerror(rv));
   continue;
   }
   if ((void*)whatgoeshere == PTHREAD_CANCELED)
{
   continue;
   } else if (status == NULL) {
   printf("Thread %d did not return anything\n",i);
   } else {
   printf("Thread %d exited normally: ",i);// Don't
change this line
   int threadswapcount = whatgoeshere;
   // Hint - we will need to cast something here.
   printf("%d swaps.\n",threadswapcount); // Don't change
this line
   totalswapcount += threadswapcount;// Don't change this
line
   }
   }  
   return totalswapcount;
}
/* Wait until the string is in order. Note, we need the
workperformed flag just in case a thread is in the middle of
swapping character so that the string temporarily is in order
because the swap has not been completed.
*/
void wait_till_done(char *string, int n) {
   int i;
   while(1) {
   sched_yield();
   workperformed=0;
   for(i=0;i<n;i++)
   if (string[i] > string[i+1]) {
   workperformed=1;
       }
   if(workperformed==0) break;
   }
}
void * sleeper_func(void *p) {
   sleep( (int) p);
   // This may return before p second because of
signal.
   // See man sleep for more information
   printf("sleeper func raised up - exiting the program
\n");
   exit(1);
}
int smp2_main(int argc, char **argv) {
   pthread_t enzymes[MAX];
   int n,totalswap;
   char string[MAX];
   if (argc <= 1) {
   fprintf(stderr,"Usage: %s
<word>\n",argv[0]);
   exit(1);
   }
   strncpy(string,argv[1],MAX); // Why is it
necessary? Why cant we give argv[1] directly to thread
function?
   please_quit = 0;
   use_yield =1;
  
   printf("Creating thread...\n");
   n =
make_enzyme_threads(enzymes,string,run_enzyme);
   printf("Done creating %d thread\n",n);
   pthread_t sleeperid;
  
pthread_create(&sleeperid,NULL,sleeper_func,(void*)5);
wait_till_done(string,n);
   please_quit = 1;
   printf("Joining thread...\n");
   totalswap = join_on_enzymes(enzymes, n);
   printf("Total: %d swap \n",totalswap);
   printf("Sorted string: %s\n",string);
  
   exit(0);
}