On 09/13/2013 11:07 AM, Lionel Elie Mamane wrote:
On Wed, Sep 11, 2013 at 12:35:15PM +0200, Stephan Bergmann wrote:

First, note that we don't have reference-counting garbage collection
in general in UNO.  For example, Java-UNO doesn't use
reference-counting.

Second, note that there needs to be code (the "owner" in the jargon
of the udkapi/com/sun/star/lang/XComponent.idl documentation) that
explicitly calls dispose on a UNO object.  It is not magically
called when a UNO object becomes unreachable.

So let's take the concrete example of
  reportbuilder/java/org/libreoffice/report/SDBCReportDataFactory.java
  around line 233:
  private String getOrderStatement(final int commandType, final String command, 
final List sortExpressions)

It makes use of

  ::com::sun::star::container::XNameAccess
  com.sun.star.sdb.tools.XConnectionTools.getFieldsByCommandDescriptor(
    [in] long commandType,
    [in] string command,
    [out] ::com::sun::star::lang::XComponent    keepFieldsAlive)

The documentation of this
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1sdb_1_1tools_1_1XConnectionTools.html#ab96cfd3f9e8facbd9ebe98aa41a684fd
says:
parameter keepFieldsAlive
   the fields collection which is returned by this function here is a
   temporary object. It is kept alive by another object, which is to be
   created temporarily, too. To ensure that the fields you get are
   valid as long as you need them, the owner which controls their life
   time is transfered to this parameter upon return.

   Your fields live as long as this component lives.

   Additionally, you are encouraged to dispose this component as soon as
   you don't need the fields anymore. It depends on the connection's
   implementation if this is necessary, but the is no guarantee, so to be
   on the safe side with respect to resource leaks, you should dispose
   the component.


Concretely, the Java code does:

   final XComponent[] hold = new XComponent[1];
   final XNameAccess columns = getFieldsByCommandDescriptor(commandType, 
command, hold);

I assume this whole rigmarole with "hold" is to ensure that
"dispose()" is called on "columns", providing an owner for it.
(I assume it uses this round-about way instead of just declaring the
  caller the owner of columns because in some conditions columns will be
  a temporary and in other conditions, it will not; with this setup,
  this is testable with (hold[0] == null)).

(At least the documentation you cite above does not make it look like keepFieldsAlive could return a null reference.)

So, now the question: I assume that hold[0] itself needs to be
disposed so that it calls dispose on columns. I need to do that in an
exception-safe way, that is even if this function exits by an uncaught
exception.

Yes, that's what the documentation you cite above appears to imply.

So I need to do something like:

   final XComponent[] hold = new XComponent[1];
   try
   {
     (...)
     final XNameAccess columns = getFieldsByCommandDescriptor(commandType, 
command, hold);
     (...)
     if(hold[0] != null)
        hold[0].dispose();
   }
   catch (SQLException ex)
   {
      if(hold[0] != null)
        hold[0].dispose();
      LOGGER.error("ReportProcessing failed", ex);
   }
   catch(java.lang.Exception e)
   {
     if(hold[0] != null)
       hold.dispose();
     throw e;
   }

Why not use a finally block?

Did I understand what you were saying correctly? This function (and
_any_ code getting a fresh/temporary UNO object) needs to clean up
after itself manually in this pedestrian, error-prone way? Do we get a
cop-out in C++ because of the use of com::sun::star::uno::Reference
which calls _pInterface->release()? Do we get a cop-out in any other
UNO language? (Python? Basic?)

This specific Java code obtaining ownership of an XComponent is supposed to follow the protocol of XComponent and ensure that dispose is called, yes. But not every UNO object implements the XComponent protocol, so there is lots of code obtaining references to UNO objects that does not need to do this sort of clean-up. (And the explicit code to do the clean-up is necessary in every UNO language binding, though different languages offer different means to code that more or less elegantly.)

In the above Java example, it wants me to add "throws Exception"
before it compiles that. I'm confused because usually it won't let me
put a catch clause if the body of the try cannot possibly throw that
exception, but here it lets me but the catch clause. So there must be
some way that the body of the try throws an Exception (other than
SQLException), but then why does it let me get away without the
"throws Exception" in the current version of the code?

That's the gotcha that you can /catch/ any supertype of any exception potentially thrown in the try-block, but /throw/ only (static) subtypes of what is allowed in the method's raises clause. (Java 8 improves on that IIRC, making use of the fact that while the e in "throw e;" is statically of type java.lang.Exception, it can be proven by the compiler that it will dynamically only be of any subtypes of java.lang.Exception that can actually be thrown from the try-block.) You avoid all these problems by using finally.

Stephan
_______________________________________________
LibreOffice mailing list
LibreOffice@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/libreoffice

Reply via email to