You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@celix.apache.org by Sascha Zelzer <s....@dkfz-heidelberg.de> on 2012/06/01 15:35:38 UTC

Re: [Native-OSGi] OSGi API: Allocated memory ownership

On 05/30/2012 01:12 PM, Pepijn Noltes wrote:
> On Wed, May 30, 2012 at 11:53 AM, Alexander Broekhuis
> <a....@gmail.com>  wrote:
>> Hi all,
>>
>> I'm looking to extract the current Celix API to the Native-OSGi project,
>> and running into some issues related to data/memory ownership.
>>
>> Celix uses APR and memory pools for memory management. In cases where the
>> API returns some data, the user supplies a pool, and the framework
>> allocates the needed memory on that pool. This makes the user the owner of
>> the data.
>> At the moment Celix doesn't solve this nicely, sometimes data is allocated
>> on the supplied pool, sometimes a pointer is returned.. But this is another
>> issue.
>>
>> Some examples of this can be found in bundle.h, eg bundle_getEntry.
>>
>> In the Native-OSGi specification we don't want to use APR (or impose it on
>> others), so basically the question is, how do we want to handle functions
>> where some data is returned?
>>
>> Some possible solutions:
>> - Keep it on the call stack, so it gets out of scope.
>> - Ignore APR, and allocate the memory, making the user responsible for
>> freeing it.
>>
>> Are there any other options I am missing? And is there a common solution
>> for problems like this?
> Another option is to let the user provide a buffer and its size. Then
> the user can choose to use a pool,the stack or malloc/free. The
> downsize is that you do not known how big the buffer has to be and
> therefore there is change that the buffer is to small and additional
> action / return parameter is required.
>
> Greetings,
> Pepijn

Hi,

I'm not a C - expert, but I think the only viable solution is to 
accurately document the lifetime and ownership of pointers.

For memory owned by the framework, any memory management technique which 
guarantees the stated lifetime will do (e.g. APR memory pools).

Memory which is to be owned by the caller (user) could probably be 
allocated and deleted by using special functions (hooks) provided by 
each bundle which use implementation specific routines for memory 
management. Otherwise we just state that the returned memory belongs to 
the caller.

Best,
Sascha

Re: [Native-OSGi] OSGi API: Allocated memory ownership

Posted by Alexander Broekhuis <a....@gmail.com>.
Hi,


> >
> > I hope this still makes sense...
>
> It does :), but IMO supplying handles - even on framework level - is
> not a good idea. It will add too much complexity for something where
> most C programmers just expect to use of malloc and free.
> Personally I am in favor for using memory pools, but for a generic API
> I think we should keep close to "pure" C and in this case use malloc
> and let the users call free.
>

For now I will assume this, though I am still in favour of having something
which we can use in Celix in combination with pool.
In this case, the best solution would be an option to add pointers to an
existing pool...

Perhaps a (third) adoption layer which takes a copy into the pool and frees
the allocated memory.


> >
> > [1]: http://marc.info/?l=apr-dev&m=109065999530290&w=2
> >
> > --
> > Met vriendelijke groet,
> >
> > Alexander Broekhuis
>



-- 
Met vriendelijke groet,

Alexander Broekhuis

Re: [Native-OSGi] OSGi API: Allocated memory ownership

