[Kde-perl] [Kde-bindings] [Bug 108650] New: memory leak in qtruby? (Qt::Socket)

Richard Dale Richard_Dale at tipitina.demon.co.uk
Fri Jul 8 13:49:54 CEST 2005


On Wednesday 06 July 2005 14:42, Caleb Tennis wrote:
> ------- You are receiving this mail because: -------
> You are the assignee for the bug, or are watching the assignee.
>
> http://bugs.kde.org/show_bug.cgi?id=108650
>            Summary: memory leak in qtruby? (Qt::Socket)
>            Product: bindings
>            Version: unspecified
>           Platform: Gentoo Packages
>         OS/Version: Linux
>             Status: NEW
>           Severity: normal
>           Priority: NOR
>          Component: general
>         AssignedTo: kde-bindings kde org
>         ReportedBy: caleb gentoo org
>
>
> Version:            (using KDE KDE 3.4.1)
> Installed from:    Gentoo Packages
> OS:                Linux
>
> I've been chasing a memory leak in a couple of my programs now for
> sometime, and I believe I have found it.  Please accept my apologies if I'm
> missing something obvious here.  Bear with me as I try to describe it.
>
> I have a few qtruby  programs which, after running for a few days, tend to
> die with OOM errors after growing to very large memory consumption.  I also
> tried manually calling the garbage collector every second while the program
> was running to no avail.  Other programs did not exhibit this behavior, so
> after some tests, I found out what was causing hte leak:
>
> I have a Qt::Object derived class which houses a Qt::Socket that I use for
> communication with various servers around our building.  My Qt::Object
> class defines this connection:
>
>     Qt::Object::connect( socket, SIGNAL('readyRead()'), self,
> SLOT('MessageReceivedSlot()'))
>
> And this slot is defined as:
>
>   def MessageReceivedSlot
>     emit MessageSignal( socket readLine chomp) while( socket canReadLine)
>   end
>
> And this is where the memory leak occurs.
>
> I made a simple test case to reproduce it.  I created a small TCP server
> which just sends out data over and over again to anything that connects to
> it:
>
> require 'socket'
> port = 1999.to_i
> server = TCPServer.new('localhost', port)
> while (session = server.accept)
>   idx = 0
>   while true
>     20.times { session.puts "Request: Blah #{idx}" }
>     idx = idx + 1
>     sleep 1
>   end
> end
>
> And then I created a small program which uses this socket class to connect
> to the server and just continue to receive the messages.  I even changed
> the receive slot to NOT emit the signal, figuring that that may be the
> problem:
>
>   def MessageReceivedSlot
>      socket readLine chomp while( socket canReadLine)
>   end
>
> So all my class does now is continue to receive data, and readLine it, but
> not do anything with it.  I am also calling the garbage collector quite
> often to make sure I'm not seeing anything funny.
>
> Over time, the programs memory consumption continues to grow and grow.  It
> may start out at something like 22000K memory, and after 48 hours will be
> around 150000K, and will continue until it gets an OOM error.
>
> My *hunch* is that there's something spurious going on with string to
> Qt::String conversion, perhaps a QString is getting created somewhere as a
> child of my socket and it's never being disposed of, but I don't know for
> sure.  I'm currently running in a debugger in an attempt to see what is
> causing the continual growth of memory, but any thoughts would be helpful.
Yes, you're right there is a leak for QString value return types. When a Smoke 
method returns a QString, it creates a new temporary QString with 'new', but 
never deletes it. I also tried 'QString*' and 'QString&' return types with 
readLine(), and those don't create a temporary string.


    virtual      QString readLine();

Generates this code:
   void x_45(Smoke::Stack x) {
        // readLine()
        QString xret = this->QSocket::readLine();
        x[0].s_voidp = (void*)new QString(xret);
    }


    virtual      QString * readLine(); Becomes

Generates this code:
    void x_45(Smoke::Stack x) {
        // readLine()
        QString* xret = this->QSocket::readLine();
        x[0].s_voidp = (void*)xret;
    }


    virtual      QString & readLine();

Generates this code:
    void x_45(Smoke::Stack x) {
        // readLine()
        QString& xret = this->QSocket::readLine();
        x[0].s_voidp = (void*)&xret;
    }

In Qt.cpp, the class to handle a return value has a boolean method called 
cleanup(), which is false.

class MethodReturnValue : public Marshall {
...

    bool cleanup() { return false; }
};

In handlers.cpp, the code to marshall a QString to a ruby string will only 
delete the QString if cleanup() is true:

case Marshall::ToVALUE:
{
    QString *s = (QString*)m->item().s_voidp;
    if(s) {
        if (s->isNull()) {
            *(m->var()) = Qnil;
        } else {
            *(m->var()) = rstringFromQString(s);
        }
        if(m->cleanup())
            delete s;
    } else {
            *(m->var()) = Qnil;
    }
}

So the temporary QString from the 'QString readLine()' version of the method 
is never deleted. Either cleanup() should return true in MethodReturnValue, 
or the marshalling code should check if the QString is a value type and only 
delete it if it is. 

I looked at the PerlQt code in PerlQt-3.009 beta and it's just the same, and 
will have this leak too.

-- Richard


More information about the Kde-perl mailing list