Redsam121
Redsam121

Reputation: 129

Buffer mutex and condition variables in C

I only just started writing multithreading in C and don't have a full understanding of how to implement it. I'm writing a code that reads an input file and puts into a buffer struct array. When the buffer has no more available space, request_t is blocked waiting for available space. It is controlled by thread Lift_R. The other threads lift 1-3 operate lift() and it writes whats in buffer to the output file depending number of int sec. sec and size and given values through command line. This will free up space for request to continue reading the input.

Can someone please help me with how to implement these functions properly. I know there are other questions relating to this, but I want my code to meet specific conditions.

(NOTE: lift operates in FIFO and threads use mutual exclusion)

This is what I wrote so far, I haven't implemented any waiting conditions or FIFO yet, I'm currently focusing on the writing to file and debugging and am soon getting to wait and signal.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> 
#include "list.h"
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; //declare thread conditions
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //declare mutex
int sec; //time required by each lift to serve a request
int size; //buffer size
buffer_t A[];
write_t write;
void *lift(void *vargp)

{
    pthread_mutex_lock(&lock);
    FILE* out;
    out = fopen("sim_output.txt", "w");
    //gather information to print
    if (write.p == NULL) //only for when system begins
    {
        write.p = A[1].from;
    }
    write.rf = A[1].from;
    write.rt = A[1].to;
    write.m = (write.p - A[1].from) + (A[1].to - A[1].from);
    if (write.total_r == NULL) //for when the system first begins
    {
        write.total_r = 0;
    }
    else
    {
        write.total_r++;
    }
    if (write.total_m == NULL)
    {
        write.total_m = write.m;
    }
    else
    {
        write.total_m = write.total_m + write.m;
    }
    write.c = A[1].to;
    //Now write the information
    fprintf(out, "Previous position: Floor %d\n", write.p);
    fprintf(out, "Request: Floor %d to Floor %d\n", write.rf, write.rt);
    fprintf(out, "Detail operations:\n");
    fprintf(out, "    Go from Floor %d to Floor %d\n", write.p, write.rf);
    fprintf(out, "    Go from Floor %d to Floor %d\n", write.rf, write.rt);
    fprintf(out, "    #movement for this request: %d\n", write.m);
    fprintf(out, "    #request: %d\n", write.total_r);
    fprintf(out, "    Total #movement: %d\n", write.total_m);
    fprintf(out, "Current Position: Floor %d\n", write.c);
    write.p = write.c; //for next statement
    pthread_mutex_unlock(&lock);
    return NULL;
}


void *request_t(void *vargp)
{
    pthread_mutex_lock(&lock); //Now only request can operate
    FILE* f;
    FILE* f2;
    f = fopen("sim_input.txt", "r");
    if (f == NULL)
    {
        printf("input file empty\n");
        exit(EXIT_FAILURE);
    }
    f2 = fopen("sim_output.txt", "w");

    int i = 0;
    for (i; i < size; i++)
    {
        //read the input line by line and into the buffer
        fscanf(f, "%d %d", &A[i].from, &A[i].to);\
        //Print buffer information to sim_output
        fprintf(f2, "----------------------------\n");
        fprintf(f2, "New Lift Request from Floor %d to Floor %d \n", A[i].from, A[i].to);
        fprintf(f2, "Request No %d \n", i);
        fprintf(f2, "----------------------------\n");
    }
    printf("Buffer is full");
    fclose(f);
    fclose(f2);
    pthread_mutex_unlock(&lock);
    return NULL;
}


void main(int argc, char *argv[]) // to avoid segmentation fault
{
    size = atoi(argv[0]);
    if (!(size >= 1))
    {
        printf("buffer size too small\n");
        exit(0);
    }
    else
    {
        A[size].from = NULL;
        A[size].to = NULL;
    }
    sec = atoi(argv[1]);
    pthread_t Lift_R, lift_1, lift_2, lift_3;

    pthread_create(&Lift_R, NULL, request_t, NULL);

    pthread_join(Lift_R, NULL);

    pthread_create(&lift_1, NULL, lift, NULL);

    pthread_join(lift_1, NULL);

    pthread_create(&lift_2, NULL, lift, NULL);

    pthread_join(lift_2, NULL);

    pthread_create(&lift_3, NULL, lift, NULL);

    pthread_join(lift_3, NULL);

}

And here is the struct files:

#include <stdbool.h>

typedef struct Buffer
{
    int from;
    int to;
}buffer_t; //buffer arrary to store from and to values from sim_input

typedef struct Output
{
    int l; //lift number
    int p; //previous floor
    int rf; //request from
    int rt; //request to
    int total_m; //total movement
    int c; // current position
    int m; //movement
    int total_r; //total requests made
}write_t;

Upvotes: 2

Views: 712

Answers (1)

mevets
mevets

Reputation: 10435

Between reading your code and question I see a large conceptual gap. There are some technical problems in the code (eg. you never fclose out); and a hard to follow sequence.

So, this pattern:

  pthread_create(&x, ?, func, arg);
  pthread_join(x, ...);

Can be replaced with:

  func(arg);

so, your really aren't multithreaded at all; it is exactly as if:

void main(int argc, char *argv[]) // to avoid segmentation fault
{
    size = atoi(argv[0]);
    if (!(size >= 1))
    {
        printf("buffer size too small\n");
        exit(0);
    }
    else
    {
        A[size].from = NULL;
        A[size].to = NULL;
    }
    sec = atoi(argv[1]);

    request_t(0);
    lift(0);
    lift(0);
    lift(0);
}

and, knowing that, I hope you can see the futility in:

pthread_mutex_lock(&lock);
....
pthread_mutex_unlock(&lock);

So, start with a bit of a rethink of what you are doing. It sounds like you have a lift device which needs to take inbound requests, perhaps sort them, then process them. Likely 'forever'.

This probably means a sorted queue; however one not sorted by an ordinary criteria. The lift traverses the building in both directions, but means to minimize changes in direction. This involves traversing the queue with both an order ( >, < ) and a current-direction. You would likely want request to simply evaluate the lift graph, and determine where to insert the new request. The lift graph would be a unidirectional list of where the lift goes next. And, perhaps a rule that the list only consults its list as it stops at a given floor.

So, the Request can take a lock of the graph, alter it to reflect the new requestion, then unlock it.

The Lift can simply:

  while (!Lift_is_decommissioned) {
        pthread_mutex_lock(&glock);
        Destination = RemoveHead(&graph);
        pthread_mutex_unlock(&glock);
        GoTo(Destination);
  }

And the Request can be:

  pthread_mutex_lock(&glock);

  NewDestination = NewEvent.floorpressed;
  NewDirection   = NewEvent.floorpressed > NewEvent.curfloor ? Up : Down;
  i = FindInsertion(&graph, NewDestination, NewDirection);
  InsertAt(&graph, i, NewDestination);

  pthread_mutex_unlock(&glock);

Which may be a bit surprising that there is no difference between pressing a "goto floor" button from within the lift, and a "I want lift here now" from outside the lift.

But, with this sort of separation, you can have the lift simply follow the recipe above, and the handlers for the buttons invoke the other pseudo code above.

The FindInsertion() may be a bit hairy though....

Upvotes: 2

Related Questions