Posted by Pepijn Noltes <pe...@gmail.com>.
On Mon, Jun 4, 2012 at 12:00 PM, Alexander Broekhuis
<a....@gmail.com> wrote:
> Hi
>
>
>> I'm not a C - expert, but I think the only viable solution is to
>> accurately document the lifetime and ownership of pointers.
>>
>
> Well I think this is not a C specific problem, the same applies to C++ I
> guess. Someone has to free allocated memory.
>
>>
>> For memory owned by the framework, any memory management technique which
>> guarantees the stated lifetime will do (e.g. APR memory pools).
>>
>
> Since this is "internal" to the framework this can be anything the
> framework implementation uses. For Celix this is indeed APR.
>
> Memory which is to be owned by the caller (user) could probably be
>> allocated and deleted by using special functions (hooks) provided by each
>> bundle which use implementation specific routines for memory management.
>> Otherwise we just state that the returned memory belongs to the caller.
>>
>
> This is the more interesting part of the problem.
>
> How would such hooks look like, and doesn't this making bundles awkward?
>
> A generic header for this could look like this:
> memory_hooks.h:
>  int mem_alloc(void *handle, size_t size, void **block);
>  int mem_calloc(void *handle, size_t num, size_t size, void **block);
>  int mem_realloc(void *handle, void *block, size_t size, void **newblock);
>  int mem_free(void *handle, void *block);
>
> These function are basically the "normal" C memory functions, does C++ have
> different/additional ones?
> I've added a *handle to all of them to be able to pass some "instance" to
> it. For example, APR would need a memory pool, which can be a member of the
> instance type/struct.
>
> How does the framework get the handle? One additional method could work:
> int mem_createHandle(void **handle);
>
> For APR:
> mem_alloc -> use apr_palloc(handle->pool, size)
> mem_calloc -> use apr_pcalloc(handle->pool, size)
> mem_realloc -> no alternative in APR, implement a solution like [1]
> mem_free -> not implemented, the user has to clear the pool at some time
>
> Looking at it like this, I just realize this solution has a huge drawback
> when used in combination with APR. For different calls different pools are
> needed. Using this model, there is only one pool which is used for every
> allocation. Basically this means the user is not in control of the lifetime
> of memory.
> The only solution I can think of is to have the user supply the pool on
> which the memory has to be allocated, but we don't want this on our API.
>
> A possibility would be to supply the handle (instead of the pool) which is
> used by the memory hooks. For example:
>
> int bundle_getEntry(bundle, handle, char *name, char **entry)
>
> Instead of letting the framework use handle, handle is simply passed on the
> the memory hooks and used in a way the caller wants. This also eliminates
> the need for the createHandle function in the header file. The user can
> create one when needed. In case of APR, this handle probably can directly
> be the pool.
>
> I hope this still makes sense...

It does :), but IMO supplying handles - even on framework level - is
not a good idea. It will add too much complexity for something where
most C programmers just expect to use of malloc and free.
Personally I am in favor for using memory pools, but for a generic API
I think we should keep close to "pure" C and in this case use malloc
and let the users call free.

>
> [1]: http://marc.info/?l=apr-dev&m=109065999530290&w=2
>
> --
> Met vriendelijke groet,
>
> Alexander Broekhuis

Re: [Native-OSGi] OSGi API: Allocated memory ownership

Posted by Alexander Broekhuis <a....@gmail.com>.
>  Hm, I think you misunderstood me, or I did not fully understand the
> problem. We are talking about memory which is actively allocated by the
> framework (by calls to free or some hook provided by a bundle) but which is
> owned by the API user, right?
>

Yes, this is the case.


> My suggestion was (if we really want that kind of complexity) that the
> bundle writer (user of the API) can use the function pointer passed to the
> memory hook by the framework to decide on which internal pool the memory
> should be allocated. So the user has full control over the lifetime (e.g.
> via memory pools).


Yeah I see, but that would introduce a rather complex "allocate" method in
the bundle to handle the calls, especially if the same call has to be
handled differently each time.


>
>  I'm with Pepijn on this one, user the regular "malloc" and let the user
>> "free" it is the best for now I think.
>>
>>  That is definitely the most "common sense" solutions and is what I also
> suggested at the very beginning.
>

Ok, so this is settled for now. How we handle this in Celix will be another
question.

-- 
Met vriendelijke groet,

Alexander Broekhuis

Re: [Native-OSGi] OSGi API: Allocated memory ownership

Posted by Sascha Zelzer <s....@dkfz-heidelberg.de>.
On 06/05/2012 08:43 PM, Alexander Broekhuis wrote:
> Hi,
>
>
>> So bundles could use different pools for different API calls. Maybe that
>> kind of flexibility is enough... If there is no bundle specific "mem_alloc"
>> function, the framework could fall back to usual malloc/free calls. Does
>> that make sense?
>
> This still ties the user to a fixed lifetime for pools, still leaving the
> possibility for unneeded memory growth. The user should be in absolute
> control of the memory and when it has to be cleared. So for each call
> (similar or not) it can decide how to handle the memory. Tying it to a
> function still is a rather fixed solution.
Hm, I think you misunderstood me, or I did not fully understand the 
problem. We are talking about memory which is actively allocated by the 
framework (by calls to free or some hook provided by a bundle) but which 
is owned by the API user, right?

My suggestion was (if we really want that kind of complexity) that the 
bundle writer (user of the API) can use the function pointer passed to 
the memory hook by the framework to decide on which internal pool the 
memory should be allocated. So the user has full control over the 
lifetime (e.g. via memory pools).
> I'm with Pepijn on this one, user the regular "malloc" and let the user
> "free" it is the best for now I think.
>
That is definitely the most "common sense" solutions and is what I also 
suggested at the very beginning.

Best,
Sascha

Re: [Native-OSGi] OSGi API: Allocated memory ownership

Posted by Alexander Broekhuis <a....@gmail.com>.
Hi,


> So bundles could use different pools for different API calls. Maybe that
> kind of flexibility is enough... If there is no bundle specific "mem_alloc"
> function, the framework could fall back to usual malloc/free calls. Does
> that make sense?


This still ties the user to a fixed lifetime for pools, still leaving the
possibility for unneeded memory growth. The user should be in absolute
control of the memory and when it has to be cleared. So for each call
(similar or not) it can decide how to handle the memory. Tying it to a
function still is a rather fixed solution.

I'm with Pepijn on this one, user the regular "malloc" and let the user
"free" it is the best for now I think.


-- 
Met vriendelijke groet,

Alexander Broekhuis

Re: [Native-OSGi] OSGi API: Allocated memory ownership

Posted by Sascha Zelzer <s....@dkfz-heidelberg.de>.
Hi,

On 06/04/2012 12:00 PM, Alexander Broekhuis wrote:
>> I'm not a C - expert, but I think the only viable solution is to
>> accurately document the lifetime and ownership of pointers.
>>
> Well I think this is not a C specific problem, the same applies to C++ I
> guess. Someone has to free allocated memory.
Sure. I meant that I am familiar with a couple of C++ idioms for 
managing memory (smart pointers, copy constructors, implicitly shared 
classes, new/delete overloads, custom allocators etc.) but I am not 
accustomed with possibly existing idioms in C.

>> Memory which is to be owned by the caller (user) could probably be
>> allocated and deleted by using special functions (hooks) provided by each
>> bundle which use implementation specific routines for memory management.
>> Otherwise we just state that the returned memory belongs to the caller.
> This is the more interesting part of the problem.
>
> How would such hooks look like, and doesn't this making bundles awkward?
>
> A generic header for this could look like this:
> memory_hooks.h:
>    int mem_alloc(void *handle, size_t size, void **block);
>    int mem_calloc(void *handle, size_t num, size_t size, void **block);
>    int mem_realloc(void *handle, void *block, size_t size, void **newblock);
>    int mem_free(void *handle, void *block);
>
> These function are basically the "normal" C memory functions, does C++ have
> different/additional ones?
> I've added a *handle to all of them to be able to pass some "instance" to
> it. For example, APR would need a memory pool, which can be a member of the
> instance type/struct.
>
> How does the framework get the handle? One additional method could work:
> int mem_createHandle(void **handle);
>
> For APR:
> mem_alloc ->  use apr_palloc(handle->pool, size)
> mem_calloc ->  use apr_pcalloc(handle->pool, size)
> mem_realloc ->  no alternative in APR, implement a solution like [1]
> mem_free ->  not implemented, the user has to clear the pool at some time
>
> Looking at it like this, I just realize this solution has a huge drawback
> when used in combination with APR. For different calls different pools are
> needed. Using this model, there is only one pool which is used for every
> allocation. Basically this means the user is not in control of the lifetime
> of memory.
> The only solution I can think of is to have the user supply the pool on
> which the memory has to be allocated, but we don't want this on our API.
>
> A possibility would be to supply the handle (instead of the pool) which is
> used by the memory hooks. For example:
>
> int bundle_getEntry(bundle, handle, char *name, char **entry)
>
> Instead of letting the framework use handle, handle is simply passed on the
> the memory hooks and used in a way the caller wants. This also eliminates
> the need for the createHandle function in the header file. The user can
> create one when needed. In case of APR, this handle probably can directly
> be the pool.
>
> I hope this still makes sense...
>
> [1]: http://marc.info/?l=apr-dev&m=109065999530290&w=2
>

Yes, it is getting complicated... ;-)

Maybe another possibility which avoids the introduction of handles in 
the API is to pass the function pointer of the API function being called 
to the memory handling functions. E.g.

int bundle_getEntry(bundle, char *name, char **entry)
{
   bundle_mem_alloc(bundle_getEntry, <buffer_size>, entry);
   ...
}

So bundles could use different pools for different API calls. Maybe that 
kind of flexibility is enough... If there is no bundle specific 
"mem_alloc" function, the framework could fall back to usual malloc/free 
calls. Does that make sense?

- Sascha

Re: [Native-OSGi] OSGi API: Allocated memory ownership

Posted by Alexander Broekhuis <a....@gmail.com>.
Hi


> I'm not a C - expert, but I think the only viable solution is to
> accurately document the lifetime and ownership of pointers.
>

Well I think this is not a C specific problem, the same applies to C++ I
guess. Someone has to free allocated memory.

>
> For memory owned by the framework, any memory management technique which
> guarantees the stated lifetime will do (e.g. APR memory pools).
>

Since this is "internal" to the framework this can be anything the
framework implementation uses. For Celix this is indeed APR.

Memory which is to be owned by the caller (user) could probably be
> allocated and deleted by using special functions (hooks) provided by each
> bundle which use implementation specific routines for memory management.
> Otherwise we just state that the returned memory belongs to the caller.
>

This is the more interesting part of the problem.

How would such hooks look like, and doesn't this making bundles awkward?

A generic header for this could look like this:
memory_hooks.h:
  int mem_alloc(void *handle, size_t size, void **block);
  int mem_calloc(void *handle, size_t num, size_t size, void **block);
  int mem_realloc(void *handle, void *block, size_t size, void **newblock);
  int mem_free(void *handle, void *block);

These function are basically the "normal" C memory functions, does C++ have
different/additional ones?
I've added a *handle to all of them to be able to pass some "instance" to
it. For example, APR would need a memory pool, which can be a member of the
instance type/struct.

How does the framework get the handle? One additional method could work:
int mem_createHandle(void **handle);

For APR:
mem_alloc -> use apr_palloc(handle->pool, size)
mem_calloc -> use apr_pcalloc(handle->pool, size)
mem_realloc -> no alternative in APR, implement a solution like [1]
mem_free -> not implemented, the user has to clear the pool at some time

Looking at it like this, I just realize this solution has a huge drawback
when used in combination with APR. For different calls different pools are
needed. Using this model, there is only one pool which is used for every
allocation. Basically this means the user is not in control of the lifetime
of memory.
The only solution I can think of is to have the user supply the pool on
which the memory has to be allocated, but we don't want this on our API.

A possibility would be to supply the handle (instead of the pool) which is
used by the memory hooks. For example:

int bundle_getEntry(bundle, handle, char *name, char **entry)

Instead of letting the framework use handle, handle is simply passed on the
the memory hooks and used in a way the caller wants. This also eliminates
the need for the createHandle function in the header file. The user can
create one when needed. In case of APR, this handle probably can directly
be the pool.

I hope this still makes sense...

[1]: http://marc.info/?l=apr-dev&m=109065999530290&w=2

-- 
Met vriendelijke groet,

Alexander Broekhuis