Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

need ib_util_free to complement ib_util_malloc [CORE3826] #4168

Open
firebird-automations opened this issue Apr 22, 2012 · 11 comments
Open

need ib_util_free to complement ib_util_malloc [CORE3826] #4168

firebird-automations opened this issue Apr 22, 2012 · 11 comments

Comments

@firebird-automations
Copy link
Collaborator

Submitted by: Ray Holme (rholme)

Votes: 8

It is reasonable to allocate memory in a UDF and then abort the operation. It would be better if the UDF code could free the allocated memory.
It is also possible to allocate multiple chunks of memory and only return one to the caller.

While it is possible that the engine frees memory even if it is not used (FREE_IT), it is also reasonable for the code to allocate multiple memory hunks during the course of a function and only leave the last one to be free'd - sometimes only one hunk but the UDF aborts and returns 0. In the case of a long running transaction with lots of UDF calls per row, it is also possible to eat all memory before the clever engine decides to free unused hunks by transaction (assuming it does that as that is what I would do).

Instead, the coder must malloc the additional or conditional hunks (is this handled by FREE-IT if using ib_util_malloc?) and cannot free anything malloc'd by the ib CALL.
If it is a conditional chunk, then when it is decided to use it, an additional call to ib_util_malloc is required and then a plain free would be used.

This is a double potential memory leak problem.

@firebird-automations
Copy link
Collaborator Author

Commented by: @hvlad

ib_util_malloc should be used for result *only*
All other allocations\deallocations could be done by any suitable way (new\delete, alloc\free, etc)
So, i see no need in ib_util_free

@firebird-automations
Copy link
Collaborator Author

Commented by: Ray Holme (rholme)

You are BOTH missing my pointS. You answered one thing BUT not the other.

I have a UDF which allocates a return buffer and may discover a problem later.

In the old code, I free'd the buffer and returned 0 (a null pointer).

So I cannot use ib_util_malloc to allocate the original buffer NOW, but must alloc it and then if no error occurs in the middle of processing - I need to copy it.

This is sloppy and poor standards. If you provide an allocation routine, you should provide a free routine.

On the second reason which you did answer, this too is sloppy - if I alloc stuff and forget to free it, a memory leak will occur in time.

The whole reason for ib_util_alloc was (I hope) to make sure a sloppy coder did not leak the server to death.

@firebird-automations
Copy link
Collaborator Author

Commented by: Ray Holme (rholme)

And to be correct - I might add that sometimes the UDF changes its mind and call realloc (yes I have code doing this)

so you need an ib_util_realloc(long size) TOO

@firebird-automations
Copy link
Collaborator Author

Commented by: @hvlad

Ray,

take it easy and understand - ib_util_malloc is not a full featured memory manager for your UDF's.
It is just an support mechanizm to return strings.
If you don't like it - don't use it.
Learn how to ask Firebird to allocate memory for the return value using RETURNS PARAMETER <n>.
Hint: it works not for blobs only .

@firebird-automations
Copy link
Collaborator Author

Commented by: Ray Holme (rholme)

thanks Vlad - that is fine

I use it for strings and from ISC_QUADs as they are compound things (but I think the latter is not necessary)

The problem I have is two fold. Some of my UDFs encounter errors well after they start (illegal characters encountered) and some have to expand a string so the final size is not known. For both of these conditions, I need to malloc first and then re-do it later.

I am not worried about me, but I know how sloppy programmers can be and FREE is often forgotten.

You can ignore these suggestions or use them as you see fit.

@firebird-automations
Copy link
Collaborator Author

Commented by: @asfernandes

I think ib_util_free is a valid request.

@firebird-automations
Copy link
Collaborator Author

Commented by: @hvlad

Adriano,

it was not needed last 12 years and will not be needed for the next 20 years.

I'd prefer to kill FREE_IT mechanism with ib_util.dll all together... it was bad solution invented before Firebird.
RETURNS PARAMETER <n> is much better and should be used over FREE_IT.
But this is another story and old users will kill me ;)

@firebird-automations
Copy link
Collaborator Author

Commented by: Ray Holme (rholme)

My belief is that if you create a function like ib_util_malloc, you need both ib_util_free and ib_util_realloc - thanks Adriano.

Of course, you could always allocate the maximum string size returned (e.g. 32k -20) if you wanted and needed only 20 bytes, but I am an old user (more than 12 years for sure and I just said BANG Vlad :=] - I wrote code for Interbase back in 1990). I am not sure why this new function was invented AT all - why the heck not use malloc, realloc and then let FREE_IT work (users can still use them and be sloppy). In the OLD days before free_it (which dates back perhaps 12 years or less), we had real problems as we returned a pointer to a static area (which got munged pretty quickly with the next row or even sooner if multiple calls were made to the same udf function with different inputs for one row).

returns parameter<N> would result in huge mallocs for lsubstr (my version returns up to the maximum string supported by the engine - just as the newer one does, but mine was written before the firebird version and works just fine for me). So if I had several computed fields using lsubstr in one table, we could chew memory faster than Microsoft. :=<]

The modern tendency is to ignore memory and treat it as infinite. I wish I could share some stories with you about that, but then again maybe I should just retire.

Your point is well taken Vlad, I am too much a purist and learned early to save bytes of memory just like money (perhaps I should send you get_bit which allows you to save 15, 31, or 63 booleans in one short/int/long - yes I can count but you have to be nuts to use the sign bit). The actual code is about 2-3 lines.

Thanks to all, I am set for now and hopefully will just stop worrying about these things.

@firebird-automations
Copy link
Collaborator Author

Commented by: @AlexPeshkoff

As for me, I see absolutely nothing bad in adding ib_util_free() if people need it.

@firebird-automations
Copy link
Collaborator Author

Commented by: Marcin (wodzu)

Hi guys.

I've come by here because so as Ray I was searching for an ib_util_free for the same reasons. Because ib_util_free is missing I must allocate temporary memory just to make sure that if something goes wrong I can free it. And in case of success I must copy this tmp memory to the one allocated by ib_util_malloc. This is a waste of performance and memory and could be easly fixed by introducing ib_util_free.

I do not buy the argument that if it wasn't needed for 12 years then we still do not need it. You could say the same about the Internet - after all it was not needed for few hundred years so we do not need it now;)

And lets face it, not many people write their own UDFs...so this might be the reason that this topic have not got attention which it deserves,

Anyway, I hope that you will reconsider introducing this small feature.

@firebird-automations
Copy link
Collaborator Author

Commented by: prenosil (prenosil)

In theory, what Vlad recommends is good (using RETURNS PARAMETER <n>).
In practice it is not so easy.

Some 10 years ago I wrote UDF for some text processing. Typically the output string had tens (or just couple of hundreds) characters, but since it was universal function I had to declare the output length 32k. The speed drop when declaring the output length 32K instead of e.g. 100 was enormous (I do not remember exactly, may be it was 20x slower, may be even much more). So I had to use ib_util mechanism instead of RETURNS PARAMETER.

So now I did simple test with FB2.5.2. The result is much much better than with older FB version, but still the speed of calling function with declared parameter 32k is 2.5x slower than with 15 characters (when using RETURNS PARAMETER of course). So using ib_util has still some advantages over PARAMETER and killing it would not be good.

This is how I did the test:
declare external function dow timestamp, varchar(15) returns parameter 2 entry_point 'DOW' module_name 'fbudf';
declare external function dow2 timestamp, varchar(32700) returns parameter 2 entry_point 'DOW' module_name 'fbudf';
EXECUTE BLOCK AS
DECLARE X VARCHAR(15);
DECLARE I INTEGER=4000000;
BEGIN
WHILE (I>0) DO BEGIN
X = DOW2/*or DOW*/(CURRENT_TIMESTAMP);
I=I-1;
END
END

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant