Can you program your game to detect and exit a infinite loop?

Discussion in 'Advanced Programming Discussion' started by Lord KJWilliams, Oct 10, 2019.

  1. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    I wrote a program long ago, in C that detects and exits infinite loops in my programming, if they happen.

    For instance, in order to detect a infinite loop, I use a time counter to count the duration in seconds, of where a loop can happen. I start the counter before the loop starts, and update the counter in the loop in each iteration. If the counter exceeds the number of seconds, it exits it, using either abort(), break, or goto to a label. In GML you dont have labels ( e.g. part1: ) , abort(), or goto ( which is used with labels ). You have break in GML, but if the invention of break is based on C or C++ invention of break, then you can only break outside of one loop. If you have a nested for loops, a break will only break to the the next for loop, which where the only few valid uses of goto works in C. The use of goto is sneered at by C programmers in that community, in most cases where it leads to spaghetti code ( which is a nightmare from my use in Commodore Business Machines BASIC ).

    In my code below , I use a platform specific function called delay() which delays the code using a count in CPU clock cycles. Using CPU clock cycles allows a more tight control of the program since CPU clock cycles are smaller than seconds.

    In this program the anti_hang interrupts the do..while loop before it reaches 5000, and delays each iteration of the loop by 10 CPU clock cycles. GML should have a function that uses a delay function based on CPU processing cycles.

    ( Written using DJGPP for MS-DOS )

    Code:
    // anti-hang function breaks out of continuous loops
    #include <stdio.h>
    #include <time.h>
    
    #include "t_delay.h"
    
    // global variable
    unsigned char kjw_bailout = 1;// in minutes
    
    int anti_hang(char);
    
    int main(void)
    {
       int x = 0;
    
       anti_hang(0);// initialize anti_hang before loop ( or loop nest )
    
       do
       {
         //x++;
         //printf("%04d\r",x);
         time_delay(10);
    
         printf("I cant break out..\n");
    
         // this is where anti_hang works
         if( (anti_hang(1)) == 1) { break; }
    
       } while (x <= 5000);
    
       printf("\n");
       printf("..now anti_hang stopped the endless loop..", x);
    
       return 0;
    }
    
    
    // loop breaker
    int anti_hang(char x)
    {
     
       static int start_time, finish_time;// start and ending time
    
       // set up time variables
       static time_t t;
       static struct tm *s;
       static int timediff = 0;
    
       // Get the starting time from the computers clock
       if (x == 0)
       {
       t = time(NULL); /* gets time of day */
       s = localtime(&t); /* converts date/time to a structure */
       start_time = (int)s->tm_min;// get starting minutes of the hour
       }
    
       // update the difference
       if (x == 1)
       {
       t = time(NULL);
       s = localtime(&t);
       finish_time = (int)s->tm_min; // get ending minutes of the hour
    
       // get the difference time in minutes
       if (start_time > finish_time)
       { timediff = finish_time + (60 - start_time); }
       else
       { timediff = finish_time - start_time; }
       }
    
       // determine whether a loop is not breaking by comparing the
       // timediff with kjw_bailout value
       if(timediff >= kjw_bailout) { return 1; }
    
       // if not then return 0
       return 0;
    }
    
    
    Another invention of mine is a count down , using a do..while loop, which uses goto and a label.
    This is similar to the behavior of how a bomb's counter count downs before it explodes.

    Code:
    
    #include <stdio.h>
    #include <conio.h>
    #include <dos.h>
    
    int main(void)
    {
       double x = 10.00;//user defined delay
    
       do
       {
         printf("%05.2lf...\r",x);//temp
         delay(50);
         x -= 0.01;
    
         // the only acceptable instance of using goto
         if(x < 0) { goto getout; }
    
       } while(!kbhit());
    
        printf("....countdown aborted\n");//temp
        return 1;
    
        getout: ;
    
        printf("The timer ran out\n");//temp
        return 0;
    }
    
    
    In this example of programming , I can set up my code so that if there is an infinite loop, the program can break out of it if the user does not press a key. But in GML there is no provision for breaking out of a nested loop.

    Lets say I have a nested loop and I want to get out of the deepest loop that is inside

    Psuedocode for a counter of time:
    Code:
    int x,y,z,a;
    
    a = 0;// this represents a condition if the loop is detected as a infinite loop.
    
    //hours
    for(x=0;x<10;x++)
    {
       //minutes
       for(y=0;y<10;y++) 
       {
          //seconds - this is the deepest loop
          for(z=0;z<10;z++)
          {
             delay(10);// each second waits for 10 CPU clock cycles
     
             // if a = 0, then its true    
             if (a) { goto getout; }
           }
       }
    }
    
    getout: ;
    
    
    • How would you get out of a nested loop in GML, if you only have break?
    ( Break only breaks to the outer loop in a nested loop. )
    • How would you detect and exit a inifinite loop ?

    Any help appreciated,
    Thanks
     
  2. IndianaBones

    IndianaBones Member

    Joined:
    Jul 5, 2016
    Posts:
    2,207
    I usually just use a counter, if it exceeds a certain value, then I either break out of the loop using break, or if it requires some finesse, an extra boolean.

    Code:
    infinite_loop_prevention_counter = 0
    loop_limit = 1000;
    finished = false;
    found = false;
    
    while ( !finished)
    {
         if ( irandom(1000) == 0 ) // purely for example purposes
        {
            finished = true;
            found = true;
        }
    
        ++infinite_loop_prevention_counter;
        if ( infinite_loop_prevention_counter >= loop_limit )
       {
           finished = true;
           show_debug_message("Loop Limit Exceeded");
       }
    }
    
    if ( !found )
    {
        show_debug_message("Result not found");
    }
    
    I don't think I've ever needed this, but if so, I would just use a similar check per layer of the loop.

    Perhaps there is a sophisticated way to do it, but this way has been adequate for my purposes so far.
     
    Lord KJWilliams likes this.
  3. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    Yes, but can you design a function to test if a loop is infinite , so that you can exit it, and secondly if the loop is nested , thats the question I am asking .
     
  4. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    Rather than go to all this trouble, just don't code an infinite loop. ;)
     
  5. chamaeleon

    chamaeleon Member

    Joined:
    Jun 21, 2016
    Posts:
    975
    Put the potentially "infinite looping" code in a script, and return/exit from it using whatever criteria you want like you do in your examples.
     
    ParodyKnaveBob likes this.
  6. IndianaBones

    IndianaBones Member

    Joined:
    Jul 5, 2016
    Posts:
    2,207
    No, not that I know of, because GMS has basically a single thread of execution and no yield commands. No other code could run externally to check for loops because GMS would be stuck in the loop.

    I suppose you could write an extension that runs on another thread, but I don't know if that would still be able to break an infinite loop in any way.

    I wonder, because I haven't tested it, if async events would still trigger during an infinite loop, that could be used to trigger a global variable that lets you break out of the loop.

    But I would go with not writing an infinite loop in the first place.
     
    Lord KJWilliams likes this.
  7. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    But how do you know if your program is not creating an infinite loop through the procedures of each of your objects that you iterate through repeatedly?

    Secondly, how do you break out of a nested loop system if the inner most loop hangs ?
     
  8. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    Because the program doesn't hang. :)

    You are in search of a solution for a problem that doesn't exist.
     
  9. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    The premise is that you don't know that you have a infinite loop in your program

    I came up with the idea of how to stop one, if you don't know if it exists in the first place. You use a timer that you start outside of the loop, when you enter the loop, you have a call to the timer to update it, so it that counts the time and resets every time in each iteration ( e.g. for..loop ). Therefore, if you measure how long a loop takes normally takes, and the loop takes longer than what it does , you exit the loop if it takes longer than that time.

    Say for instance, you have a procedure that takes 2 seconds for each iteration, but an exception happens that makes it take longer which is not what you wanted, and in doing so your program hangs in a loop because its trying to meet the condition of the for..loop to exit. Now in my idea, if you can detect a infinite loop and exit it, without having to halt the program - you saved yourself from the problem. So creating a procedure, is a good way to save yourself from the problem. Naturally, you want to correct the problem and you don't want to on purpose create a infinite loop. What I am creating is a safety net.

    Its a logical error, that can happen.
     
  10. Bingdom

    Bingdom Googledom

    Joined:
    Jul 1, 2016
    Posts:
    1,678
    Yes, you can.

    In your main object
    Code:
    //Preferably the create event from here
    while(true) {
        var startTime = current_time;
        while(true) {
            if gml_check_freeze(1000) {
                break;
            }
        }
        show_debug_message("We were stuck in the loop for " + string(current_time - startTime) + "ms");
    }
    Then for your gml_check_freeze script
    Code:
    ///@func gml_check_freeze(duration)
    ///@param duration
    //Checks to see if a loop has been running longer than the specified time (in ms)
    var duration = argument0;
    
    if !variable_global_exists("__gml_timer") {
        global.__gml_timer = current_time;
    }
    
    if global.__gml_timer == undefined {
        global.__gml_timer = current_time;
    }
    
    if current_time - global.__gml_timer >= duration {
        global.__gml_timer = undefined;
        return true;
    }
    return false;
    For slightly better optimization, you can initialize global.__gml_timer outside the script. Otherwise, it would work as-is.

    But uh, it should be noted that global.__gml_timer needs to be set back to undefined for every iteration, heh.

    Edit: I think I might've missed the point, as IndianaBones already implemented a similar solution.
     
    Last edited: Oct 10, 2019
    Lord KJWilliams and IndianaBones like this.
  11. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    Sounds very bloody nasty.

    Instead of fixing the problem in the first place, you are exiting this infinite loop and introducing undefined behaviour for the rest of the application.

    Just don't code infinite loops. That's silly.
     
    IndianaBones likes this.
  12. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    Im anticipating the problem, in a situation where it can , it might happen. Haven't you ever considered contingencies that your program does not know how to handle? I believe in Murphy's law "anything that can go wrong , will go wrong". Programs cant be bullet proofed, but you can take measures to anticipate problems. That's how people program in my point of view.

    I had a professor who would take my programs, run them, look at the source code, tell me everything I did not consider in the planing, and then tell me what I did right. Then when I asked him what I could do to improve my programming, he said, " That's not my problem, that's your problem. Your the programmer who is assumed, that you know what your doing. I dont tell my students how to program, I tell them WHAT to program for the assignment, with the methods I have taught them in how to learn and use a programming language such as C, C++, Assembly, and etc. You learn by falling on your face in this profession, and programming in any language takes a lifetime, not a decade of years to master, because technology is always changing."

    So I learn to anticipate what the program will not do right, because the user does not know any better, their just pushing buttons. Thats what a game is , in terms of interaction, a player is just pushing buttons to play the game. One of my responsibilities as the sole author and programmer of the game, is to make sure that the buttons, switches, and triggers work to make the game work right.


    Let's consider my idea, that I need a function that will stop my program from having a infinite loops. I have a program that generates random data, to produce the information for a mapping system 1000 times. If it does not stop after 1000 times or it takes too long in any iteration of 1000, I need to stop it immediately. Heres my solution. I did it in C .... ( see original post ), can we do it in GML?

    Haven't you ever considered this problem when you design your games or programs? I hope I am not the only one here, who thinks this.


     
  13. Catan

    Catan Member

    Joined:
    Jun 20, 2016
    Posts:
    737
    You can break nested loops by using exit , although that will stop the entire event.
    Your code however does not “detect” infinite loops, you are just providing an exit condition inside the loop itself, so as a programmer you still have to remember to include that into the code, and at that point you might as well consider manually how many loops you need to break.

    It’s pointless if you ask me. Of course you want to avoid infinite loops, but in general if you are breaking out of an infinite loop like this, it means you are doing something wrong to begin with, and most likely you end up with some wrong data as a result. I’d prefer my program to hang than having to debug an unknown problem with the logic that follows.

    By your reasoning we should also disable ALL error messages in the program and force it to run no matter what happens, which is silly.
     
    Last edited: Oct 10, 2019
    ParodyKnaveBob likes this.
  14. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    Absolutely agree. I have extremely robust code in all of my applications. But you are going about it the wrong way with your current train of thought.

    Why test to see if you are stuck inside a loop because of a failure somewhere inside the loop? Why wouldn't you have error checking on the parts that might fail instead and report back what failed?


    So your program fails if it has been run on a slow CPU midway through processing? Not how I would do it.

    In this cause, error checking inside the loop should be monitoring any functions that may fail, then and only then, exit due to a failure.

    If you are taking a turd, do you pull up your pants mid way and walk off because it is taking too long?

    Code:
    while(turd == true)
    {
         result = turd();
    
         if(result == TURD_COMPLETED)
               break;
         if(result == IN_PROGRESS)
              keep_turding();
         if(result == BOWEL_OBSTRUCTION)
         {
              seek_medical_advice();
              break;
         }
    }
    
    If there are only three turd states when having a turd, you can't possibly ever get an infinite loop.
     
    Last edited: Oct 10, 2019
    rIKmAN and Catan like this.
  15. Catan

    Catan Member

    Joined:
    Jun 20, 2016
    Posts:
    737
    Best analogy ever ahah. It does fit, because even if you do, you may walk it off as nothing has happened, but the turd is still in your pants. (let's not overthink this in the context of an infinite loop though...)
     
    Lonewolff likes this.
  16. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    Code:
    fly_from_london_to_new_york()
    
    while(flying)
    {
           if(taking_too_long)
                 exit_plane();
    }
    
     
    BattleRifle BR55 likes this.
  17. YellowAfterlife

    YellowAfterlife ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    2,404
    You can break out of multiple loops by introducing a flag variable to the condition(s)
    Code:
    var loop = true;
    while (loop && ...) {
        ...
        while (...) {
            ...
            if (...) { loop = false; break; }
        }
    }
    However, as people have mentioned, instead of attempting adventurous workarounds for a problem, consider why would you have a loop without a definitive exit condition to begin with.

    If you are trying to find an empty grid cell to put something on, for instance, instead of hitting random ones till you get one you can do a pass over all cells to count up candidates, then pick a random one from those (either storing them or doing a second half-pass to find one by number).
    If you are generating arbitrary values, structure your algorithm to produce correct ranges to begin with (gaps or overlaps aren't hard to code), or introduce a counter for max attempts before you default to a dummy value.
     
  18. Yal

    Yal GMC Memer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    3,703
    I'm baffled that nobody has pointed out that the Halting Problem, which essentially is about whether you can detect if a program will loop infinitely or end eventually - as the topic title suggests this discussion is about - has been mathematically proven impossible to solve. It's nerdy, and not very practical, but I still thinks it's good to be aware of this. It means there's no good solution available, so any hacky solution you find that works... that's probably going to be the best you can get, because there's no true solution.

    I'm with everyone else on this: figure out an upper bound for how many times the thing SHOULD iterate at most, add a counter that is incremented in each iteration, and abort if either the loop reaches its end condition OR the counter passes its max value. For grids, the upper bound could be based on the total number of cells, for AI it could be based on timing measurements when profiling the code (so that you don't make the game freeze for too long while the AI makes decisions, but still lets it run a reasonable amount of time), and you can of course always just take some random number that sounds nice and use that.

    The good thing with a forced bound is that you know how messy and CPU-greedy this thing will become in the worst case. If you only have an exit condition, you don't. I doubt GM will ever be used to control the brakes of a car or some other real-time-critical service, but even for a game there's pretty tight realtime requirements (or people will complain about lag spikes or long loading times) so it doesn't hurt to think like a realtime system engineer, trying to minimize and control worst-case scenarios in the first hand and improving average performace second.
     
  19. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    I'm more worried about how far he is taking his Professors advice on making sure everything works correctly.

    If I code an infinite loop, why would I need a routine to safeguard against it? Just fix the bug.

    What if your RAM is faulty and a variable doesn't store correctly? Do I store 16 of the same variable and do an operational 'and' on them to make sure they agree?

    He is taking scuba gear in to a shower, in the event that the plug might get clogged.


    If he talks to his Professor about this endless loop scenario, I am sure the Professor will agree. Just fix the source of the problem. Not that complex.
     
    Lord KJWilliams likes this.
  20. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    Well my professor stated, that as a programmer, I have to consider all the details. Its my program, its responsiblity, its my fault should it go wrong. If there is a problem, I should fix it and not let exist. In a game, like I said, in a strange analogy, Im responsible for the buttons working and making sure that the game does by what the program is supposed to do. I believe that is possible that you can set up a infinite loop, and you do not know it, until you find it. So my idea is to have a safeguard. Sure, its more work to my plate and it consumes more RAM in the memory. Its called a logical error, a really nasty problem. Im not talking about a small piece of code, I am talking about in the behavior of the whole program, its possible that it can happen. Thats why I posted, "...Can you program your to detect and exit a infinite loop?". It is a contingency plan, thats all. Whether a infinite loop will exist or not, that is not the problem in any small piece of code. People make mistakes in code all the time. I am no exception. I think my idea is a good one. Im not saying that you should follow me and do the same.

    My professor has been reading what I have been posting here, because I have been showing it to him when I log on at the college computer lab. He likes what I have saying and he likes what all of you have been saying too. He says that one of faults of many students, is that many do not broaden their experiences with talking to other programmers of different calibers , and instead develop a "tunnel vision" in their programming philosophy by only associating with programmers of the same discipline of learning.
     
  21. rIKmAN

    rIKmAN Member

    Joined:
    Sep 6, 2016
    Posts:
    4,525
    So using that logic, if your program has an infinite loop and freezes / crashes - find it, fix it and don't let it exist.
     
  22. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    We all make mistakes, for sure.

    My personal preference for sections of code I am dubious about is to splatter the code with debug messages, which you could turn on or off with a simple variable 'flag' at the start of the program code.

    Code:
    while(true)
    {
         show_debug_message(" Inside loop blahblah ");
    }
    
    If all you see in the output window is the following and the program isn't doing what it is meant to do, you know exactly where your faulty code lies.

    Then you fix the bad section of code.

    With the safe guards you are proposing you will have exited out of the loop without knowing there was an issue and the program will likely run in an undefined way, possibly making you think the errors are somewhere else in your code.

    The more of these 'safe guards' you add in, the more error prone your code will become. Not to mention, you will kill performance.

    Keep it simple :)
     
  23. IndianaBones

    IndianaBones Member

    Joined:
    Jul 5, 2016
    Posts:
    2,207
    Functionally that's what I did, just using frame count rather than time, and inside the loop itself rather than calling a script. Using time for this case could have unknown side-effects depending on the speed of the system its running on.

    Contain the loop in its own script, then you could use exit or return to leave the script ( and therefore the loop) completely.

    Interestingly, I just checked my latest project, I'm only using a while loop in two places(out of 30,000+ lines of code, for perspective). One is checking for end-of-file when loading a text file, the other is used when processing all elements in a priority queue, exit condition is when the queue is empty.

    I'm confident in these cases that there won't be an infinite loop, if there was it would mean there are bigger problems and/or bugs with the underlying system that need to be dealt with.

    In my experience, when I use while() and I worry about it getting stuck in an infinite loop, that means I am not 100% clear on the side effects of my code, and probably shouldn't be doing that!

    You may know this already, but I'll just point out that in mission critical applications, such as spacecraft/aircraft they do need allow for extreme code/instrument failures. So what they do is have for example 3 independent computers/hardware for each critical system. Each of the systems are built and coded by different companies, so if there is a software or hardware fault in one, it won't be shared with the other 2. Then a central controller takes all 3 inputs and verifies they all agree. If one computer fails or gives different readouts, it can fall back to the other 2 systems. They may also have a backup system kept offline for use if the main system fails.

    So maybe if you really want to avoid unknown infinite loops, you would want a separate piece of hardware monitoring your main hardware/software, and if it detects no response from the application, it would take over.
     
    ParodyKnaveBob and Nocturne like this.
  24. Yal

    Yal GMC Memer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    3,703
    This is a well-known concept in embedded systems, and it called a "watchdog timer" if you want to research it further. The idea is that you have a task with the lowest possible priority that keeps sending an "everything is all right" signal every once in a while; if the signal isn't sent in time, something bad has happened to the system (for instance an infinite loop, but also infinite waiting for a hardware interrupt, too high system load, etc), and the system is power-cycled. There's both hardware (unit separate from the main core) and software (highest-priority periodical background task) watchdogs.
     
  25. Coded Games

    Coded Games Member

    Joined:
    Jun 20, 2016
    Posts:
    413
    As people have already said this is the halting problem. I recently saw this video that does a pretty good job at explaining the proof for why it is impossible:
     
  26. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    I dont think its undefined behavior, in programming C its perfectly sound. You just have to know how to use it
     
  27. Lord KJWilliams

    Lord KJWilliams Member

    Joined:
    Jul 2, 2018
    Posts:
    106
    However, I am talking about the type of infinite loops that you cant find, that is created by how your program routes itself around your source code in its logic that you have defined. Its called a logical error. A logical error is syntactically correct, it compiles with out a problem, and runs as exactly as you have told it to, but somewhere there is a combination of values that lead it to the exception of a infinite loop.
     
  28. Coded Games

    Coded Games Member

    Joined:
    Jun 20, 2016
    Posts:
    413
    Like with most programs, this all is dependent on your use case. If you have some code that is dependent on a loop finishing, adding a system that might force exit the loop will cause problems and unexpected behavior.

    Also one major thing to consider is how much overhead your antihang system uses. As a quick test, I modified your C code to do a little bit of work. It will do 10,000,000 random calculations of a * b / c where each of those are random doubles from 1 to 100000. Without hang detection it takes about 8 seconds, and with hang detection it took about 12 seconds (if I don't let it break out of the loop).

    This is a worst case scenario, where each iteration of the loop does very little work but loops millions of times, but you have to consider if a 50% performance loss is worth it. Any anti hang system is going to introduce some sort of overhead.

    So overall, can you program your game to detect and exit infinite loops? Yes. But is it always necessary or worth it? It depends.

    Code:
    // anti-hang function breaks out of continuous loops
    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    
    // global variable
    unsigned char kjw_bailout = 1;// in minutes
    
    int anti_hang(char);
    
    int main(void)
    {
        int x = 0;
    
        clock_t start, end;
        double cpu_time_used;
    
        start = clock();
    
        anti_hang(0);// initialize anti_hang before loop ( or loop nest )
    
        do
        {
            double a = (rand() % 100000) + 1;
            double b = (rand() % 100000) + 1;
            double c = (rand() % 100000) + 1;
            double d = a * b / c;
            x++;
            printf("%f", d);
            //time_delay(10);
    
            printf("I cant break out..\n");
    
            // this is where anti_hang works
            if( (anti_hang(1)) == 1) {
                printf("..now anti_hang stopped the endless loop..");
                //break;
                }
    
        } while (x <= 10000000);
    
        printf("\n");
    
        end = clock();
        cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
    
        printf("\n\n%f seconds to execute \n", cpu_time_used);
    
        return 0;
    }
    
    
    // loop breaker
    int anti_hang(char x)
    {
    
        static int start_time, finish_time;// start and ending time
    
        // set up time variables
        static time_t t;
        static struct tm *s;
        static int timediff = 0;
    
        // Get the starting time from the computers clock
        if (x == 0)
        {
            t = time(NULL); /* gets time of day */
            s = localtime(&t); /* converts date/time to a structure */
            start_time = (int)s->tm_min;// get starting minutes of the hour
        }
    
        // update the difference
        if (x == 1)
        {
            t = time(NULL);
            s = localtime(&t);
            finish_time = (int)s->tm_min; // get ending minutes of the hour
    
            // get the difference time in minutes
            if (start_time > finish_time)
            { timediff = finish_time + (60 - start_time); }
            else
            { timediff = finish_time - start_time; }
        }
    
        // determine whether a loop is not breaking by comparing the
        // timediff with kjw_bailout value
        if(timediff >= kjw_bailout) { return 1; }
    
        // if not then return 0
        return 0;
    }
    
     
  29. Lonewolff

    Lonewolff Member

    Joined:
    Jan 8, 2018
    Posts:
    1,207
    Rubbish.

    No one walks away mid turd in C or C++ because they think it is taking too long.

    If a loop has a purpose for being there, you should never quit out after a certain time period, in the assumption that something has gone wrong. If you do, and your program goes on its merry way you are very much creating undefined behaviour.

    If you know the answer allready, why are you posting questions?
     
    Last edited: Oct 12, 2019 at 9:26 AM
  30. GMWolf

    GMWolf aka fel666

    Joined:
    Jun 21, 2016
    Posts:
    3,442
    To answer the question:
    Is a flag! Something like:
    Code:
    var abort = false;
    while(!abort && some_condition())
    {
        //Lots of logic, could be more loops
        //But it sets abort when it's fed up
    }
    if (abort)
    //Handle abort case
    else
    //Handle success case
    
    You might like to rewrite the loops such that it sets a success flag instead. I think that's neater.

    Of course this is useful for lots of reasons! You could early out of a nested loop if you found a solution already, etc.

    But as a mechanism to end an infinite loop? Yikes! How close to shipping are you to need these kinds of hacks?

    As others have said in this thread, if you get the time, write your loop in a way that it will provably terminate! (The abort flag is one way to do it). Gotoing out is not great because it's not clear.
    I'm a great believer in making sure you can always follow a piece of code without looking into the blocks.
    That means that I should understand the flow.
    If I see the following:
    Code:
    While(foo == 0)
    {
      //lines and lines of code
      if (rick < 0) break;
      //Lines and lines of code
    }
    bar = baz / foo;
    
    Without Looking at the body understand that when the loop exits, foo will no longer be 0. So dividing by foo should be safe.

    But if you look into the body you see that it decides not to handle negative numbers then my division will break!
    It becomes a pain to debug because the loop tells me it's not going to be 0!

    Instead it would be much nicer to write the loop as follows:
    Code:
    While(foo == 0 && rick >= 0)
    {
      //lines and lines of code
      if (rick >= 0)
      {
          //Lines and lines of code
       }
    }
    //!!! Can no longer assume foo is not 0
    bar = (foo != 0) ? baz / foo : 0!
    
    Crisis averted!


    Other things too: avoid continue and return in the middle of loops. If I see this:
    Code:
    while(flying)
    {
       // code!
       // More code
       // And still more code
       adjust_pitch_and_yaw();
    }
    
    I see the loop, and see that as long as the plane is flying, pitch and yaw should adjust!
    But sometimes the plane is flying, but stops adjusting pitch and yaw.

    I might look at the pitch and yaw function; 1h of debugging: looks good. Check if it's being called when it should. Nope, it's not called when it crashes. That means that the "flying" condition isn't working. Spend a. Hour checking that: looks good too!

    Then I actually look at the code again and notice this:

    Code:
    while(flying)
    {
       // code!
       if (pilot_is_dead) continue;
       // And still more code
       adjust_pitch_and_yaw();
    }
    
    Debugging the pilot_is_dead condition shows it we forgot to feed them! Yikes! All that debugging for such a simple problem!

    It could have been avoided if the code was better written:

    Code:
    while(flying)
    {
       // code!
       if (!pilot_is_dead) {
            // And still more code
            adjust_pitch_and_yaw();
       }
    }
    
    Now I can immediately see that adjust_pitch_and_yaw is not always called when flying, it has another condition.



    Now these were pretty simple examples but as you may know production code is much bigger, more complicated, and when debugging there are often many possibilities to explore. Making those possibilities clear in code is very important.
     
    Last edited: Oct 12, 2019 at 10:37 AM
  31. chamaeleon

    chamaeleon Member

    Joined:
    Jun 21, 2016
    Posts:
    975
    By definition, you don't have an infinite loop happening if you have conditions in place to break out of loops based on time or counters. On the other hand, just because you have these checks in place, the halting problem ensures you cannot guarantee it will always do the trick for arbitrary programs. The more code you have, the more difficult it is to ensure that you do the right thing at the right place with 100% certainty, be it "not writing infinite loops" or "putting checks in place to break out of infinite loops".
     
    Lord KJWilliams likes this.
  32. MishMash

    MishMash Member

    Joined:
    Jun 20, 2016
    Posts:
    379
    So yeah.. as others have said, having a better understanding of what your code can do and avoiding this situation is more important. Patching code with a high level fix to alleviate part of a problem is just a horrible way of going about things, and you should avoid it at all costs.

    This type of thinking is actually most prominent in multi-threaded code. Developers don't have confidence in their solutions, or perhaps they don't understand the exact nature of the system, so when attempting to synchronise between threads, they start slapping synchronisation primitives such as mutex locks and semaphores all over the place to try and prevent deadlocks or livelocks. However, the crux here is that you aren't really avoiding the problem, all you are doing now is whacking a bunch of band-aids on the code and hoping you've plugged all of the holes. Whereas, what you should ideally do is tear the system down to its essentials, understand better what is going on, try and resolve any issues which prevent you from optimising your code in the way you want.
    For example, the dining philosophers problem, where there are N philosphers dining, but only N-1 forks; this is used as a fun example to demonstrate how you can have a seemingly unsolveable synchronisation problem, and how do you deal with it? Well, in this case, you actually change your problem somehwat, and introduce a "waiter" who is responsible for distributing forks as a diner needs one.

    REDESIGNING THE PROBLEM

    Now, that example may not seem relevant, but rather than trying to work out "how can I get out of an infinite loop?", which you could do by having a flag variable and rippling that up the chain, as others have suggested. Or... you could instead re-design your solution so that you don't run into this problem in the first place.
    One example of something you could do is a job queue. Rather than nesting loops for emergent functionality, objects that want to execute some code can issue "tasks" which will be executed later. This is exactly how Javascript (Node JS) works with its "event loop" and why it is so efficient for web services:

    Rather than having it so that every time someone connects to a website and we need to generate a page, having this huge, deep script, which runs hundreds of routines and sits spinning on a CPU for ages, we split our work into manageable chunks of tasks. For example, if we need to do a database lookup, then we may use futures to issue an async task which will get added to the event queue and execute at a slightly later point in time. The main benefit of this is that we can continue with other jobs while we wait for a database query to execute, but the other point is that we never get too deep into a singular chain, and we form a queue of dependent tasks. I admit, this is something quite difficult in GM, but it's certainly possible.

    What you would do is whenever an instance needs to run a loop of its own, it could instead generate a bunch of tasks and add them onto the end of a queue. It's hard to suggest a specific design without knowing exactly what you are doing, but you could have some limits at the high level for the number of pending jobs each task can submit.

    I did something similar to this for my pathfinding system. Instances submit pathfinding requests, but the system performs a capped number of pathfinding iterations per frame (to avoid stuttering and lag). The system would perform as many steps of the algorithm as possible, but would never exceed a given limit. if a certain path required more to find, then its processing would take a couple of frames, or for very very complex paths, even a few seconds. The idea here though is we never get stuck in an infinite loop, waiting for an algorithm to complete or a path to find, instead we have a task queue of paths that we want to resolve, and work on ONE at a time.


    On another note...
    Given I also see that the question changed somewhat throughout the thread, from a hypothetical to tracking down an unknown infinite loop, this is kind of two different issues. Firstly, this can only happen in a loop, and should be relatively quick to find. Any situation where you have a loop with a waiting condition that isn't a counter will be susceptible, alternatively, any loop with a counter where the counter variable is modified in different ways will also be places to check. In C++, if debugging and you hit this loop, then you can just pause your program to see what line of code is executing. I believe you should be able to do something similar in the GM debugger by hitting the pause button. Equally, the profiler would then give you a hint, as you imagine you'd get stuck in one place, the profiler counters would increase for very specific functions. But this finding in a debugger is separate to having some runtime system to do it, you should never really have that, as you should know what your code does, and once you find an infinite loop opportunity through testing, you should consider redesigning that part of the code.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice