Hello
I recently stumbled over a missing placement new operator in the Genode framework. I needed an object to be shared between a client and a server. The server should construct the object. The client should connect to the server, and in response receive a capability allowing the client to access the data held by the object. The class defining the object provides a constructor to do some necessary initialisation of the object. It looks like this:
class Object {
...
Object(...) : ... { ... }
};
The chosen approach is to create a dataspace capability with the size of the object. On the server side the object is instantiated:
Dataspace_capability cap = env()->ram_session()->alloc(sizeof(Object));
Object *obj = static_cast<Object *>(env()->rm_session()->attach(cap));
However, this does not work as expected, since this way Object's constructor is never called. To achieve this applying the new operator is required:
Dataspace_capability cap = env()->ram_session()->alloc(sizeof(Object));
Object *obj = new(env()->rm_session()->attach(cap)) Object();
However, this does not work either, because it leads to a type error with the placement parameter. The reason is that the only publicly available placement new operator is:
void *operator new(Genode::size_t size, Genode::Allocator *allocator)
And worse, its implementation tries to effectively allocate the object's space, something which is not wanted in the given context. What is needed here is a new operator which just returns the placement parameter as the address of the object so that its constructor is invoked when applying the operator. Something like this:
void *operator new(Genode::size_t size, void *addr) { return addr; }
I found such an operator locally defined in some Genode source files (allocator_avl.cc for instance). I wonder why such an operator is not publicly available although its usefulness is obvious. For the time being I created a local inplementation for my purpose by cloning it from an exisiting one. It would be helpful to have it generally thru a system include file.
Regards
Frank
Hi again,
you have a compelling use case for a placement 'new' operator but there is no such operator provided by the Genode framework for good reasons:
- A placement new operator is very simple. Indeed, its only one line of code, which does not relate in any way with rest of the Genode framework. It is completely independent. If you like, you are free to implement one in a custom header file of your's and use it from several places.
- Having to use of a placement new operator is (and should be) rare. If it cannot be avoided, it is the best to provide it local to where it is used to document the reason of why it is needed in the particular case. If not used with caution, a placement new operator is easy to misuse (in particular if overloads exist - see below).
- You already found that our only "official" new operator takes an 'Allocator' as argument. This argument allows you to specify the memory pool from where to allocate. For the motivation, see the "heap partitioning concept" of Genode:
http://genode.org/documentation/architecture/framework#Heap_partitioning
Because the 'Allocator' interface is abstract, it further enables the use of customized allocation strategies by supplying different 'Allocator' arguments to different sub systems. This way, allocation strategies can be optimized (e.g., using slabs or a list-based implementation) without changing the code that uses 'new'.
If we provided a general placement new operator, this would overload our standard new operator, which could lead to really bad errors. For example, assume that your code included 'placement_new.h' but not 'allocator.h', and you try passing a real allocator interface as argument to 'new'. In this case, the placement new operator would be used w/o any compiler warning and the new object would corrupt the 'Allocator' object.
If you like to stick with the classes provided with the Genode framework, there is a clean way to solve your placement-new problem by introducing a new 'Allocator' implementation - quite similar to the 'Sliced_heap' allocator. But your special allocator would allow for only one allocation. A 'Single_allocator' object would hold the dataspace capability ('ds_cap'). When 'alloc' called and the 'ds_cap' is not already initialized, a dataspace is allocated and locally mapped. The local address is then returned. The destructor could automatically unmap and free the dataspace. This way, the lifetime of the dataspace gets implicitly taken care of. For allocating an instance of your 'Object', you will need a dedicated 'Single_allocator' instance (hosted outside the shared-memory of course), which you supply as argument for 'new'.
And worse, its implementation tries to effectively allocate the object’s
Honestly, I do not know why you are using the word 'worse'. Which part of our interfaces is unreasonable from your point of view?
Regards Norman