1

I hava some trouble understanding about guard flag in the following code.

// User-defined operator new.  
    void *operator new( size_t stAllocateBlock ) {  
       static int fInOpNew = 0;   // Guard flag.  

       if ( fLogMemory && !fInOpNew ) {  
          fInOpNew = 1;  
          clog << "Memory block " << ++cBlocksAllocated  
              << " allocated for " << stAllocateBlock  
              << " bytes\n";  
          fInOpNew = 0;  
       }  
       return malloc( stAllocateBlock );  
    } 

Anyone help to understand this ?

Thanks

EDIT: Thanks for clearing my doubt, and I understand this now. I got this code from here: https://msdn.microsoft.com/en-us/library/kftdy56f.aspx, they used same guard for delete also. Is the reason same ?

Please check my code, and share your suggestions to improve the code. Also, how to protect the code in multi threading with an example ? I would really appreciate this and it will clear my understanding about thread safe code, and also memory management very well.

Thanks

My version of code:

/*
 * Memory leak detector
 */

#include <iostream> // for cout, clog
#include <new>      // for size_t

using std::clog;
using std::cout;


/* Logging toggle, 0x0 - No, 0xFF - Yes */
char    logMemory;

/* Number of Memory blocks allocated */
int     memoryBlocksAllocated;

void* AllocateMemory(size_t size)
{
    /*
     * Guard flag for protecting from reentrancy (recursion),
     * as clog also make use of new operator for
     * allocating memory to the buffer.
     */

    static char InOperationNew = 0x0;

    if ( logMemory && !InOperationNew ) {
        InOperationNew = 0xFF;
        clog << "Memory block allocated " << ++memoryBlocksAllocated
        <<" for requested size " << size << " bytes\n";
        InOperationNew = 0x0;
    }

    return malloc(size);
}

void ReleaseMemory(void *ptr)
{
    /*
     * Guard flag for protecting from reentrancy (recursion),
     * as clog also make use of new operator for
     * allocating memory to the buffer.
     */

    static char InOperationDelete = 0x0;


    if ( logMemory && !InOperationDelete ) {
        InOperationDelete = 0xFF;
        clog << "Memory block deallocated " << memoryBlocksAllocated-- << "\n";
        InOperationDelete = 0x0;
    }

    free(ptr);
}

/* User defined(overriden) global scope new operator */
void* operator new (size_t size)
{
    return AllocateMemory(size);
}

/* User defined(overriden) global scope new[] array operator */
void* operator new[] (size_t size)
{
    return AllocateMemory(size);
}


/* User defined(overriden) global scope delete operator */
void operator delete (void *ptr)
{
    ReleaseMemory(ptr);
}

/* User defined(overriden) global scope delete[] array operator */
void operator delete[] (void *ptr)
{
    ReleaseMemory(ptr);
}

int main()
{
    logMemory = 0xFF;   // Enable logging

    // Allocate and destroy an array of objects
    char *objArray = new char[2];
    if ( objArray )
    {
        delete[] objArray;
    }
    else {
        cout << "Memory allocation failed\n";
        return -1;
    }

    // Allocate and destroy an object
    char *obj = new char;
    if ( obj )
    {
        delete obj;
    }
    else {
        cout << "Memory allocation failed\n";
        return -1;
    }

    logMemory = 0x0;    // Disable logging

    return 0;
}
Dinesh G
  • 129
  • 7

1 Answers1

1

From what I understand this is an attempt to avoid clobbering of the log stream by multiple threads.

Say two threads call the function at the same time. The messages being printed would get mixed (depending on the buffering mechanism used by the runtime).

To avoid this the programmer has created a static flag. Static variables local to a function are shared across all calls.

The intent is that when one call is logging the message it will first set the flag to 1. This will not allow other threads to enter. (See if !fInOpNew)

When it is done printing it sets the flag back to 0.

Though this mechanism at avoiding race condition in not going to work. Since the Two threads can enter the if condition and then the flag can be set.

The programmer needs to use atomic primitives like compare and swap to ensure protection of critical section.

Also use of static variables won't be safe. If two threads write to it simultaneously, it can get a value not equal to 0 or 1.

Edit : As suggested by Toby Speight, another purpose of the flag to avoid reentrance. calling the << operator on clog is also a function that might make use of the new operator (could be the same new operator). This will lead to an infinite recursion of this function. The flag makes sure that the logging in not invoked when the << operator calls new.

The assumption that the program is single threaded doesn't bring in race condition.

Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • 1
    I second this. I'm used to such "guards" from exercises at the university in which one has to state why this does not work, getting a basic understanding that even variable = value is not an atomic operation (well, in the case of the posted code, there are a lot more things wrong as you stated). – Aziuth Mar 01 '17 at 09:47
  • 1
    I'd say it's more likely an attempt to avoid reentrancy (and infinite recursion) when `clog.operator<<` needs to allocate memory. Output from other threads *may* be affected (since `fInOpNew` isn't thread-local) but I think whoever wrote this was assuming single-thread operation. – Toby Speight Mar 01 '17 at 09:53
  • Oh, that makes a lot more sense. – Ajay Brahmakshatriya Mar 01 '17 at 09:57
  • Can you check my code and suggest me anything we can improve on this ? Also, how to protect this code from multi thread ? – Dinesh G Mar 01 '17 at 11:17
  • Yes, delete has the exact same reason. Now for the second part if you want to make this safe for multiple threads, you can enclose the logging part inside a mutex lock/semaphores. They can be used to protect against race conditions – Ajay Brahmakshatriya Mar 01 '17 at 11:23
  • Ok, Is there anything we can do to improve my code ? Can you take a look at the code and suggest me for improvements as I want to learn ? Thanks – Dinesh G Mar 01 '17 at 11:40
  • Your code seems correct. Although there is another thing I would suggest. Use a common guard flag for both new and delete ( you can make it into a global variable instead of a static variable). This will avoid printing of "new" messages when delete is called (if new is invoke by clog in the process) and vice versa. – Ajay Brahmakshatriya Mar 01 '17 at 11:44