1

(C++) My code is supposed to mimic the round robin cpu scheduling algorithm using linked lists (so accept list of process names with times ex: ProcessA 10, subtract 3 from the times, if the result is greater than 0 it is shifted to end of list. This continues until process time reaches 0 where at that point the process is finished).

My program accepts and displays the list of processes and their times correctly. So I didn't include the code for accepting, creating, and displaying the list. Somewhere after displaying the list the user has inputted, the program just abruptly ends for some reason.

my output:

[John@fish lab2]$ ./a.out
Enter your processes, to end press '^d'
ProcessA 4
Enter your processes, to end press '^d'
ProcessB 10
Enter your processes, to end press '^d'
ProcessC 6
Enter your processes, to end press '^d'
^d
Displaying the list of processes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ProcessA 4
ProcessB 10
ProcessC 6
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


I tried modifying the while loop since I thought there was an issue with the flag so I changed it from while(print_flag) to while(true) and placed a break statement in the else condition.

my output:

same as last ouput except with an extra line saying : 'segmentation fault(core dumped)'

I have no idea on how to fix the underlying issue. Any help is appreciated.

#include <iostream>
#include <list>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string>

using namespace std;

volatile sig_atomic_t print_flag = false;

struct NodeType
{
    string value1; //process name
    int value2; //process time
    NodeType* next;
    
    void DisplayLinkedList(NodeType* head)
    {
        NodeType* p;

        p = head;   //initialize pointer p to point to the first node in the linked list 

        cout << "Displaying the list of processes: " << endl;
        cout << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" << endl;
        while (p != NULL)
        {
            cout << p->value1 << " " << p->value2 << endl;
            p = p->next;  //update p to point to next node in the linked list ... 
        }
        cout << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" << endl;
        cout << " " << endl;
    }

    void createnode(NodeType*& head, string x, int y)
    {
        NodeType* p = new NodeType;
        
        
        if (head == NULL)
        {
            p->value1 = x;
            p->value2 = y;
            p->next = NULL;
            head = p;
        }
        else
        {
            p = head;
            while (p->next != NULL)
                p = p->next;
            p->next = new NodeType;
            p = p->next;
            p->value1 = x;
            p->value2 = y;
            p->next = NULL;
            
        }
    }

    bool IsEmpty(NodeType* h) const
    {
        return h == NULL;
    }

    void DeleteNode(NodeType*& head)
    {
        NodeType* p = head;
        head = head->next;
        delete p;
    }

    
    void roundrobin(NodeType*& head)
    {
        head->value2 -= 3;
        if (head->value2 == 0) // no time remaining 
        {
            cout << head->value1 << " Finished" << endl;
            DeleteNode(head);
        }
        else // time remaining
        {
            NodeType* p = head;
            p->next = NULL;
            head = head->next;
            NodeType* q = head;
            while (q->next != NULL)
                q = q->next;
            q->next = p;
        }
    }
};

void handle_alarm(int sig) // interrupt handler, manipulates flags to allow roundrobin to run
{
    print_flag = true;
}


