5

I'm facing an issue with std::map. For unknown reasons sometimes insertions to map lead to a "bad allocation" exception.

Below is the function which I use for inserting into the map.

BOOL Add2WaitList(Object<LPVOID> *newObj)
{
    try
    {
        _set_se_translator( trans_func );
        m_syncWQ.Lock();
        if (m_waitingQueue.count(newObj->uid)>0)
        {
            m_syncWQ.Unlock();
            return FALSE;
        }
        m_waitingQueue[newObj->uid] = *newObj; <-- failing here
        m_syncWQ.Unlock();
        return TRUE;
    }
    catch(std::exception &ex){
        ...
    }
    catch(SE_Exception &e){
        ...
    }
    catch(...){
        ...
    }
}

Can someone tell me how to solve this?

NOTE: I cannot identify the steps to reproduce it.

THX in advance!

Adding details about Object & map:

template <typename T>
struct Object{
public:
    void Kill()
    {
        if (response!=NULL)
            delete response;
        if (object!=NULL)
            delete object;
    }

    enum objType;
    std::string uid;
    enum status;
    double p;
    enum execType;
    T object;
    LPVOID response;
};

std::map<std::string,Object<LPVOID>> m_waitingQueue;
TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52
SairuS
  • 149
  • 1
  • 2
  • 11
  • its a typo mistake fixed. – SairuS Jul 19 '13 at 08:07
  • With a shallow copy default copy constructor and assignment operator, although it contains pointers. Very suspicious. – James Kanze Jul 19 '13 at 08:28
  • Pointers are used because I add there different types. how this can affect my issue? – SairuS Jul 19 '13 at 08:38
  • Pointers usually imply dynamic allocation somewhere. Which can lead to things like double delete if there is no user defined copy constructor (or if it does the wrong thing). Things like double delete _can_ corrupt the free space arena, or lead to other code overwriting parts of `std::map`'s internal representation. – James Kanze Jul 19 '13 at 08:55
  • That is not the actual code of your `Object` type, since the code you give won't compile. – James Kanze Jul 19 '13 at 09:12
  • And when do you call `Object::Kill`? If you actually have a destructor which calls it, and no copy constructor, that is your problem (or one of them, at least). – James Kanze Jul 19 '13 at 09:13
  • @JamesKanze can you PLS explain me more about double delete? how default copy constructor can lead to double delete? – SairuS Jul 19 '13 at 09:14
  • I cannot expose project, just replace enum with int and Object should work. Regarding Kill, it's called in some conditions controlled by us (usually when we finish to process the object). – SairuS Jul 19 '13 at 09:46
  • Double delete is when you delete the same pointer twice. In this case, if you called `Kill` twice on the same object (or a copy of the object) without having changed `response` or `object`. – James Kanze Jul 19 '13 at 09:52
  • All things considered: I would strongly suggest that you replace the pointers with `std::shared_ptr` (or its Boost equivalent if you don't have C++11). It's not a universal panacea, but this looks like a case where it would be appropriate. – James Kanze Jul 19 '13 at 09:53

4 Answers4

2

Exception std::bad_alloc means "operator new failed". So either operator new is getting called by operator* on newObj (which we don't know anything about) or by the insertion operator of the map (which is extremely more likely).

Specifically, when you call operator[] on the map with some parameter k

If k does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value. Notice that this always increases the container size by one, even if no mapped value is assigned to the element (the element is constructed using its default constructor).

(as documented here).

Map::operator[] provides the strong guarantee on failure:

Strong guarantee: if an exception is thrown, there are no changes in the container.

but doesn't guarantee for an exception not to be thrown (i.e. it provides no no-throw guarantee).

The reason for operator new throwing an exception could be of different nature. However, it all boils down to:

throws bad_alloc if it fails to allocate storage.

That said, as JamesKanze suggests in the comments:

Another possible reason for std::bad_alloc is undefined behavior. If he's corrupted the free space arena, for example. And realistically, if he's really running out of memory, the allocation where it fails would vary. If it is systematically here, I would suspect a problem in the copy constructor of Object, more than anything else.

meaning that operator new fails to allocate storage because of some bug in other portions of the program. You can debug against his null-assupmtion (as statistician would call it) by means of allocating a (very) big chunck of data right before the call to operator[]. If the dummy allocation doesn't fail you can say that there is a bug on the copy constructor with good confidence.

Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24
  • 1
    Another possible reason for `std::bad_alloc` is undefined behavior. If he's corrupted the free space arena, for example. And realistically, if he's really running out of memory, the allocation where it fails would vary. If it is systematically here, I would suspect a problem in the copy constructor of `Object`, more than anything else. – James Kanze Jul 19 '13 at 08:25
  • I was thinking about this, but I was not able to identify what can be the reason of bad allocation? I added the description of Object, hop it will help. – SairuS Jul 19 '13 at 08:26
  • @JamesKanze thank for suggesting this, your comments are always extremely pertinent. Hope you want to keep screening my answers! :) – Stefano Falasca Jul 19 '13 at 08:45
2

It is obvious that std::map operation cause the problem

m_waitingQueue[newObj->uid] = *newObj;

It is actually a map insert operation, which would possiblly allocate memory behind the scene: How is a STL map allocated? Stack or Heap?.One possible reason is allocating memory lead to Bad allocation exception : Bad allocation exceptions in C++.

But this code does not itself lead to explaination what is going on behind the scene. I think more information related to "m_waitingQueue" is needed, since the variable is global, which anything might be done to outside this function.

Community
  • 1
  • 1
lulyon
  • 6,707
  • 7
  • 32
  • 49
  • the map is use as a wait queue & is accessed bu 3 threads. in different functions. insert is done only here, rest are read operations using same lock. – SairuS Jul 19 '13 at 08:32
  • @SairuS Good. I thinks the question is more complete now. It is now a question I have to upvote and favorite. I am waiting for someone to come up with a specific answer, or if you solve the problem by yourself, let me know. – lulyon Jul 19 '13 at 08:41
0

The operator new() function is unable to find the requested memory. This function may be called from a new expression, or directly, in the allocator of std::map.

You don't give any information with regards to the context. The real question is: is it always failing at this particular point. If you were really running out of memory (e.g. because of a memory leak), one would expect it to hit other allocations as well. Other possibilities are that you are corrupting the free space arena just before calling this function, or that there is a problem with the copy constructor of Object<LPVOID> which causes it to request unlimited memory, or which corrupts the free space arena, so that the next allocation fails. Do you copy this object elsewhere? The fact that you pass it by pointer suggests maybe not, in which case, this would be the place where you'd see the problem.

EDIT:

Since you've posted the code for Object: where do the Object you use originate? And what do the pointers normally point to, and if it is dynamically allocated, how are the deletes managed?

Because Object has no user defined constructors, which means that there will be cases where the pointers contain random junk. And deleting random pointers is a very good way to corrupt the free space arena.

Also: I notice that you have what looks like synchronization primitives (Lock() and Unlock()). Where else is m_waitingQueue used? If m_waitingQueue can be accessed from a different thread, all accesses to m_waitingQueue must be synchronized, using the same synchronization object (m_syncWQ). An attempt to modify m_waitingQueue in another thread while you're modifying it here could also lead to undefined behavior (with the queue object writing somewhere where it's not supposed to).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • source Object as not a pointer. Usually I'm calling functions with pointers, but there are some cases where I'm sending object as non pointer(means a copy). – SairuS Jul 19 '13 at 08:45
  • IMHO, the issue is not related to memory leak as sometime is happening in the first action/add to the map. Will it help if I will use different way of insert? – SairuS Jul 19 '13 at 08:53
  • @SairuS This is more or less what I suspected. There's a problem in your memory management upstream: it could be a double delete, or writing to deleted memory, or overwriting beyond the end of an allocated buffer. If you're under Linux, run the code under `valgrind`. (There's probably an equivalent tool for Windows as well, but I don't know it. Except for Purify, which is ungodly expensive and very, very slow on an Intel.) – James Kanze Jul 19 '13 at 08:58
  • @James Kanze - this is obviously Windows, which have, for example, *Dr.Memory* memory debugger available for free. – SChepurin Jul 19 '13 at 09:13
0

Perhaps Add2WaitList(Object<LPVOID>) is simply being called millions of times until you run out of memory.

In that case, the cause would lie elsewhere in the code - e.g. in the form of an infinite loop or regression. Another possible cause would be if your Objects inadvertently all get different uids. This could happen when uid is derived from an uninitialized number, for example.

nkaleidoskop
  • 126
  • 4
  • Thx for your answer, but all cases not the my situation. I mentioned in the comments of "James Kanze" that it's happening sometime on first insert. – SairuS Jul 19 '13 at 09:10