Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / ucb / source / ucp / webdav-curl / SerfLockStore.cxx
blob6d7b89e9e687bdb68af891ce358899a0f06e285e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <chrono>
21 #include <rtl/ustring.hxx>
22 #include <sal/log.hxx>
23 #include <osl/time.h>
24 #include <osl/thread.hxx>
25 #include <salhelper/thread.hxx>
27 #include <com/sun/star/ucb/LockScope.hpp>
28 #include <thread>
30 #include "CurlSession.hxx"
31 #include "SerfLockStore.hxx"
33 using namespace http_dav_ucp;
35 namespace http_dav_ucp {
37 class TickerThread : public salhelper::Thread
39 bool m_bFinish;
40 SerfLockStore & m_rLockStore;
42 public:
44 explicit TickerThread( SerfLockStore & rLockStore )
45 : Thread( "WebDavTickerThread" ), m_bFinish( false ),
46 m_rLockStore( rLockStore ) {}
48 void finish() { m_bFinish = true; }
50 private:
52 virtual void execute();
55 } // namespace http_dav_ucp
58 void TickerThread::execute()
60 osl_setThreadName("http_dav_ucp::TickerThread");
62 SAL_INFO("ucb.ucp.webdav", "TickerThread: start." );
64 // we have to go through the loop more often to be able to finish ~quickly
65 const int nNth = 25;
67 int nCount = nNth;
68 while ( !m_bFinish )
70 if ( nCount-- <= 0 )
72 m_rLockStore.refreshLocks();
73 nCount = nNth;
76 std::this_thread::sleep_for( std::chrono::milliseconds(1000/25) );
79 SAL_INFO("ucb.ucp.webdav", "TickerThread: stop." );
83 SerfLockStore::SerfLockStore()
88 SerfLockStore::~SerfLockStore()
90 std::unique_lock aGuard(m_aMutex);
91 stopTicker(aGuard);
92 aGuard.lock(); // actually no threads should even try to access members now
94 // release active locks, if any.
95 SAL_WARN_IF( !m_aLockInfoMap.empty(), "ucb.ucp.webdav",
96 "SerfLockStore::~SerfLockStore - Releasing active locks!" );
98 for ( auto& rLockInfo : m_aLockInfoMap )
100 rLockInfo.second.m_xSession->NonInteractive_UNLOCK(rLockInfo.first);
104 void SerfLockStore::startTicker()
106 std::unique_lock aGuard( m_aMutex );
108 if ( !m_pTickerThread.is() )
110 m_pTickerThread = new TickerThread( *this );
111 m_pTickerThread->launch();
116 void SerfLockStore::stopTicker(std::unique_lock<std::mutex> & rGuard)
118 rtl::Reference<TickerThread> pTickerThread;
120 if (m_pTickerThread.is())
122 m_pTickerThread->finish(); // needs mutex
123 // the TickerThread may run refreshLocks() at most once after this
124 pTickerThread = m_pTickerThread;
125 m_pTickerThread.clear();
128 rGuard.unlock();
130 if (pTickerThread.is() && pTickerThread->getIdentifier() != osl::Thread::getCurrentIdentifier())
132 pTickerThread->join(); // without m_aMutex locked (to prevent deadlock)
136 OUString const*
137 SerfLockStore::getLockTokenForURI(OUString const& rURI, css::ucb::Lock const*const pLock)
139 assert(rURI.startsWith("http://") || rURI.startsWith("https://"));
141 std::unique_lock aGuard( m_aMutex );
143 auto const it(m_aLockInfoMap.find(rURI));
145 if (it == m_aLockInfoMap.end())
147 return nullptr;
149 if (!pLock) // any lock will do
151 return &it->second.m_sToken;
153 // 0: EXCLUSIVE 1: SHARED
154 if (it->second.m_Lock.Scope == ucb::LockScope_SHARED && pLock->Scope == ucb::LockScope_EXCLUSIVE)
156 return nullptr;
158 assert(it->second.m_Lock.Type == pLock->Type); // only WRITE possible
159 if (it->second.m_Lock.Depth < pLock->Depth)
161 return nullptr;
163 // Only own locks are expected in the lock store, but depending on the
164 // server it->second.m_Lock.Owner may contain the string this UCP passed in
165 // the LOCK request, or a user identifier generated by the server (happens
166 // with Sharepoint), so just ignore it here.
167 // ignore Timeout ?
168 return &it->second.m_sToken;
171 void SerfLockStore::addLock( const OUString& rURI,
172 ucb::Lock const& rLock,
173 const OUString& sToken,
174 rtl::Reference<CurlSession> const & xSession,
175 sal_Int32 nLastChanceToSendRefreshRequest )
177 assert(rURI.startsWith("http://") || rURI.startsWith("https://"));
179 std::unique_lock aGuard( m_aMutex );
181 m_aLockInfoMap[ rURI ]
182 = LockInfo(sToken, rLock, xSession, nLastChanceToSendRefreshRequest);
185 startTicker();
189 void SerfLockStore::removeLock(const OUString& rURI)
191 std::unique_lock aGuard( m_aMutex );
193 removeLockImpl(aGuard, rURI);
196 void SerfLockStore::removeLockImpl(std::unique_lock<std::mutex> & rGuard, const OUString& rURI)
198 assert(rURI.startsWith("http://") || rURI.startsWith("https://"));
200 m_aLockInfoMap.erase(rURI);
202 if ( m_aLockInfoMap.empty() )
204 stopTicker(rGuard);
208 void SerfLockStore::refreshLocks()
210 std::unique_lock aGuard( m_aMutex );
212 ::std::vector<OUString> authFailedLocks;
214 for ( auto& rLockInfo : m_aLockInfoMap )
216 LockInfo & rInfo = rLockInfo.second;
217 if ( rInfo.m_nLastChanceToSendRefreshRequest != -1 )
219 // 30 seconds or less remaining until lock expires?
220 TimeValue t1;
221 osl_getSystemTime( &t1 );
222 if ( rInfo.m_nLastChanceToSendRefreshRequest - 30
223 <= sal_Int32( t1.Seconds ) )
225 // refresh the lock.
226 sal_Int32 nlastChanceToSendRefreshRequest = -1;
227 bool isAuthFailed(false);
228 if (rInfo.m_xSession->NonInteractive_LOCK(
229 rLockInfo.first, rLockInfo.second.m_sToken,
230 nlastChanceToSendRefreshRequest,
231 isAuthFailed))
233 rInfo.m_nLastChanceToSendRefreshRequest
234 = nlastChanceToSendRefreshRequest;
236 else
238 if (isAuthFailed)
240 authFailedLocks.push_back(rLockInfo.first);
242 // refresh failed. stop auto-refresh.
243 rInfo.m_nLastChanceToSendRefreshRequest = -1;
249 for (auto const& rLock : authFailedLocks)
251 removeLockImpl(aGuard, rLock);
255 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */