Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/io/source/stm/opump.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
21
#include <sal/log.hxx>
22
23
#include <com/sun/star/io/IOException.hpp>
24
#include <com/sun/star/io/NotConnectedException.hpp>
25
#include <com/sun/star/io/XActiveDataSource.hpp>
26
#include <com/sun/star/io/XActiveDataSink.hpp>
27
#include <com/sun/star/io/XActiveDataControl.hpp>
28
#include <com/sun/star/io/XConnectable.hpp>
29
#include <com/sun/star/lang/XServiceInfo.hpp>
30
#include <com/sun/star/uno/XComponentContext.hpp>
31
32
#include <cppuhelper/implbase.hxx>
33
#include <comphelper/interfacecontainer4.hxx>
34
#include <cppuhelper/supportsservice.hxx>
35
#include <osl/thread.h>
36
#include <mutex>
37
38
using namespace cppu;
39
using namespace com::sun::star::uno;
40
using namespace com::sun::star::lang;
41
using namespace com::sun::star::io;
42
43
namespace io_stm {
44
45
    namespace {
46
47
    class Pump : public WeakImplHelper<
48
          XActiveDataSource, XActiveDataSink, XActiveDataControl, XConnectable, XServiceInfo >
49
    {
50
        std::mutex                              m_aMutex;
51
        oslThread                               m_aThread;
52
53
        Reference< XConnectable >               m_xPred;
54
        Reference< XConnectable >               m_xSucc;
55
        Reference< XInputStream >               m_xInput;
56
        Reference< XOutputStream >              m_xOutput;
57
        comphelper::OInterfaceContainerHelper4<XStreamListener>  m_cnt;
58
        bool                                m_closeFired;
59
60
        void run();
61
        static void static_run( void* pObject );
62
63
        void close();
64
        void joinWithThread();
65
        void fireClose();
66
        void fireStarted();
67
        void fireTerminated();
68
        void fireError( const Any &a );
69
70
    public:
71
        Pump();
72
        virtual ~Pump() override;
73
74
        // XActiveDataSource
75
        virtual void SAL_CALL setOutputStream( const Reference< css::io::XOutputStream >& xOutput ) override;
76
        virtual Reference< css::io::XOutputStream > SAL_CALL getOutputStream() override;
77
78
        // XActiveDataSink
79
        virtual void SAL_CALL setInputStream( const Reference< css::io::XInputStream >& xStream ) override;
80
        virtual Reference< css::io::XInputStream > SAL_CALL getInputStream() override;
81
82
        // XActiveDataControl
83
        virtual void SAL_CALL addListener( const Reference< css::io::XStreamListener >& xListener ) override;
84
        virtual void SAL_CALL removeListener( const Reference< css::io::XStreamListener >& xListener ) override;
85
        virtual void SAL_CALL start() override;
86
        virtual void SAL_CALL terminate() override;
87
88
        // XConnectable
89
        virtual void SAL_CALL setPredecessor( const Reference< css::io::XConnectable >& xPred ) override;
90
        virtual Reference< css::io::XConnectable > SAL_CALL getPredecessor() override;
91
        virtual void SAL_CALL setSuccessor( const Reference< css::io::XConnectable >& xSucc ) override;
92
        virtual Reference< css::io::XConnectable > SAL_CALL getSuccessor() override;
93
94
    public: // XServiceInfo
95
        virtual OUString    SAL_CALL getImplementationName() override;
96
        virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
97
        virtual sal_Bool     SAL_CALL supportsService(const OUString& ServiceName) override;
98
    };
99
100
    }
101
102
0
Pump::Pump() : m_aThread( nullptr ),
103
0
               m_closeFired( false )
104
0
{
105
0
}
106
107
Pump::~Pump()
108
0
{
109
    // exit gracefully
110
0
    if( m_aThread )
111
0
    {
112
0
        osl_joinWithThread( m_aThread );
113
0
        osl_destroyThread( m_aThread );
114
0
    }
115
0
}
116
117
void Pump::fireError( const  Any & exception )
118
0
{
119
0
    std::unique_lock guard( m_aMutex );
120
0
    comphelper::OInterfaceIteratorHelper4<XStreamListener> iter( guard, m_cnt );
121
0
    guard.unlock();
122
0
    while( iter.hasMoreElements() )
123
0
    {
124
0
        try
125
0
        {
126
0
            iter.next()->error( exception );
127
0
        }
128
0
        catch ( const RuntimeException &e )
129
0
        {
130
0
            SAL_WARN("io.streams","com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners" << e);
131
0
        }
132
0
    }
133
0
}
134
135
void Pump::fireClose()
136
0
{
137
0
    bool bFire = false;
138
0
    {
139
0
        std::unique_lock guard( m_aMutex );
140
0
        if( ! m_closeFired  )
141
0
        {
142
0
            m_closeFired = true;
143
0
            bFire = true;
144
0
        }
145
0
    }
146
147
0
    if( !bFire )
148
0
        return;
149
150
0
    std::unique_lock guard( m_aMutex );
151
0
    comphelper::OInterfaceIteratorHelper4<XStreamListener> iter( guard, m_cnt );
152
0
    guard.unlock();
153
0
    while( iter.hasMoreElements() )
154
0
    {
155
0
        try
156
0
        {
157
0
            iter.next()->closed( );
158
0
        }
159
0
        catch ( const RuntimeException &e )
160
0
        {
161
0
            SAL_WARN("io.streams","com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners" << e);
162
0
        }
163
0
    }
164
0
}
165
166
void Pump::fireStarted()
167
0
{
168
0
    std::unique_lock guard( m_aMutex );
169
0
    comphelper::OInterfaceIteratorHelper4<XStreamListener> iter( guard, m_cnt );
170
0
    guard.unlock();
171
0
    while( iter.hasMoreElements() )
172
0
    {
173
0
        try
174
0
        {
175
0
            iter.next()->started( );
176
0
        }
177
0
        catch ( const RuntimeException &e )
178
0
        {
179
0
            SAL_WARN("io.streams","com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners" << e);
180
0
        }
181
0
    }
182
0
}
183
184
void Pump::fireTerminated()
185
0
{
186
0
    std::unique_lock guard( m_aMutex );
187
0
    comphelper::OInterfaceIteratorHelper4<XStreamListener> iter( guard, m_cnt );
188
0
    guard.unlock();
189
0
    while( iter.hasMoreElements() )
190
0
    {
191
0
        try
192
0
        {
193
0
            iter.next()->terminated();
194
0
        }
195
0
        catch ( const RuntimeException &e )
196
0
        {
197
0
            SAL_WARN("io.streams","com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners" << e);
198
0
        }
199
0
    }
200
0
}
201
202
void Pump::close()
203
0
{
204
    // close streams and release references
205
0
    Reference< XInputStream > rInput;
206
0
    Reference< XOutputStream > rOutput;
207
0
    {
208
0
        std::unique_lock guard( m_aMutex );
209
0
        rInput = m_xInput;
210
0
        m_xInput.clear();
211
212
0
        rOutput = m_xOutput;
213
0
        m_xOutput.clear();
214
0
        m_xSucc.clear();
215
0
        m_xPred.clear();
216
0
    }
217
0
    if( rInput.is() )
218
0
    {
219
0
        try
220
0
        {
221
0
            rInput->closeInput();
222
0
        }
223
0
        catch( Exception & )
224
0
        {
225
            // go down calm
226
0
        }
227
0
    }
228
0
    if( rOutput.is() )
229
0
    {
230
0
        try
231
0
        {
232
0
            rOutput->closeOutput();
233
0
        }
234
0
        catch( Exception & )
235
0
        {
236
            // go down calm
237
0
        }
238
0
    }
239
0
}
240
241
void Pump::joinWithThread()
242
0
{
243
0
    std::unique_lock guard( m_aMutex );
244
    // wait for the worker to die
245
0
    if( m_aThread )
246
0
        osl_joinWithThread( m_aThread );
247
0
}
248
249
void Pump::static_run( void* pObject )
250
0
{
251
0
    osl_setThreadName("io_stm::Pump::run()");
252
0
    static_cast<Pump*>(pObject)->run();
253
0
    static_cast<Pump*>(pObject)->release();
254
0
}
255
256
void Pump::run()
257
0
{
258
0
    try
259
0
    {
260
0
        fireStarted();
261
0
        try
262
0
        {
263
0
            Reference< XInputStream > rInput;
264
0
            Reference< XOutputStream > rOutput;
265
0
            {
266
0
                std::unique_lock aGuard( m_aMutex );
267
0
                rInput = m_xInput;
268
0
                rOutput = m_xOutput;
269
0
            }
270
271
0
            if( ! rInput.is() )
272
0
            {
273
0
                throw NotConnectedException( u"no input stream set"_ustr, getXWeak() );
274
0
            }
275
0
            Sequence< sal_Int8 > aData;
276
0
            while( rInput->readSomeBytes( aData, 65536 ) )
277
0
            {
278
0
                if( ! rOutput.is() )
279
0
                {
280
0
                    throw NotConnectedException( u"no output stream set"_ustr, getXWeak() );
281
0
                }
282
0
                rOutput->writeBytes( aData );
283
0
                osl_yieldThread();
284
0
            }
285
0
        }
286
0
        catch ( const IOException & e )
287
0
        {
288
0
            fireError( Any( e ) );
289
0
        }
290
0
        catch ( const RuntimeException & e )
291
0
        {
292
0
            fireError( Any( e ) );
293
0
        }
294
0
        catch ( const Exception & e )
295
0
        {
296
0
            fireError( Any( e ) );
297
0
        }
298
299
0
        close();
300
0
        fireClose();
301
0
    }
302
0
    catch ( const css::uno::Exception &e )
303
0
    {
304
        // we are the last on the stack.
305
        // this is to avoid crashing the program, when e.g. a bridge crashes
306
0
        SAL_WARN("io.streams","com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners" << e);
307
0
    }
308
0
}
309
310
311
/*
312
 * XConnectable
313
 */
314
315
void Pump::setPredecessor( const Reference< XConnectable >& xPred )
316
0
{
317
0
    std::unique_lock aGuard( m_aMutex );
318
0
    m_xPred = xPred;
319
0
}
320
321
322
Reference< XConnectable > Pump::getPredecessor()
323
0
{
324
0
    std::unique_lock aGuard( m_aMutex );
325
0
    return m_xPred;
326
0
}
327
328
329
void Pump::setSuccessor( const Reference< XConnectable >& xSucc )
330
0
{
331
0
    std::unique_lock aGuard( m_aMutex );
332
0
    m_xSucc = xSucc;
333
0
}
334
335
336
Reference< XConnectable > Pump::getSuccessor()
337
0
{
338
0
    std::unique_lock aGuard( m_aMutex );
339
0
    return m_xSucc;
340
0
}
341
342
343
/*
344
 * XActiveDataControl
345
 */
346
347
void Pump::addListener( const Reference< XStreamListener >& xListener )
348
0
{
349
0
    std::unique_lock aGuard( m_aMutex );
350
0
    m_cnt.addInterface( aGuard, xListener );
351
0
}
352
353
354
void Pump::removeListener( const Reference< XStreamListener >& xListener )
355
0
{
356
0
    std::unique_lock aGuard( m_aMutex );
357
0
    m_cnt.removeInterface( aGuard, xListener );
358
0
}
359
360
361
void Pump::start()
362
0
{
363
0
    std::unique_lock aGuard( m_aMutex );
364
0
    m_aThread = osl_createSuspendedThread(Pump::static_run,this);
365
0
    if( !m_aThread )
366
0
    {
367
0
        throw RuntimeException(
368
0
            u"Pump::start Couldn't create worker thread"_ustr,
369
0
            *this);
370
0
    }
371
372
    // will be released by OPump::static_run
373
0
    acquire();
374
0
    osl_resumeThread( m_aThread );
375
376
0
}
377
378
void Pump::terminate()
379
0
{
380
0
    close();
381
382
0
    joinWithThread();
383
384
0
    fireTerminated();
385
0
    fireClose();
386
0
}
387
388
/*
389
 * XActiveDataSink
390
 */
391
392
void Pump::setInputStream( const Reference< XInputStream >& xStream )
393
0
{
394
0
    std::unique_lock aGuard( m_aMutex );
395
0
    m_xInput = xStream;
396
0
    Reference< XConnectable > xConnect( xStream, UNO_QUERY );
397
0
    if( xConnect.is() )
398
0
        xConnect->setSuccessor( this );
399
    // data transfer starts in XActiveDataControl::start
400
0
}
401
402
403
Reference< XInputStream > Pump::getInputStream()
404
0
{
405
0
    std::unique_lock aGuard( m_aMutex );
406
0
    return m_xInput;
407
0
}
408
409
410
/*
411
 * XActiveDataSource
412
 */
413
414
void Pump::setOutputStream( const Reference< XOutputStream >& xOut )
415
0
{
416
0
    std::unique_lock aGuard( m_aMutex );
417
0
    m_xOutput = xOut;
418
0
    Reference< XConnectable > xConnect( xOut, UNO_QUERY );
419
0
    if( xConnect.is() )
420
0
        xConnect->setPredecessor( this );
421
    // data transfer starts in XActiveDataControl::start
422
0
}
423
424
Reference< XOutputStream > Pump::getOutputStream()
425
0
{
426
0
    std::unique_lock aGuard( m_aMutex );
427
0
    return m_xOutput;
428
0
}
429
430
// XServiceInfo
431
OUString Pump::getImplementationName()
432
0
{
433
0
    return u"com.sun.star.comp.io.Pump"_ustr;
434
0
}
435
436
// XServiceInfo
437
sal_Bool Pump::supportsService(const OUString& ServiceName)
438
0
{
439
0
    return cppu::supportsService(this, ServiceName);
440
0
}
441
442
// XServiceInfo
443
Sequence< OUString > Pump::getSupportedServiceNames()
444
0
{
445
0
    return { u"com.sun.star.io.Pump"_ustr };
446
0
}
447
448
}
449
450
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
451
io_Pump_get_implementation(
452
    css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
453
0
{
454
0
    return cppu::acquire(new io_stm::Pump());
455
0
}
456
457
458
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */