In C ++, how can I prevent a function called recursively

I have a function that uses memory on the heap, and it will be badly mistaken if it is called before another instance of the same function completes. How can I prevent this at compile time?

+5
source share
12 answers

Finding recursion with any amount of determinism at compile time will be quite difficult. Some static code analysis tools can do this, but even then, you can move on to run-time scripts involving threads that code analyzers cannot detect.

You must detect recursion at runtime. In fact, this is very simple:

bool MyFnSimple()
{
    static bool entered = false;
    if( entered )
    {
        cout << "Re-entered function!" << endl;
        return false;
    }
    entered = true;

    // ...

    entered = false;
    return true;
}

, , , -. , - , . Windows ( ):

bool MyFnCritSecBlocking()
{
    static HANDLE cs = CreateMutex(0, 0, 0);
    WaitForSingleObject(cs, INFINITE);
    // ... do stuff
    ReleaseMutex(cs);
    return true;
}

, , :

bool MyFnCritSecNonBlocking()
{
    static HANDLE cs = CreateMutex(0, 0, 0);
    DWORD ret = WaitForSingleObject(cs, 0);
    if( WAIT_TIMEOUT == ret )
        return false;   // someone already in here
    // ... do stuff
    ReleaseMutex(cs);
    return true;
}

, , bools critsecs. Windows:

bool MyFnInterlocked()
{
    static LONG volatile entered = 0;
    LONG ret = InterlockedCompareExchange(&entered, 1, 0);
    if( ret == 1 )
        return false;   // someone already in here
    // ... do stuff
    InterlockedExchange(&entered, 0);
    return false;
}

, , . , . RAII, .

:

, , , RAII, , , RAII . RAII, , , - :

#include <windows.h>
#include <cstdlib>
#include <stdexcept>
#include <iostream>

class CritSecLock
{
public:
    CritSecLock(HANDLE cs) : cs_(cs)
    {
        DWORD ret = WaitForSingleObject(cs_, INFINITE);
        if( ret != WAIT_OBJECT_0 ) 
            throw std::runtime_error("Unable To Acquire Mutex");
        std::cout << "Locked" << std::endl;
    }
    ~CritSecLock()
    {
        std::cout << "Unlocked" << std::endl;
        ReleaseMutex(cs_);
    }
private:
    HANDLE cs_;
};

bool MyFnPrimitiveRAII()
{
    static HANDLE cs = CreateMutex(0, 0, 0);
    try
    {
        CritSecLock lock(cs);
        // ... do stuff
        throw std::runtime_error("kerflewy!");
        return true;
    }
    catch(...)
    {
        // something went wrong 
        // either with the CritSecLock instantiation
        // or with the 'do stuff' code
        std::cout << "ErrorDetected" << std::endl;
        return false;
    }
}

int main()
{
    MyFnPrimitiveRAII();
    return 0;
}
+7

, ( ) ( )?

, .

, , . , , , , .

+9

. :

#include <cassert>

class simple_lock
{
public:
    simple_lock(bool& pLock):
    mLock(pLock)
    {
        assert(!mLock && "recursive call");
        mLock = true;
    }

    ~simple_lock(void)
    {
        mLock = false;
    }

private:
    simple_lock(const simple_lock&);
    simple_lock& operator=(const simple_lock&);

    bool& mLock;
};

#define ASSERT_RECURSION static bool _lockFlag = false; \
                            simple_lock _lock(_lockFlag)

void foo(void)
{
    ASSERT_RECURSION;

    foo();
}

int main(void)
{
    foo();
    //foo();
}
+6

- . , :

. , - .

void myFunc() {
  static int locked = 0;
  if (locked++)
  {
    printf("recursion detected\n!");
  }

  ....

  locked--;
}

. .c .cc, .

, pthread , .

+5

. . .

+4

. , . .

* edit *

, ?

, .

+2

. , , ?.

+2

c++-faq-lite : , :

// We'll fire you if you try recursion here

,

+2

, , (, , , , , , ...).

. . , , , - .

: (boost scoped_lock ).

+1

#include <stdio.h>

int testFunc() {
#define testFunc
printf("Ok\n");
}
#undef testFunc
int main() { testFunc(); }

#include <stdio.h>

int testFunc() {
#define testFunc
printf("Ok\n");
testFunc();
}
#undef testFunc
int main() { testFunc(); }

: test.c:7: error: expected expression before ‘)’ token

:

#include <stdio.h>

int testFunc1() {
#define testFunc1
printf("1\n");
testFunc2();
}

int testFunc2() {
#define testFunc2
printf("2\n");
//uncomment to cause error: `test.c:13: error: expected expression before ‘)’ token`
//testFunc1();
}

#undef testFunc1
#undef testFunc2

int main() { testFunc1(); }
+1

- , , . , static - .

, , , , main , , .

, ++ , main undefined, , main . main , .

, .

And I expected you to want the compiler to give you an error when you tried anyway, and not just make sure that simple was not in any possible way, you could do it.

0
source

Here is my solution to prevent single-threaded re-entry using RAII:

    struct NestingTracker
    {
        NestingTracker(uint32_t &cnt) : _cnt(cnt) { ++_cnt; }
        ~NestingTracker()                         { assert(_cnt); --_cnt; }
        uint32_t& _cnt;
    };

#define NO_REENTRY(x)                        \
    static uint32_t cur_nesting = 0;         \
    if (cur_nesting)                         \
        return (x);                             \
    NestingTracker nesting_track(cur_nesting)

use as

void f(...)
{
    NO_REENTRY(void());
    ...
}
0
source

All Articles