Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/tools/LifeTime.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <LifeTime.hxx>
21
#include <osl/diagnose.h>
22
23
#include <com/sun/star/document/XStorageChangeListener.hpp>
24
#include <com/sun/star/lang/XComponent.hpp>
25
#include <com/sun/star/util/CloseVetoException.hpp>
26
#include <com/sun/star/util/XCloseListener.hpp>
27
#include <com/sun/star/util/XCloseable.hpp>
28
#include <com/sun/star/util/XModifyListener.hpp>
29
#include <com/sun/star/view/XSelectionChangeListener.hpp>
30
#include <comphelper/diagnose_ex.hxx>
31
#include <sal/log.hxx>
32
33
using namespace ::com::sun::star;
34
35
namespace apphelper
36
{
37
38
LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent )
39
0
    : m_pComponent(pComponent)
40
0
{
41
0
    m_bDisposed = false;
42
0
    m_bInDispose = false;
43
0
    m_nAccessCount = 0;
44
0
    m_nLongLastingCallCount = 0;
45
0
    m_aNoAccessCountCondition.set();
46
0
    m_aNoLongLastingCallCountCondition.set();
47
0
}
48
49
LifeTimeManager::~LifeTimeManager()
50
0
{
51
0
}
52
53
bool LifeTimeManager::impl_isDisposed( bool bAssert )
54
0
{
55
0
    if( m_bDisposed || m_bInDispose )
56
0
    {
57
0
        if( bAssert )
58
0
        {
59
0
            OSL_FAIL( "This component is already disposed " );
60
0
        }
61
0
        return true;
62
0
    }
63
0
    return false;
64
0
}
65
66
bool LifeTimeManager::impl_canStartApiCall()
67
0
{
68
0
    if( impl_isDisposed() )
69
0
        return false; //behave passive if already disposed
70
71
    //mutex is acquired
72
0
    return true;
73
0
}
74
75
void LifeTimeManager::impl_registerApiCall(bool bLongLastingCall)
76
0
{
77
    //only allowed if not disposed
78
    //do not acquire the mutex here because it will be acquired already
79
0
    m_nAccessCount++;
80
0
    if(m_nAccessCount==1)
81
        //@todo? is it ok to wake some threads here while we have acquired the mutex?
82
0
        m_aNoAccessCountCondition.reset();
83
84
0
    if(bLongLastingCall)
85
0
        m_nLongLastingCallCount++;
86
0
    if(m_nLongLastingCallCount==1)
87
0
        m_aNoLongLastingCallCountCondition.reset();
88
0
}
89
90
void LifeTimeManager::impl_unregisterApiCall(std::unique_lock<std::mutex>& rGuard, bool bLongLastingCall)
91
0
{
92
    //Mutex needs to be acquired exactly once
93
    //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
94
95
0
    OSL_ENSURE( m_nAccessCount>0, "access count mismatch" );
96
0
    m_nAccessCount--;
97
0
    if(bLongLastingCall)
98
0
        m_nLongLastingCallCount--;
99
0
    if( m_nLongLastingCallCount==0 )
100
0
    {
101
0
        m_aNoLongLastingCallCountCondition.set();
102
0
    }
103
0
    if( m_nAccessCount== 0)
104
0
    {
105
0
        m_aNoAccessCountCondition.set();
106
0
        impl_apiCallCountReachedNull(rGuard);
107
108
0
    }
109
0
}
110
111
bool LifeTimeManager::dispose()
112
0
{
113
    //hold no mutex
114
0
    {
115
0
        std::unique_lock aGuard( m_aAccessMutex );
116
117
0
        if( m_bDisposed || m_bInDispose )
118
0
        {
119
0
            SAL_WARN("chart2",  "This component is already disposed " );
120
0
            return false; //behave passive if already disposed
121
0
        }
122
123
0
        m_bInDispose = true;
124
        //adding any listener is not allowed anymore
125
        //new calls will not be accepted
126
        //still running calls have the freedom to finish their work without crash
127
128
0
        uno::Reference< lang::XComponent > xComponent(m_pComponent);
129
0
        if(xComponent.is())
130
0
        {
131
            // notify XCLoseListeners
132
0
            lang::EventObject aEvent( xComponent );
133
0
            m_aCloseListeners.disposeAndClear( aGuard, aEvent );
134
0
            m_aModifyListeners.disposeAndClear( aGuard, aEvent );
135
0
            m_aStorageChangeListeners.disposeAndClear( aGuard, aEvent );
136
0
            m_aEventListeners.disposeAndClear( aGuard, aEvent );
137
0
            m_aSelectionChangeListeners.disposeAndClear( aGuard, aEvent );
138
0
        }
139
140
0
        OSL_ENSURE( !m_bDisposed, "dispose was called already" );
141
0
        m_bDisposed = true;
142
0
    }
143
    //no mutex is acquired
144
145
    //wait until all still running calls have finished
146
    //the accessCount cannot grow anymore, because all calls will return after checking m_bDisposed
147
0
    m_aNoAccessCountCondition.wait();
148
149
    //we are the only ones working on our data now
150
151
0
    return true;
152
    //--release all resources and references after calling this method successful
153
0
}
154
155
CloseableLifeTimeManager::CloseableLifeTimeManager( css::util::XCloseable* pCloseable
156
        , css::lang::XComponent* pComponent )
