the engine frees memory in the middle of an allocated chunk

Started by
3 comments, last by WitchLord 3 years ago

In short, firstly I binded a global memory pool to the engine, then I assigned a class that also uses the same memory pool as string class. When I try to run some short code, the engine attempts to release memory (an instance of binded string class) inside an large allocated memory (it seems like the stack of the run-time). Is that a designed behavior so that my memory pool shall allow freeing part of its allocated memory, or it is an error caused by my incorrect binding?

The script code that was run with error is:

const char* code =
    "void main() {\n"
    "    Identifier a = \"abc\";\n"
    "}";

Specifically, if I run script code without assignment operator, it won't crash:

const char* code =
    "void main() {\n"
    "    \"abc\";\n"
    "}";

More details:

I'm using JUCE Identifier as string class for the AngelScript engine. It is registered as a value type.

I used GENERIC function binding at all places for maximum compatibility.

The code below are Identifier methods. Specifically, the assignment operator is registered with void opAssign(Identifier) signature.

static void Identifier_construct( asIScriptGeneric* ctx )
{
    void* mem = ctx->GetObject();
    new ( mem ) juce::Identifier;
}

static void Identifier_destruct( asIScriptGeneric* ctx )
{
    void* mem = ctx->GetObject();
    ( (juce::Identifier*) mem )->~Identifier();
}

static void Identifier_eq( asIScriptGeneric* as )
{
    auto* a = reinterpret_cast< juce::Identifier* >( as->GetObject() );
    auto* b = reinterpret_cast< juce::Identifier* >( as->GetArgObject( 0 ) );
    as->SetReturnByte( *a == *b );
}

static void Identifier_assign( asIScriptGeneric* as )
{
    auto* self = reinterpret_cast< juce::Identifier* >( as->GetObject() );
    auto* peer = reinterpret_cast< juce::Identifier* >( as->GetArgObject(0) );
    *self = *peer;
}

And the code below is the string factory:

struct IdentifierStringFactory : public asIStringFactory
{
    static IdentifierStringFactory& getInstance()
    {
        static IdentifierStringFactory obj;
        return obj;
    }
    const void* GetStringConstant( const char* data, asUINT len ) override;
    int ReleaseStringConstant( const void* str ) override;
    int GetRawStringData( const void* str, char* data, asUINT* len ) const override;
};

const void* IdentifierStringFactory::GetStringConstant( const char* data, asUINT len )
{
    void* mem = threadedPooledAlloc( sizeof( juce::Identifier ) );
    return new ( mem ) juce::Identifier( data );
}

int IdentifierStringFactory::ReleaseStringConstant( const void* str )
{
    juce::Identifier* id = (juce::Identifier*) str;
    id->~Identifier();
    threadedPooledFree( id );
    return 0;
}

int IdentifierStringFactory::GetRawStringData( const void* str, char* data, asUINT* len ) const
{
    juce::Identifier* id = (juce::Identifier*) str;
    auto sz = id->getCharPointer().sizeInBytes();
    if ( data == nullptr )
        *len = asUINT( sz );
    else
        memcpy( data, id->getCharPointer().getAddress(), sz );
    return 0;
}

Advertisement

It's obviously not proper behaviour that an attempt to delete/free memory on an address that has not previously been allocated with new/alloc.

But with only the information you have given I cannot start to guess where the problem is. It would be necessary to debug the code to identify what is wrong.

Can you provide a fully working example program that reproduces the problem?

In the meantime here is a thoughts that might put you in the right direction:

  • The string factory is only for the literal string constants, i.e. the “abc” constant in your script. Any string variable would still need to be allocated as ordinary objects., and these allocations may be on the heap or on the VM callstack depending on the situation.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

WitchLord said:

It's obviously not proper behaviour that an attempt to delete/free memory on an address that has not previously been allocated with new/alloc.

But with only the information you have given I cannot start to guess where the problem is. It would be necessary to debug the code to identify what is wrong.

Can you provide a fully working example program that reproduces the problem?

In the meantime here is a thoughts that might put you in the right direction:

  • The string factory is only for the literal string constants, i.e. the “abc” constant in your script. Any string variable would still need to be allocated as ordinary objects., and these allocations may be on the heap or on the VM callstack depending on the situation.

After several attempts, I found it may be caused by incorrect script interface declaration. I changed void opAssign(Identifier) to void opAssign(const Identifier&in) then the error disappears.

It feels quite reasonable: as I am declaring the copy operation here, I should not use a copy of the type to pass arguments.

Yes, the opAssign should take the value by reference.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement