Memory leaks in OpenDBXDriver-NativeBoost:NBPharoOpenDBX

10 views
Skip to first unread message

Michael Roland

unread,
Jul 30, 2014, 12:42:59 PM7/30/14
to dbx...@googlegroups.com
Memory leaks in OpenDBXDriver-NativeBoost:NBPharoOpenDBX

Several NBPharoOpenDBX methods create strings with NBExternalAddress class>>#fromString.These strings are not freed after use.

Memory for NBExternalAddress strings are allocated directly from the operating system and are not garbage collected by Pharo. NBExternalAddress strings must be explicitly deallocated with the method NBExternalAddress>>#free. These strings are passed as parameters to opendbx library functions.


Affected DBXTalk versions:
1.3 -- Pharo 2
1.4.1 -- Pharo 3

I didn't check any DBXTalk versions older than 1.3, or any Pharo versions older than Pharo 2.


Affected operating systems:
Any operating system that NativeBoost supports.


Example memory usage

Insert 8019816 rows into a PostgresQL database. Pharo 3 running on Windows 7. Memory values taken from the Windows task manager private working set.

Unpatched DBXTalk 1.4.1
    Starting memory usage 33 MB.
    Ending memory usage 481 MB.

Patched DBXTalk 1.4.1
    Starting memory usage 34 MB.
    Ending memory usage 128 MB.

The extra 353 MB of memory that the unpatched version uses can't be reused by Pharo. The memory will stay allocated until Pharo is closed. The memory contains copies of each query string that was executed. In this case, that was 41769 query strings of about 8150 bytes each, which gives an estimated size of 340 MB.


The affected methods in DBXTalk 1.3 and 1.4.1 are:

NBPharoOpenDBX>>#apiBind:database:name:password:method:
NBPharoOpenDBX>>#apiInitialize:backend:host:port:
NBPharoOpenDBX>>#apiQuery:query:length:
NBPharoOpenDBX>>#apiSetOption:option:value:


The original methods and corrected methods:

apiBind: handle database: databaseName name: userName password: password method: method
    "int odbx_bind(odbx_t* handle, const char* database, const char* who, const char* cred,int method )"
    ^self openDBXlib odbx_bind: handle database: databaseName asNBExternalString who: userName asNBExternalString cred: password asNBExternalString method: method
----


apiBind: handle database: databaseName name: userName password: password method: method
    "int odbx_bind(odbx_t* handle, const char* database, const char* who, const char* cred,int method )"
    "^self openDBXlib odbx_bind: handle database: databaseName asNBExternalString who: userName asNBExternalString cred: password asNBExternalString method: method"

    | databaseString whoString credString result |
    [databaseString := databaseName asNBExternalString.
    whoString := userName asNBExternalString.
    credString := password asNBExternalString.
    result := self openDBXlib odbx_bind: handle database: databaseString who: whoString cred: credString method: method]
        ensure: [
            databaseString ifNotNil: [databaseString free].
            whoString ifNotNil: [whoString free].
            credString ifNotNil: [credString free]].
    ^result
--------


apiInitialize: handle backend: backend host: host port: port
    "long odbx_init(odbx_t**, char*, char*, char*)"
    ^self openDBXlib odbx_init: handle backend: backend asNBExternalString host:  host asNBExternalString port: port asNBExternalString.
----


apiInitialize: handle backend: backend host: host port: port
    "long odbx_init(odbx_t**, char*, char*, char*)"
    "^self openDBXlib odbx_init: handle backend: backend asNBExternalString host:  host asNBExternalString port: port asNBExternalString."

    | backendString hostString portString result |
    [backendString := backend asNBExternalString.
    hostString := host asNBExternalString.
    portString := port asNBExternalString.
    result := self openDBXlib odbx_init: handle backend: backendString host: hostString port: portString]
        ensure: [
            backendString ifNotNil: [backendString free].
            hostString ifNotNil: [hostString free].
            portString ifNotNil: [portString free]].
    ^result
--------


apiQuery: handle query: query length: length
    "long odbx_query(odbx_t*, char*, ulong)"
    ^self openDBXlib odbx_query: handle query: query asNBExternalString length: length.
----


apiQuery: handle query: query length: length
    "long odbx_query(odbx_t*, char*, ulong)"
    "^self openDBXlib odbx_query: handle query: query asNBExternalString length: length."

    | queryString result |
    queryString := query asNBExternalString.
    [result := self openDBXlib odbx_query: handle query: queryString length: length]
        ensure: [queryString free].
    ^result
--------


The aValue parameter is either a ByteString or a ByteArray. A ByteString is converted to an NBExternalAddress string. A ByteArray is unchanged.

apiSetOption: handle option: anOption value: aValue
    "int odbx_set_option(odbx_t* handle, unsigned int option, void* value ) "
    ^self openDBXlib odbx_set_option: handle option: anOption value: aValue asNBOpenDBXExternalOption
----


apiSetOption: handle option: anOption value: aValue
    "int odbx_set_option(odbx_t* handle, unsigned int option, void* value ) "
    "^self openDBXlib odbx_set_option: handle option: anOption value: aValue asNBOpenDBXExternalOption"

    | value result |
    value := aValue asNBOpenDBXExternalOption.
    [result := self openDBXlib odbx_set_option: handle option: anOption value: value]
        ensure: [
            (value isMemberOf: NBExternalAddress) ifTrue: [value free]].
    ^result
--------

Are there any instructions describing how to submit changes to the DBXTalk project? My Smalltalk hub username is mroland.

Reply all
Reply to author
Forward
0 new messages