LCOV - code coverage report
Current view: top level - framework/source/jobs - jobexecutor.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 58 107 54.2 %
Date: 2012-08-25 Functions: 13 22 59.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 108 334 32.3 %

           Branch data     Line data    Source code
       1                 :            : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 :            : /*************************************************************************
       3                 :            :  *
       4                 :            :  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       5                 :            :  *
       6                 :            :  * Copyright 2000, 2010 Oracle and/or its affiliates.
       7                 :            :  *
       8                 :            :  * OpenOffice.org - a multi-platform office productivity suite
       9                 :            :  *
      10                 :            :  * This file is part of OpenOffice.org.
      11                 :            :  *
      12                 :            :  * OpenOffice.org is free software: you can redistribute it and/or modify
      13                 :            :  * it under the terms of the GNU Lesser General Public License version 3
      14                 :            :  * only, as published by the Free Software Foundation.
      15                 :            :  *
      16                 :            :  * OpenOffice.org is distributed in the hope that it will be useful,
      17                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19                 :            :  * GNU Lesser General Public License version 3 for more details
      20                 :            :  * (a copy is included in the LICENSE file that accompanied this code).
      21                 :            :  *
      22                 :            :  * You should have received a copy of the GNU Lesser General Public License
      23                 :            :  * version 3 along with OpenOffice.org.  If not, see
      24                 :            :  * <http://www.openoffice.org/license.html>
      25                 :            :  * for a copy of the LGPLv3 License.
      26                 :            :  *
      27                 :            :  ************************************************************************/
      28                 :            : 
      29                 :            : #include <jobs/jobexecutor.hxx>
      30                 :            : #include <jobs/job.hxx>
      31                 :            : #include <jobs/joburl.hxx>
      32                 :            : 
      33                 :            : #include <classes/converter.hxx>
      34                 :            : #include <threadhelp/transactionguard.hxx>
      35                 :            : #include <threadhelp/readguard.hxx>
      36                 :            : #include <threadhelp/writeguard.hxx>
      37                 :            : #include <general.h>
      38                 :            : #include <services.h>
      39                 :            : 
      40                 :            : #include "helper/mischelper.hxx"
      41                 :            : 
      42                 :            : #include <com/sun/star/beans/XPropertySet.hpp>
      43                 :            : #include <com/sun/star/container/XNameAccess.hpp>
      44                 :            : #include <com/sun/star/container/XContainer.hpp>
      45                 :            : 
      46                 :            : #include <unotools/configpaths.hxx>
      47                 :            : #include <rtl/ustrbuf.hxx>
      48                 :            : #include <vcl/svapp.hxx>
      49                 :            : 
      50                 :            : #include <rtl/logfile.hxx>
      51                 :            : 
      52                 :            : namespace framework{
      53                 :            : 
      54 [ +  + ][ +  - ]:     238786 : DEFINE_XINTERFACE_6( JobExecutor                                                               ,
      55                 :            :                      OWeakObject                                                               ,
      56                 :            :                      DIRECT_INTERFACE(css::lang::XTypeProvider                                ),
      57                 :            :                      DIRECT_INTERFACE(css::lang::XServiceInfo                                 ),
      58                 :            :                      DIRECT_INTERFACE(css::task::XJobExecutor                                 ),
      59                 :            :                      DIRECT_INTERFACE(css::container::XContainerListener                      ),
      60                 :            :                      DIRECT_INTERFACE(css::document::XEventListener                           ),
      61                 :            :                      DERIVED_INTERFACE(css::lang::XEventListener,css::document::XEventListener)
      62                 :            :                    )
      63                 :            : 
      64 [ #  # ][ #  # ]:          0 : DEFINE_XTYPEPROVIDER_6( JobExecutor                       ,
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
      65                 :            :                         css::lang::XTypeProvider          ,
      66                 :            :                         css::lang::XServiceInfo           ,
      67                 :            :                         css::task::XJobExecutor           ,
      68                 :            :                         css::container::XContainerListener,
      69                 :            :                         css::document::XEventListener     ,
      70                 :            :                         css::lang::XEventListener
      71                 :            :                       )
      72                 :            : 
      73 [ +  - ][ +  - ]:       3800 : DEFINE_XSERVICEINFO_ONEINSTANCESERVICE( JobExecutor                   ,
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ #  # ][ #  # ]
      74                 :            :                                         ::cppu::OWeakObject           ,
      75                 :            :                                         SERVICENAME_JOBEXECUTOR       ,
      76                 :            :                                         IMPLEMENTATIONNAME_JOBEXECUTOR
      77                 :            :                                       )
      78                 :            : 
      79 [ +  - ][ +  - ]:        230 : DEFINE_INIT_SERVICE( JobExecutor,
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
      80                 :            :                      {
      81                 :            :                          m_xModuleManager = css::uno::Reference< css::frame::XModuleManager >(
      82                 :            :                              m_xSMGR->createInstance(
      83                 :            :                                  SERVICENAME_MODULEMANAGER ),
      84                 :            :                              css::uno::UNO_QUERY_THROW );
      85                 :            : 
      86                 :            :                          /*Attention
      87                 :            :                              I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
      88                 :            :                              to create a new instance of this class by our own supported service factory.
      89                 :            :                              see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
      90                 :            :                          */
      91                 :            :                         // read the list of all currently registered events inside configuration.
      92                 :            :                         // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
      93                 :            :                         // We need it later to check if an incoming event request can be executed successfully
      94                 :            :                         // or must be rejected. It's an optimization! Of course we must implement updating of this
      95                 :            :                         // list too ... Be listener at the configuration.
      96                 :            : 
      97                 :            :                         m_aConfig.open(ConfigAccess::E_READONLY);
      98                 :            :                         if (m_aConfig.getMode() == ConfigAccess::E_READONLY)
      99                 :            :                         {
     100                 :            :                             css::uno::Reference< css::container::XNameAccess > xRegistry(m_aConfig.cfg(), css::uno::UNO_QUERY);
     101                 :            :                             if (xRegistry.is())
     102                 :            :                                 m_lEvents = Converter::convert_seqOUString2OUStringList(xRegistry->getElementNames());
     103                 :            : 
     104                 :            :                             css::uno::Reference< css::container::XContainer > xNotifier(m_aConfig.cfg(), css::uno::UNO_QUERY);
     105                 :            :                             if (xNotifier.is())
     106                 :            :                             {
     107                 :            :                                 m_xConfigListener = new WeakContainerListener(this);
     108                 :            :                                 xNotifier->addContainerListener(m_xConfigListener);
     109                 :            :                             }
     110                 :            : 
     111                 :            :                             // don't close cfg here!
     112                 :            :                             // It will be done inside disposing ...
     113                 :            :                         }
     114                 :            :                      }
     115                 :            :                    )
     116                 :            : 
     117                 :            : //________________________________
     118                 :            : 
     119                 :            : /**
     120                 :            :     @short      standard ctor
     121                 :            :     @descr      It initialize this new instance.
     122                 :            : 
     123                 :            :     @param      xSMGR
     124                 :            :                     reference to the uno service manager
     125                 :            :  */
     126                 :        230 : JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR )
     127         [ +  - ]:        230 :     : ThreadHelpBase      (&Application::GetSolarMutex()                                   )
     128                 :            :     , ::cppu::OWeakObject (                                                                )
     129                 :            :     , m_xSMGR             (xSMGR                                                           )
     130                 :            :     , m_xModuleManager    (                                                                )
     131 [ +  - ][ +  - ]:        460 :     , m_aConfig           (xSMGR, ::rtl::OUString::createFromAscii(JobData::EVENTCFG_ROOT) )
                 [ +  - ]
     132                 :            : {
     133                 :            :     // Don't do any reference related code here! Do it inside special
     134                 :            :     // impl_ method() ... see DEFINE_INIT_SERVICE() macro for further informations.
     135                 :        230 : }
     136                 :            : 
     137 [ +  - ][ +  - ]:        230 : JobExecutor::~JobExecutor()
         [ +  - ][ +  - ]
     138                 :            : {
     139 [ +  - ][ +  - ]:        230 :     css::uno::Reference< css::container::XContainer > xNotifier(m_aConfig.cfg(), css::uno::UNO_QUERY);
     140         [ +  - ]:        230 :     if (xNotifier.is())
     141 [ +  - ][ +  - ]:        230 :         xNotifier->removeContainerListener(m_xConfigListener);
     142         [ -  + ]:        460 : }
     143                 :            : 
     144                 :            : //________________________________
     145                 :            : 
     146                 :            : /**
     147                 :            :     @short  implementation of XJobExecutor interface
     148                 :            :     @descr  We use the given event to locate any registered job inside our configuration
     149                 :            :             and execute it. Further we control the lifetime of it and supress
     150                 :            :             shutdown of the office till all jobs was finished.
     151                 :            : 
     152                 :            :     @param  sEvent
     153                 :            :                 is used to locate registered jobs
     154                 :            :  */
     155                 :        163 : void SAL_CALL JobExecutor::trigger( const ::rtl::OUString& sEvent ) throw(css::uno::RuntimeException)
     156                 :            : {
     157                 :            :     RTL_LOGFILE_CONTEXT(aLog, "fwk (as96863) JobExecutor::trigger()");
     158                 :            : 
     159                 :            :     /* SAFE { */
     160         [ +  - ]:        163 :     ReadGuard aReadLock(m_aLock);
     161                 :            : 
     162                 :            :     // Optimization!
     163                 :            :     // Check if the given event name exist inside configuration and reject wrong requests.
     164                 :            :     // This optimization supress using of the cfg api for getting event and job descriptions ...
     165 [ +  - ][ +  - ]:        163 :     if (m_lEvents.find(sEvent) == m_lEvents.end())
                 [ +  - ]
     166                 :        163 :         return;
     167                 :            : 
     168                 :            :     // get list of all enabled jobs
     169                 :            :     // The called static helper methods read it from the configuration and
     170                 :            :     // filter disabled jobs using it's time stamp values.
     171         [ #  # ]:          0 :     css::uno::Sequence< ::rtl::OUString > lJobs = JobData::getEnabledJobsForEvent(m_xSMGR, sEvent);
     172                 :            : 
     173         [ #  # ]:          0 :     aReadLock.unlock();
     174                 :            :     /* } SAFE */
     175                 :            : 
     176                 :            :     // step over all enabled jobs and execute it
     177                 :          0 :     sal_Int32 c = lJobs.getLength();
     178         [ #  # ]:          0 :     for (sal_Int32 j=0; j<c; ++j)
     179                 :            :     {
     180                 :            :         /* SAFE { */
     181         [ #  # ]:          0 :         aReadLock.lock();
     182                 :            : 
     183         [ #  # ]:          0 :         JobData aCfg(m_xSMGR);
     184 [ #  # ][ #  # ]:          0 :         aCfg.setEvent(sEvent, lJobs[j]);
     185         [ #  # ]:          0 :         aCfg.setEnvironment(JobData::E_EXECUTION);
     186                 :            : 
     187                 :            :         /*Attention!
     188                 :            :             Jobs implements interfaces and dies by ref count!
     189                 :            :             And freeing of such uno object is done by uno itself.
     190                 :            :             So we have to use dynamic memory everytimes.
     191                 :            :          */
     192         [ #  # ]:          0 :         Job* pJob = new Job(m_xSMGR, css::uno::Reference< css::frame::XFrame >());
     193 [ #  # ][ #  # ]:          0 :         css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY);
     194         [ #  # ]:          0 :         pJob->setJobData(aCfg);
     195                 :            : 
     196         [ #  # ]:          0 :         aReadLock.unlock();
     197                 :            :         /* } SAFE */
     198                 :            : 
     199 [ #  # ][ #  # ]:          0 :         pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
                 [ #  # ]
     200 [ #  # ][ #  # ]:        163 :     }
         [ +  - ][ -  + ]
     201                 :            : }
     202                 :            : 
     203                 :            : //________________________________
     204                 :            : 
     205                 :      33077 : void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent ) throw(css::uno::RuntimeException)
     206                 :            : {
     207                 :      33077 :     const char EVENT_ON_NEW[] = "OnNew";                            // Doc UI  event
     208                 :      33077 :     const char EVENT_ON_LOAD[] = "OnLoad";                          // Doc UI  event
     209                 :      33077 :     const char EVENT_ON_CREATE[] = "OnCreate";                      // Doc API event
     210                 :      33077 :     const char EVENT_ON_LOAD_FINISHED[] = "OnLoadFinished";         // Doc API event
     211                 :      33077 :     ::rtl::OUString EVENT_ON_DOCUMENT_OPENED("onDocumentOpened");   // Job UI  event : OnNew    or OnLoad
     212                 :      33077 :     ::rtl::OUString EVENT_ON_DOCUMENT_ADDED("onDocumentAdded");     // Job API event : OnCreate or OnLoadFinished
     213                 :            : 
     214                 :            :     /* SAFE { */
     215         [ +  - ]:      33077 :     ReadGuard aReadLock(m_aLock);
     216                 :            : 
     217         [ +  - ]:      33077 :     ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding > lJobs;
     218                 :            : 
     219                 :            :     // Optimization!
     220                 :            :     // Check if the given event name exist inside configuration and reject wrong requests.
     221                 :            :     // This optimization supress using of the cfg api for getting event and job descriptions.
     222                 :            :     // see using of m_lEvents.find() below ...
     223                 :            : 
     224                 :            :     // retrieve event context from event source
     225                 :      33077 :     rtl::OUString aModuleIdentifier;
     226                 :            :     try
     227                 :            :     {
     228 [ +  + ][ +  - ]:      33077 :         aModuleIdentifier = m_xModuleManager->identify( aEvent.Source );
     229                 :            :     }
     230         [ +  - ]:        786 :     catch( const css::uno::Exception& )
     231                 :            :     {}
     232                 :            : 
     233                 :            :     // Special feature: If the events "OnNew" or "OnLoad" occures - we generate our own event "onDocumentOpened".
     234         [ +  + ]:      65069 :     if (
           [ +  +  +  + ]
     235                 :      33077 :         (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_NEW))) ||
     236                 :      31992 :         (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_LOAD)))
     237                 :            :        )
     238                 :            :     {
     239 [ +  - ][ +  - ]:       1220 :         if (m_lEvents.find(EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end())
                 [ +  + ]
     240         [ +  - ]:       1125 :             JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_OPENED, lJobs);
     241                 :            :     }
     242                 :            : 
     243                 :            :     // Special feature: If the events "OnCreate" or "OnLoadFinished" occures - we generate our own event "onDocumentAdded".
     244         [ +  + ]:      64307 :     if (
           [ +  +  +  + ]
     245                 :      33077 :         (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_CREATE))) ||
     246                 :      31230 :         (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_LOAD_FINISHED)))
     247                 :            :        )
     248                 :            :     {
     249 [ +  - ][ +  - ]:       2752 :         if (m_lEvents.find(EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end())
                 [ -  + ]
     250         [ #  # ]:          0 :             JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_ADDED, lJobs);
     251                 :            :     }
     252                 :            : 
     253                 :            :     // Add all jobs for "real" notified event too .-)
     254 [ +  - ][ +  - ]:      33077 :     if (m_lEvents.find(aEvent.EventName) != m_lEvents.end())
                 [ -  + ]
     255         [ #  # ]:          0 :         JobData::appendEnabledJobsForEvent(m_xSMGR, aEvent.EventName, lJobs);
     256                 :            : 
     257         [ +  - ]:      33077 :     aReadLock.unlock();
     258                 :            :     /* } SAFE */
     259                 :            : 
     260                 :            :     // step over all enabled jobs and execute it
     261                 :      33077 :     ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding >::const_iterator pIt;
     262 [ +  - ][ +  - ]:      68404 :     for (  pIt  = lJobs.begin();
                 [ +  + ]
     263                 :      34202 :            pIt != lJobs.end()  ;
     264                 :            :          ++pIt                 )
     265                 :            :     {
     266                 :            :         /* SAFE { */
     267         [ +  - ]:       1125 :         aReadLock.lock();
     268                 :            : 
     269                 :       1125 :         const JobData::TJob2DocEventBinding& rBinding = *pIt;
     270                 :            : 
     271         [ +  - ]:       1125 :         JobData aCfg(m_xSMGR);
     272         [ +  - ]:       1125 :         aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName);
     273         [ +  - ]:       1125 :         aCfg.setEnvironment(JobData::E_DOCUMENTEVENT);
     274                 :            : 
     275 [ +  - ][ +  + ]:       1125 :         if (!aCfg.hasCorrectContext(aModuleIdentifier))
     276                 :       1099 :             continue;
     277                 :            : 
     278                 :            :         /*Attention!
     279                 :            :             Jobs implements interfaces and dies by ref count!
     280                 :            :             And freeing of such uno object is done by uno itself.
     281                 :            :             So we have to use dynamic memory everytimes.
     282                 :            :          */
     283         [ +  - ]:         26 :         css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY);
     284         [ +  - ]:         26 :         Job* pJob = new Job(m_xSMGR, xModel);
     285 [ +  - ][ +  - ]:         26 :         css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY);
     286         [ +  - ]:         26 :         pJob->setJobData(aCfg);
     287                 :            : 
     288         [ +  - ]:         26 :         aReadLock.unlock();
     289                 :            :         /* } SAFE */
     290                 :            : 
     291 [ +  - ][ +  - ]:         26 :         pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
                 [ +  - ]
     292 [ +  - ][ +  + ]:      34202 :     }
                 [ +  - ]
     293         [ -  + ]:      33863 : }
     294                 :            : 
     295                 :            : //________________________________
     296                 :            : 
     297                 :          0 : void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException)
     298                 :            : {
     299                 :          0 :     ::rtl::OUString sValue;
     300         [ #  # ]:          0 :     if (aEvent.Accessor >>= sValue)
     301                 :            :     {
     302         [ #  # ]:          0 :         ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
     303         [ #  # ]:          0 :         if (!sEvent.isEmpty())
     304                 :            :         {
     305         [ #  # ]:          0 :             OUStringList::iterator pEvent = m_lEvents.find(sEvent);
     306 [ #  # ][ #  # ]:          0 :             if (pEvent == m_lEvents.end())
     307         [ #  # ]:          0 :                 m_lEvents.push_back(sEvent);
     308                 :          0 :         }
     309                 :          0 :     }
     310                 :          0 : }
     311                 :            : 
     312                 :          0 : void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException)
     313                 :            : {
     314                 :          0 :     ::rtl::OUString sValue;
     315         [ #  # ]:          0 :     if (aEvent.Accessor >>= sValue)
     316                 :            :     {
     317         [ #  # ]:          0 :         ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
     318         [ #  # ]:          0 :         if (!sEvent.isEmpty())
     319                 :            :         {
     320         [ #  # ]:          0 :             OUStringList::iterator pEvent = m_lEvents.find(sEvent);
     321 [ #  # ][ #  # ]:          0 :             if (pEvent != m_lEvents.end())
     322         [ #  # ]:          0 :                 m_lEvents.erase(pEvent);
     323                 :          0 :         }
     324                 :          0 :     }
     325                 :          0 : }
     326                 :            : 
     327                 :          0 : void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& ) throw(css::uno::RuntimeException)
     328                 :            : {
     329                 :            :     // I'm not interested on changed items :-)
     330                 :          0 : }
     331                 :            : 
     332                 :            : //________________________________
     333                 :            : 
     334                 :            : /** @short  the used cfg changes notifier wish to be released in its reference.
     335                 :            : 
     336                 :            :     @descr  We close our internal used configuration instance to
     337                 :            :             free this reference.
     338                 :            : 
     339                 :            :     @attention  For the special feature "bind global document event broadcaster to job execution"
     340                 :            :                 this job executor instance was registered from outside code as
     341                 :            :                 css.document.XEventListener. So it can be, that this disposing call comes from
     342                 :            :                 the global event broadcaster service. But we don't hold any reference to this service
     343                 :            :                 which can or must be released. Because this broadcaster itself is an one instance service
     344                 :            :                 too, we can ignore this request. On the other side we must relase our internal CFG
     345                 :            :                 reference ... SOLUTION => check the given event source and react only, if it's our internal
     346                 :            :                 hold configuration object!
     347                 :            :  */
     348                 :          0 : void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException)
     349                 :            : {
     350                 :            :     /* SAFE { */
     351         [ #  # ]:          0 :     ReadGuard aReadLock(m_aLock);
     352 [ #  # ][ #  # ]:          0 :     css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY);
     353 [ #  # ][ #  # ]:          0 :     if (
                 [ #  # ]
     354         [ #  # ]:          0 :         (xCFG                == aEvent.Source        ) &&
     355         [ #  # ]:          0 :         (m_aConfig.getMode() != ConfigAccess::E_CLOSED)
     356                 :            :        )
     357                 :            :     {
     358         [ #  # ]:          0 :         m_aConfig.close();
     359                 :            :     }
     360 [ #  # ][ #  # ]:          0 :     aReadLock.unlock();
     361                 :            :     /* } SAFE */
     362                 :          0 : }
     363                 :            : 
     364                 :            : } // namespace framework
     365                 :            : 
     366                 :            : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10