Question

I observed something in a log file that I cannot explain:

All code in project is ANSI C, 32bit exe running on Windows 7 64bit

I have a worker function similar to this one, running in a single threaded program, using no recursion. During debugging logging was included as shown:

//This function is called from an event handler
//triggered by a UI timer similar in concept to 
//C# `Timer.OnTick` or C++ Timer::OnTick
//with tick period set to a shorter duration
//than this worker function sometimes requires
int LoadState(int state)
{
    WriteToLog("Entering ->");  //first call in
    //...
    //Some additional code - varies in execution time, but typically ~100ms.
    //...
    WriteToLog("Leaving  <-");//second to last call out

    return 0;
}  

The function above is simplified from our actual code but is sufficient for illustrating the issue.

Occasionally we have seen log entries such as this:

enter image description here

Where the time/date stamp is on the left, then message, the last field is duration in clock() ticks between calls to logging function. This logging indicates that the function was entered twice in a row before exiting.

Without recursion, and in a single threaded program, how is it (or is it) possible that execution flow can enter a function twice before the first call was completed?

EDIT: (to show top call of logging function)

int WriteToLog(char* str)
{
    FILE* log;
    char *tmStr;
    ssize_t size;
    char pn[MAX_PATHNAME_LEN];
    char path[MAX_PATHNAME_LEN], base[50], ext[5];
    char LocationKeep[MAX_PATHNAME_LEN];
    static unsigned long long index = 0;

    if(str)
    {
        if(FileExists(LOGFILE, &size))
        {
            strcpy(pn,LOGFILE);
            ManageLogs(pn, LOGSIZE);
            tmStr = calloc(25, sizeof(char));
            log = fopen(LOGFILE, "a+");
            if (log == NULL)
            {
                free(tmStr);
                return -1;
            }
            //fprintf(log, "%10llu %s: %s - %d\n", index++, GetTimeString(tmStr), str, GetClockCycles());
            fprintf(log, "%s: %s - %d\n", GetTimeString(tmStr), str, GetClockCycles());
            //fprintf(log, "%s: %s\n",  GetTimeString(tmStr), str);
            fclose(log);
            free(tmStr);
        }
        else
        {
            strcpy(LocationKeep, LOGFILE);
            GetFileParts(LocationKeep, path, base, ext);
            CheckAndOrCreateDirectories(path);
            tmStr = calloc(25, sizeof(char));
            log = fopen(LOGFILE, "a+");
            if (log == NULL)
            {
                free(tmStr);
                return -1;
            }
            fprintf(log, "%s: %s - %d\n", GetTimeString(tmStr), str, GetClockCycles());
            //fprintf(log, "%s: %s\n",  GetTimeString(tmStr), str);
            fclose(log);
            free(tmStr);
        }
    }
    return 0;
}
Was it helpful?

Solution

I asked the question wondering at the time if there was some obscure part of the C standard that allowed execution flow to enter a function more than once without first exiting (given that multi-threading or recursion was not present)

Your comments, I believe, have clearly answered the question. Borrowing from what @Oli Charlesworth said in one comment, he summarizes it up pretty well:

If the code is truly single-threaded, and the log-function is truly sane, and there's truly no other piece of code that can be outputting to the log, then obviously this can't happen (UB notwithstanding).

But since the actual log files (which I could not post for proprietary reasons) on several occasions have demonstrated this pattern, one of the conditions @Oli Charlesworth listed is not actually true for our software. My best guess at this point, given that the logging function is sane and is the only input to the file, is to consider the alternate context/Fiber possibility suggested by @jxh:

"Primary thread only" can mean multiple things. The library could still possibly use <ucontext.h> on POSIX, or Fibers on Windows.

So, I will post this same question to the supplier of my environment, specifically if their UI Timers are run in such a way as to allow parallel calls due to a fiber or thread.

If any are interested, I will also update this answer with their response.

Edit to show conclusion:

As it turns out, the cause of the double entry of execution flow into a function was implicit recursion. That is, while the worker function did not reference itself explicitly, it was designated as the event handler for two separate event generators. That, coupled with a call to Process System Events (a function available in our environment forcing events in the queue to be processed now) can (and did) result in recursive execution flow into the event handler function. Here is a quote from a person who has expertise in the relationship between UI timers and system events in our environment:

"Timer events being nested" does equate to execution flow entering a function twice before leaving. Basically, it's the same thing as basic recursion: while you're inside one function, you call that same function. The only difference between this case and basic recursion is that the recursion call is implicit (via ProcessSystemEvents) and not explicit. But the end result is the same."

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top