LibreOffice Module ucb (master) 1
SerfLockStore.cxx
Go to the documentation of this file.
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 <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>
26
27#include <com/sun/star/ucb/LockScope.hpp>
28#include <thread>
29
30#include "CurlSession.hxx"
31#include "SerfLockStore.hxx"
32
33using namespace http_dav_ucp;
34
35namespace http_dav_ucp {
36
38{
41
42public:
43
44 explicit TickerThread( SerfLockStore & rLockStore )
45 : Thread( "WebDavTickerThread" ), m_bFinish( false ),
46 m_rLockStore( rLockStore ) {}
47
48 void finish() { m_bFinish = true; }
49
50private:
51
52 virtual void execute();
53};
54
55} // namespace http_dav_ucp
56
57
58void TickerThread::execute()
59{
60 osl_setThreadName("http_dav_ucp::TickerThread");
61
62 SAL_INFO("ucb.ucp.webdav", "TickerThread: start." );
63
64 // we have to go through the loop more often to be able to finish ~quickly
65 const int nNth = 25;
66
67 int nCount = nNth;
68 while ( !m_bFinish )
69 {
70 if ( nCount-- <= 0 )
71 {
73 nCount = nNth;
74 }
75
76 std::this_thread::sleep_for( std::chrono::milliseconds(1000/25) );
77 }
78
79 SAL_INFO("ucb.ucp.webdav", "TickerThread: stop." );
80}
81
82
84{
85}
86
87
89{
90 std::unique_lock aGuard(m_aMutex);
91 stopTicker(aGuard);
92 aGuard.lock(); // actually no threads should even try to access members now
93
94 // release active locks, if any.
95 SAL_WARN_IF( !m_aLockInfoMap.empty(), "ucb.ucp.webdav",
96 "SerfLockStore::~SerfLockStore - Releasing active locks!" );
97
98 for ( auto& rLockInfo : m_aLockInfoMap )
99 {
100 rLockInfo.second.m_xSession->NonInteractive_UNLOCK(rLockInfo.first);
101 }
102}
103
105{
106 std::unique_lock aGuard( m_aMutex );
107
108 if ( !m_pTickerThread.is() )
109 {
110 m_pTickerThread = new TickerThread( *this );
111 m_pTickerThread->launch();
112 }
113}
114
115
116void SerfLockStore::stopTicker(std::unique_lock<std::mutex> & rGuard)
117{
118 rtl::Reference<TickerThread> pTickerThread;
119
120 if (m_pTickerThread.is())
121 {
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();
126 }
127
128 rGuard.unlock();
129
130 if (pTickerThread.is() && pTickerThread->getIdentifier() != osl::Thread::getCurrentIdentifier())
131 {
132 pTickerThread->join(); // without m_aMutex locked (to prevent deadlock)
133 }
134}
135
136OUString const*
137SerfLockStore::getLockTokenForURI(OUString const& rURI, css::ucb::Lock const*const pLock)
138{
139 assert(rURI.startsWith("http://") || rURI.startsWith("https://"));
140
141 std::unique_lock aGuard( m_aMutex );
142
143 auto const it(m_aLockInfoMap.find(rURI));
144
145 if (it == m_aLockInfoMap.end())
146 {
147 return nullptr;
148 }
149 if (!pLock) // any lock will do
150 {
151 return &it->second.m_sToken;
152 }
153 // 0: EXCLUSIVE 1: SHARED
154 if (it->second.m_Lock.Scope == ucb::LockScope_SHARED && pLock->Scope == ucb::LockScope_EXCLUSIVE)
155 {
156 return nullptr;
157 }
158 assert(it->second.m_Lock.Type == pLock->Type); // only WRITE possible
159 if (it->second.m_Lock.Depth < pLock->Depth)
160 {
161 return nullptr;
162 }
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;
169}
170
171void SerfLockStore::addLock( const OUString& rURI,
172 ucb::Lock const& rLock,
173 const OUString& sToken,
174 rtl::Reference<CurlSession> const & xSession,
175 sal_Int32 nLastChanceToSendRefreshRequest )
176{
177 assert(rURI.startsWith("http://") || rURI.startsWith("https://"));
178 {
179 std::unique_lock aGuard( m_aMutex );
180
181 m_aLockInfoMap[ rURI ]
182 = LockInfo(sToken, rLock, xSession, nLastChanceToSendRefreshRequest);
183 }
184
185 startTicker();
186}
187
188
189void SerfLockStore::removeLock(const OUString& rURI)
190{
191 std::unique_lock aGuard( m_aMutex );
192
193 removeLockImpl(aGuard, rURI);
194}
195
196void SerfLockStore::removeLockImpl(std::unique_lock<std::mutex> & rGuard, const OUString& rURI)
197{
198 assert(rURI.startsWith("http://") || rURI.startsWith("https://"));
199
200 m_aLockInfoMap.erase(rURI);
201
202 if ( m_aLockInfoMap.empty() )
203 {
204 stopTicker(rGuard);
205 }
206}
207
209{
210 std::unique_lock aGuard( m_aMutex );
211
212 ::std::vector<OUString> authFailedLocks;
213
214 for ( auto& rLockInfo : m_aLockInfoMap )
215 {
216 LockInfo & rInfo = rLockInfo.second;
217 if ( rInfo.m_nLastChanceToSendRefreshRequest != -1 )
218 {
219 // 30 seconds or less remaining until lock expires?
220 TimeValue t1;
221 osl_getSystemTime( &t1 );
223 <= sal_Int32( t1.Seconds ) )
224 {
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))
232 {
234 = nlastChanceToSendRefreshRequest;
235 }
236 else
237 {
238 if (isAuthFailed)
239 {
240 authFailedLocks.push_back(rLockInfo.first);
241 }
242 // refresh failed. stop auto-refresh.
244 }
245 }
246 }
247 }
248
249 for (auto const& rLock : authFailedLocks)
250 {
251 removeLockImpl(aGuard, rLock);
252 }
253}
254
255/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void removeLockImpl(std::unique_lock< std::mutex > &rGuard, const OUString &rURI)
rtl::Reference< TickerThread > m_pTickerThread
OUString const * getLockTokenForURI(OUString const &rURI, css::ucb::Lock const *pLock)
void removeLock(const OUString &rURI)
void stopTicker(std::unique_lock< std::mutex > &rGuard)
void addLock(const OUString &rURI, css::ucb::Lock const &rLock, const OUString &sToken, rtl::Reference< CurlSession > const &xSession, sal_Int32 nLastChanceToSendRefreshRequest)
SerfLockStore & m_rLockStore
TickerThread(SerfLockStore &rLockStore)
Thread(char const *name)
int nCount
#define SAL_WARN_IF(condition, area, stream)
#define SAL_INFO(area, stream)
rtl::Reference< CurlSession > m_xSession
sal_Int32 m_nLastChanceToSendRefreshRequest