int main()
{
    NodeType* head = NULL;
    NodeType a;
    string v, x, y;
    int argc = 0;
    int z = 0;
    

    while(true)
    {
        cout << "Enter your processes, to end press '^d' " << endl;
        getline(cin, v);
        if (v == "^d")
            break;
        //cin >> a;
        int index = v.find(" "); 
        x = v.substr(0, index); 
        y = v.substr(index + 1, v.length());
        
        z = stoi(y);
        a.createnode(head, x, z);

        argc++;
    }

    a.DisplayLinkedList(head);
    
    signal(SIGALRM, handle_alarm);
    alarm(3);
    while (print_flag)
    {
        
            if (a.IsEmpty(head) == false) // list not empty
            {
                a.roundrobin(head);
                a.DisplayLinkedList(head);
            }
            else
            {
                cout << "No more processes left" << endl;
                print_flag = false;
            }
        
        //print_flag = false;
        alarm(3);
    }
    
    return 0;
}

  • Could you include function `DisplayLinkedList` definition in your code? I couldn't find it in your `NodeType` struct definition – VietHTran Oct 10 '20 at 23:08
  • @user4581301 it won't be possble to reproduce without including the majority of the code which will make the post too long – Volapiik Vyrient Oct 10 '20 at 23:12
  • @VietHTran will do – Volapiik Vyrient Oct 10 '20 at 23:12
  • I decided to include all of my code since it was requested, sorry if it inconveniences anyone – Volapiik Vyrient Oct 10 '20 at 23:15
  • 1
    Display routine looks good, ` NodeType* p = new NodeType;` leaks in `createnode`'s else case, but that won't cause a segfault. – user4581301 Oct 10 '20 at 23:18
  • @user4581301 thanks will make sure to take a look at it – Volapiik Vyrient Oct 10 '20 at 23:19
  • this may sound obvious, but to fix the leak I would have to add delete p; to the else case right? – Volapiik Vyrient Oct 10 '20 at 23:33
  • The time remaining logic seems convoluted. `NodeType* q = head; while (q->next != NULL)` is fatal if `head` is null. That may be your bug. – user4581301 Oct 10 '20 at 23:33
  • My fix would be to not allocate `p` until it's needed. Move `NodeType* p = new NodeType;` into `if (head == NULL)` and in the `else` use `NodeType* p = head;` instead of `p = head;` – user4581301 Oct 10 '20 at 23:36
  • For your other comment, you mentioned it would be fatal if head is null. But head should be null only when the list is empty, so it should have at least printed a few rounds of the algorithm working instead of not printing anything at all. Not sure though. – Volapiik Vyrient Oct 10 '20 at 23:39
  • 1
    And when you used your debugger to run your program, what did you see? This is precisely what a debugger is for. If you don't know how to use a debugger this is a good opportunity to learn how to use it to run your program one line at a time, monitor all variables and their values as they change, and analyse your program's logical execution flow. Knowing how to use a debugger is a required skill for every C++ developer, no exceptions. With your debugger's help you should be able to quickly find all bugs in this and all future programs you write, without having to ask anyone for help. – Sam Varshavchik Oct 10 '20 at 23:40
  • @SamVarshavchik I usually debug in visual studio(though I'm bad at it), but in this case visual studio doesn't seem to support alarm(I may be wrong though) and so I have been writing code using vi editor on a linux terminal. – Volapiik Vyrient Oct 10 '20 at 23:42
  • I see that now. Yes, you ensure `head`'s not null before calling `roundrobin` – user4581301 Oct 10 '20 at 23:46
  • 1
    Unfortunately one line above you've got `head = head->next;`. `head` may now be null. – user4581301 Oct 11 '20 at 00:02
  • 1
    As a bare minimum, you should use a debugger -- or diagnostic messages to `cerr` -- to identify which line triggers the segmentation fault. This technique might also help you concoct a shorter [mre] that does not rely on user input. – JaMiT Oct 11 '20 at 00:08
  • @user4581301 Your right if in the case where there is only one process in the list it will create an issue. Will fix. However, I tested the program with a minimum of two inputs and had the problem where nothing shows up. Just to confirm were you getting the same issue? – Volapiik Vyrient Oct 11 '20 at 00:13
  • Just occurred to me that control +D doesn't look anything like `"^d". Five bucks says trying to parse the control+D into an integer's what's getting you. Here I was scouring the linked list logic and the bug's probably the IO logic. – user4581301 Oct 11 '20 at 00:16
  • @VolapiikVyrient -- The question that hasn't been asked you -- why are you not using [std::forward_list](https://en.cppreference.com/w/cpp/container/forward_list)? It does everything your home-made singly-linked list class does, but safer and without bugs. Even if you can't use that, a strategy you could have used is to first use `std::forward_list` to quickly prototype the solution. Then when that works, go back and work on the home-made singly-linked list class. – PaulMcKenzie Oct 11 '20 at 00:19
  • @PaulMcKenzie You're probably right, I've just never heard of forward_list before and it wasn't mentioned at all in the class I am taking. Will make sure to keep in mind for future projects. – Volapiik Vyrient Oct 11 '20 at 00:21
  • @user4581301 Could you elaborate I am confused. I simply intend to read input until the user has typed '^d' not 'ctrl + d'. I also made sure to display the list after the user inputted it and nothing out the ordinary seems to be in it. – Volapiik Vyrient Oct 11 '20 at 00:23
  • @user4581301 by integer do you mean the integer in the handle_alarm interrupt handler? I thought it just had a default value and that you didn't have to do anything with it. – Volapiik Vyrient Oct 11 '20 at 00:31
  • You have fooled me again. ^ is commonly used used to denote control+. I would have pressed control+d, hit enter, testes whatever value control+d results in against "^d", failed and then the program would abort at `stoi` because of the parsing error. – user4581301 Oct 11 '20 at 01:51
  • @user4581301 ah yes I see what you're saying sorry for the confusion – Volapiik Vyrient Oct 11 '20 at 02:28

1 Answers1

1

I think you need to make some small changes in your roundrobin function for it to work:

You need to update the condition to check if a process is finished to head->value2 <= 0 instead of head->value2 == 0 since head->value2 == 0 seems only to work for any process with value2 is divisible by 3 and it will miss other processes as they will be minus down to negative numbers through this expression head->value2 -= 3

You also need to bring your p->next = NULL; line after head = head->next; instead of before. Otherwise, head will always be NULL as p is currently head.

Finally, you need to check for if head is the only process left (head->next != NULL) before switching to the next process. Otherwise, you would make your head becomes NULL if head->next is NULL, causing segmentation fault error

  void roundrobin(NodeType*& head)
  {
    head->value2 -= 3;
    if (head->value2 <= 0) // no time remaining 
    {
      cout << head->value1 << " Finished" << endl;
      DeleteNode(head);
    }
    else // time remaining
    {
      NodeType* p = head;
      if (head->next != NULL) {
        head = head->next;
        p->next = NULL;
        NodeType* q = head;
        while (q->next != NULL)
          q = q->next;
        q->next = p;
      } 
    }
  }

VietHTran
  • 2,233
  • 2
  • 9
  • 16
  • These comments are all important mistakes I had overlooked thanks a lot for finding them. However, after adding all these changes into my code and running I still fail to get the output of the round robin algorithm. Are you getting the same issue? – Volapiik Vyrient Oct 11 '20 at 01:19
  • My output after all the changes is only the display shown after user has inputted everything and doesn't show changes after the algorithim is used. – Volapiik Vyrient Oct 11 '20 at 01:20
  • @VolapiikVyrient Sorry I couldn't see your output in the comment. Could you update it in your question instead? – VietHTran Oct 11 '20 at 01:26
  • @VolapiikVyrient Oh I see. Yeah I didn't see it initially but I just change the line `volatile sig_atomic_t print_flag = false;` to `volatile sig_atomic_t print_flag = true;` instead and it seems to be outputting the round robin process as expected – VietHTran Oct 11 '20 at 01:29
  • will do, but first I noticed something everything works perfectly if I replaced while(print_flag) with while(true) – Volapiik Vyrient Oct 11 '20 at 01:33
  • Your right making that change to the flag fixes everything! If you aren't too tired could you explain why that worked? I thought the interrupt handler function should set it to true so it shouldn't matter. Once again thanks a lot for the help! – Volapiik Vyrient Oct 11 '20 at 01:37
  • 1
    @VolapiikVyrient Sure I think you need to add `sleep(3);` or greater after every `alarm(3);` as the alarm will only trigger after 3 seconds so you need to hold the main thread from going to the while loop check only after `print_flag` is updated – VietHTran Oct 11 '20 at 01:41