157
0
        : LifeTimeManager( pComponent )
158
0
        , m_pCloseable(pCloseable)
159
0
{
160
0
    m_bClosed = false;
161
0
    m_bInTryClose = false;
162
0
    m_bOwnership = false;
163
0
    m_aEndTryClosingCondition.set();
164
0
}
165
166
CloseableLifeTimeManager::~CloseableLifeTimeManager()
167
0
{
168
0
}
169
170
bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert )
171
0
{
172
0
    if( impl_isDisposed( bAssert ) )
173
0
        return true;
174
175
0
    if( m_bClosed )
176
0
    {
177
0
        if( bAssert )
178
0
        {
179
0
            OSL_FAIL( "This object is already closed" );
180
0
        }
181
0
        return true;
182
0
    }
183
0
    return false;
184
0
}
185
186
bool CloseableLifeTimeManager::g_close_startTryClose(bool bDeliverOwnership)
187
0
{
188
    //no mutex is allowed to be acquired
189
0
    {
190
0
        std::unique_lock aGuard( m_aAccessMutex );
191
0
        if( impl_isDisposedOrClosed(false) )
192
0
            return false;
193
194
        //Mutex needs to be acquired exactly once; will be released inbetween
195
0
        if( !impl_canStartApiCall() )
196
0
            return false;
197
        //mutex is acquired
198
199
        //not closed already -> we try to close again
200
0
        m_bInTryClose = true;
201
0
        m_aEndTryClosingCondition.reset();
202
203
0
        impl_registerApiCall(false);
204
0
    }
205
206
    //no mutex is acquired
207
208
    //only remove listener calls will be worked on until end of tryclose
209
    //all other new calls will wait till end of try close // @todo? is that really ok
210
211
    //?? still running calls have the freedom to finish their work without crash
212
213
0
    try
214
0
    {
215
0
        uno::Reference< util::XCloseable > xCloseable(m_pCloseable);
216
0
        if(xCloseable.is())
217
0
        {
218
0
            std::unique_lock aGuard( m_aAccessMutex );
219
            //--call queryClosing on all registered close listeners
220
0
            if( m_aCloseListeners.getLength(aGuard) )
221
0
            {
222
0
                lang::EventObject aEvent( xCloseable );
223
0
                m_aCloseListeners.forEach(aGuard,
224
0
                    [&aEvent, bDeliverOwnership](const uno::Reference<util::XCloseListener>& l)
225
0
                    {
226
0
                        l->queryClosing(aEvent, bDeliverOwnership);
227
0
                    });
228
0
            }
229
0
        }
230
0
    }
231
0
    catch( const uno::Exception& )
232
0
    {
233
        //no mutex is acquired
234
0
        g_close_endTryClose();
235
0
        throw;
236
0
    }
237
0
    return true;
238
0
}
239
240
void CloseableLifeTimeManager::g_close_endTryClose()
241
0
{
242
    //this method is called, if the try to close was not successful
243
0
    std::unique_lock aGuard( m_aAccessMutex );
244
245
0
    m_bOwnership = false;
246
0
    m_bInTryClose = false;
247
0
    m_aEndTryClosingCondition.set();
248
249
    //Mutex needs to be acquired exactly once
250
    //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
251
0
    impl_unregisterApiCall(aGuard, false);
252
0
}
253
254
void CloseableLifeTimeManager::g_close_isNeedToCancelLongLastingCalls( bool bDeliverOwnership, util::CloseVetoException const & ex )
255
0
{
256
    //this method is called when no closelistener has had a veto during queryclosing
257
    //the method returns false, if nothing stands against closing anymore
258
    //it returns true, if some longlasting calls are running, which might be cancelled
259
    //it throws the given exception, if long calls are running but not cancelable
260
261
0
    std::unique_lock aGuard( m_aAccessMutex );
262
    //this count cannot grow after try of close has started, because we wait in all those methods for end of try closing
263
0
    if( !m_nLongLastingCallCount )
264
0
        return;
265
266
0
    m_bOwnership = bDeliverOwnership;
267
0
    m_bInTryClose = false;
268
0
    m_aEndTryClosingCondition.set();
269
270
    //Mutex needs to be acquired exactly once
271
    //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
272
0
    impl_unregisterApiCall(aGuard, false);
273
274
0
    throw ex;
275
0
}
276
277
void CloseableLifeTimeManager::g_close_endTryClose_doClose()
278
0
{
279
    //this method is called, if the try to close was successful
280
0
    std::unique_lock aGuard( m_aAccessMutex );
281
282
0
    m_bInTryClose       = false;
283
0
    m_aEndTryClosingCondition.set();
284
285
    //Mutex needs to be acquired exactly once
286
    //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
287
0
    impl_unregisterApiCall(aGuard, false);
288
0
    impl_doClose(aGuard);
289
0
}
290
291
void CloseableLifeTimeManager::impl_apiCallCountReachedNull(std::unique_lock<std::mutex>& rGuard)
292
0
{
293
    //Mutex needs to be acquired exactly once
294
    //mutex will be released inbetween in impl_doClose()
295
0
    if( m_pCloseable && m_bOwnership )
296
0
        impl_doClose(rGuard);
297
0
}
298
299
void CloseableLifeTimeManager::impl_doClose(std::unique_lock<std::mutex>& rGuard)
300
0
{
301
    //Mutex needs to be acquired exactly once before calling impl_doClose()
302
303
0
    if(m_bClosed)
304
0
        return; //behave as passive as possible, if disposed or closed already
305
0
    if( m_bDisposed || m_bInDispose )
306
0
        return; //behave as passive as possible, if disposed or closed already
307
308
0
    m_bClosed = true;
309
310
0
    uno::Reference< util::XCloseable > xCloseable;
311
0
    xCloseable.set(m_pCloseable);
312
0
    try
313
0
    {
314
0
        if(xCloseable.is())
315
0
        {
316
            //--call notifyClosing on all registered close listeners
317
0
            if( m_aCloseListeners.getLength(rGuard) )
318
0
            {
319
0
                lang::EventObject aEvent( xCloseable );
320
0
                m_aCloseListeners.notifyEach(rGuard, &util::XCloseListener::notifyClosing, aEvent);
321
0
            }
322
0
        }
323
0
    }
324
0
    catch( const uno::Exception& )
325
0
    {
326
0
        DBG_UNHANDLED_EXCEPTION("chart2");
327
0
    }
328
329
0
    rGuard.unlock();
330
0
    if(xCloseable.is())
331
0
    {
332
0
        uno::Reference< lang::XComponent > xComponent( xCloseable, uno::UNO_QUERY );
333
0
        if(xComponent.is())
334
0
        {
335
0
            OSL_ENSURE( m_bClosed, "a not closed component will be disposed " );
336
0
            xComponent->dispose();
337
0
        }
338
0
    }
339
0
    rGuard.lock();
340
0
}
341
342
void CloseableLifeTimeManager::g_addCloseListener( const uno::Reference< util::XCloseListener > & xListener )
343
0
{
344
0
    std::unique_lock aGuard( m_aAccessMutex );
345
    //Mutex needs to be acquired exactly once; will be released inbetween
346
0
    if( !impl_canStartApiCall() )
347
0
        return;
348
    //mutex is acquired
349
350
0
    m_aCloseListeners.addInterface( aGuard, xListener );
351
0
    m_bOwnership = false;
352
0
}
353
354
bool CloseableLifeTimeManager::impl_canStartApiCall()
355
0
{
356
    //Mutex needs to be acquired exactly once before calling this method
357
    //the mutex will be released inbetween and reacquired
358
359
0
    if( impl_isDisposed() )
360
0
        return false; //behave passive if already disposed
361
0
    if( m_bClosed )
362
0
        return false; //behave passive if closing is already done
363
364
    //during try-close most calls need to wait for the decision
365
0
    while( m_bInTryClose )
366
0
    {
367
        //if someone tries to close this object at the moment
368
        //we need to wait for his end because the result of the preceding call
369
        //is relevant for our behaviour here
370
371
0
        m_aAccessMutex.unlock();
372
0
        m_aEndTryClosingCondition.wait(); //@todo??? this may block??? try closing
373
0
        m_aAccessMutex.lock();
374
0
        if( m_bDisposed || m_bInDispose || m_bClosed )
375
0
            return false; //return if closed already
376
0
    }
377
    //mutex is acquired
378
0
    return true;
379
0
}
380
381
bool LifeTimeGuard::startApiCall(bool bLongLastingCall)
382
0
{
383
    //Mutex needs to be acquired exactly once; will be released inbetween
384
    //mutex is required due to constructor of LifeTimeGuard
385
386
0
    OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" );
387
0
    if(m_bCallRegistered)
388
0
        return false;
389
390
    //Mutex needs to be acquired exactly once; will be released inbetween
391
0
    if( !m_rManager.impl_canStartApiCall() )
392
0
        return false;
393
    //mutex is acquired
394
395
0
    m_bCallRegistered = true;
396
0
    m_bLongLastingCallRegistered = bLongLastingCall;
397
0
    m_rManager.impl_registerApiCall(bLongLastingCall);
398
0
    return true;
399
0
}
400
401
LifeTimeGuard::~LifeTimeGuard()
402
0
{
403
0
    try
404
0
    {
405
        //do acquire the mutex if it was cleared before
406
0
        if (!m_guard.owns_lock())
407
0
            m_guard.lock();
408
0
        if(m_bCallRegistered)
409
0
        {
410
            //Mutex needs to be acquired exactly once
411
            //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
412
0
            m_rManager.impl_unregisterApiCall(m_guard, m_bLongLastingCallRegistered);
413
0
        }
414
0
    }
415
0
    catch( uno::Exception& ex )
416
0
    {
417
        //@todo ? allow a uno::RuntimeException from dispose to travel through??
418
0
        ex.Context.is(); //to avoid compilation warnings
419
0
    }
420
0
}
421
422
}//end namespace apphelper
423
424
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */