Efficient UNO component linkage & GC ...

Stephan Bergmann sbergman at redhat.com
Fri Oct 11 08:20:16 PDT 2013


On 10/08/2013 03:23 PM, Michael Meeks wrote:
> ** Part one - splitting factories
>
> 	Clearly we need a new naming scheme for these; this should be
> based on a mangled version of the implementation name; so:
>
>    <implementation name="com.sun.star.comp.Draw.GraphicExporter">
>
> 	->
>
> 	com_sun_star_comp_Draw_GraphicExporter_getFactory();
>
> 	Despite everyone's loathing of the css. namespace this makes
> part three much easier.
>
> 	There are various ways to annotate that a .component file
> eg. svx/util/svx.component refers to a shlib with a split factory. I
> suggest we reduce the problem space by asserting that each shlib with
> this property has it's components entirely split up - yielding a
> single attribute change.
>
> 	For these we alter the prefix="svx" to prefix="<name>" or
> some equivalent magic, and tweak:
>
> stoc/source/loader/dllcomponentloader.cxx:
>
> Reference<XInterface> SAL_CALL DllComponentLoader::activate(
>      const OUString & rImplName, const OUString &, const OUString & rLibName,
>      const Reference< XRegistryKey > & xKey )
>
> 	To special case this prefix, and build a symbol name (in
> aPrefix) and use a new variant of:
>
> cppuhelper/source/shlib.cxx:Reference< XInterface > SAL_CALL loadSharedLibComponentFactory(
>
> 	To load and hook out the correctly named symbol there.

DllComponentLoader is mostly dead by now, only comes into play during 
live-deployment of (non-bundled) extensions.  And since, as per 
<http://cgit.freedesktop.org/libreoffice/core/commit/?id=3bafe5f5f529f6539363c5a291dd13ef1e2cbbde> 
"Extension shared library components must not use the 'prefix' feature," 
see 
<http://cgit.freedesktop.org/libreoffice/core/commit/?id=22edbdf53cc22c6cd5bb1cff99508fe4f588f462> 
"No need to support 'prefix' in DllComponentLoader."

Handling of the prefix feature is done entirely in 
cppuhelper/source/servicemanger.cxx and cppu::loadSharedLibComponentFactory.

Having reached this point, we basically have two options:

Either, keep with the current design of <prefix>_component_getFactory 
functions (responsible for returning factories for potentially many 
object implementations, dispatched by name).  To split into smaller, 
unconnected parts would mean to split existing <component> XML entities 
into smaller ones, each with its own prefix attribute (but potentially 
multiple ones with the same uri attribute), and to split exisiting 
<prefix>_component_getFactory function definitions into smaller ones 
accordingly (but keeping their structure).  That way, a 
<prefix>_component_getFactory could still be responsible for either 
multiple or just a single object implementation (though in the latter 
case would incur somewhat more overhead than a more straightforward 
design), and we would need no modifications to the infrastructure.

Or, as you detail below, go further and add more efficient support for 
the "single-object-implementation" factory case.  (Do you have any idea 
whether this is worth it, given we have to continue supporting the other 
case for extension-compatibility anyway?)

Anyway, given that the current "prefix" feature should only be used by 
internal code we have under full control (see recent discussion 
elsewhere on this mailing list), should we go down that road, I'd prefer 
to replace the existing "prefix" feature with something new (e.g., an 
extension of the components XML schema that associates an individual 
<implementation> rather than a <component> with a prefix attribute), 
rather than keeping both.  (Though we could support both for a 
transition period while we incrementally update the existing code.)

> 	We also define these factory functions to always be passed a
> valid pServiceManager, and only ever to be asked to construct the
> exact service name mangled into the symbol, hence:
>
>
> SAL_DLLPUBLIC_EXPORT void * SAL_CALL com_sun_star_drawing_SvxUnoColorTable_getFactory ( void * pServiceManager )
> {
>      // Ideally this reference, and the branch below would be subsumed
>      // by a helper, such that this method call was a single line, and
>      // ideally could be macro-ized elegantly.
>
>      uno::Reference< lang::XSingleServiceFactory > xFactory;
>
>      xFactory = cppu::createSingleFactory(
>                   reinterpret_cast< lang::XMultiServiceFactory * >( pServiceManager ),
>                   SvxUnoColorTable::getImplementationName_Static(),
>                   SvxUnoColorTable_createInstance,
>                   SvxUnoColorTable::getSupportedServiceNames_Static() );
>       if( xFactory.is())
>       {
>          xFactory->acquire();
>          return xFactory.get();
>       }
>       return NULL;
> }

Not sure what that specific step buys you in comparison to keeping with 
the existing

   extern "C" SAL_DLLPUBLIC_EXPORT void * SAL_CALL
   <prefix>_component_getFactory(
       char const * name, void * serviceManager, void * registryKey)

interface (which is already "always [...] passed a valid 
pServiceManager," and always ignores the long-time legacy registryKey 
argument) and just ignoring the name argument in the 
"single-object-implementation" case.

What would likely be more entertaining is to get rid of the 
cppu::createSingleFactory layer of indirection.  (As you come to in part 
three.  But note that there are service implementations that rather act 
like singletons and always hand out the same object instance; that needs 
to be taken care of.)

> ** Part two - list generation cleanup
>
>
>       For this, we define during configure time a list of services
> which we want to be internal, and unconditionally resolved into our
> binaries. This would be in the form of a flat list of implementations:
>
> com.sun.star.comp.Svx.GraphicExportHelper
> com.sun.star.comp.Svx.GraphicImportHelper
> com.sun.star.comp.graphic.PrimitiveFactory2D
> ...
>
>       Instead of having great lists of shlibs and their locations (a
> thing that would get significantly worse with longer / fragmented
> lists) eg.
>
>      static lib_to_component_mapping map[] = {
>          { "libsvxlo.a", svx_component_getFactory },
> 	...
>
> 	would turn one line into 10 - we instead auto-generate both
> these lines, and also the 'extern' statements required to import the
> symbols required inside ios/experimental/LibreOffice/LibreOffice/lo.mm
> android/experimental/desktop/native-code.cxx etc.
>
> 	It is unclear that we still require the 'lib' piece in this
> picture - though that could be inferred from the aggregated .component
> files. It would almost certainly be preferable to fix the:
>
> include/osl/detail/component-mapping.h (lib_to_component_mapping)
> cppuhelper/source/shlib.cxx
>
> 	To instead map implementation directly to symbol, something we
> can't do programatically since we need the internally bound function
> pointer listed thus:
>
>      static uno_component_mapping map[] = {
> 	{ "com.sun.star.comp.Svx.GraphicExportHelper",
> 	  com_sun_star_comp_Svx_GraphicExportHelper" },
> 	...
>
> 	etc.

[I have trouble parsing the above, starting at "It would almost 
certainly..."]

An alternative is to extend the components XML schema to support lookup 
of the factory function directly in the executable (say) rather than a 
given dynamic library (e.g., by having a local="true" attribute rather 
than a uri="..." one).  Since the uri attributes are already inserted 
into the .component XML files at build-time, it shouldn't be too hard to 
combine that with a configurable list of components that end up directly 
linked into the executable.

Somewhat orthogonal to that is the question how the components XML data 
is represented in installation sets.  In addition to having them stored 
as XML files that are parsed at start-up in 
cppuhelper::ServiceManager::init, one could optionally have them 
pre-compiled into some data structure that is accessible from 
cppuhelper/source/servicemanager.cxx.  In which case the data structures 
for such local="true" components could directly contain function 
pointers to the corresponding factories.

> * Part three - faster component instantiation
>
> 	The wonderful work Noel has been doing around more beautiful
> ways to instantiate components should dove-tail with this nicely. When
> we have the list from part-two of which components are available
> inside our Merged Library Domain, we should be able to directly
> instantiate them ourselves, without using the UNO factory / activation
> process; that should reduce code-size and improve performance, hence:
>
>
> codemaker/source/cppumaker/cpputype.cxx
>
> void ServiceType::dumpHxxFile(
>      FileStream & o, codemaker::cppumaker::Includes & includes)
> ...
>
>                    << codemaker::cpp::translateUnoToCppIdentifier(
>                        "create", "method", codemaker::cpp::ITM_NONGLOBAL,
>                        &cppName)
>                    << ("(::com::sun::star::uno::Reference<"
>                        " ::com::sun::star::uno::XComponentContext > const &"
>                        " the_context) {\n");
>
> 	(this method should be split)
>
> 	should load and parse our list if internal implementations,
> and produce a much simpler method eg. existing:
>
> workdir/unxlngi6.pro/UnoApiHeadersTarget/offapi/comprehensive/com/sun/star/frame/FrameLoaderFactory.hpp:
>
> 	would turn into:
>
>          try {
>              the_instance = css::uno::Reference< css::frame::XLoaderFactory >(
> 		com_sun_star_frame_FrameLoaderFactory_create(the_context->getServiceManager()),
> 		::com::sun::star::uno::UNO_QUERY);
> 	} ... existing disasterous size-wise exception code ... {
> 	    // or better we could pass the fn' pointer and all the details
> 	    // we need into a
> 	}
>
> 	Which should be much smaller and neater.

Question is how much that "much" would actually be.  (And one would have 
to generate two variants of service/singleton C++ headers, an optimized 
one for internal use and a traditional one for external use.)

Stephan


More information about the LibreOffice mailing list