Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / app / scheduler.cxx
bloba3de686aa2a7d5208a0f71c34eab318ab4c2f7ea
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 <svdata.hxx>
21 #include <tools/time.hxx>
22 #include <vcl/scheduler.hxx>
23 #include <saltimer.hxx>
24 #include <salinst.hxx>
26 namespace {
27 const sal_uInt64 MaximumTimeoutMs = 1000 * 60; // 1 minute
30 void ImplSchedulerData::Invoke()
32 DBG_TESTSOLARMUTEX();
34 assert(!mbInScheduler);
35 if (mbDelete || mbInScheduler )
36 return;
38 // prepare Scheduler Object for deletion after handling
39 mpTask->SetDeletionFlags();
41 // tdf#92036 Reset the period to avoid re-firing immediately.
42 mpTask->mpSchedulerData->mnUpdateTime = tools::Time::GetSystemTicks();
44 // invoke it
45 mbInScheduler = true;
46 mpTask->Invoke();
47 mbInScheduler = false;
50 void Scheduler::ImplDeInitScheduler()
52 ImplSVData* pSVData = ImplGetSVData();
53 ImplSchedulerData* pSchedulerData = pSVData->mpFirstSchedulerData;
54 if (pSVData->mpSalTimer)
56 pSVData->mpSalTimer->Stop();
59 if ( pSchedulerData )
63 ImplSchedulerData* pTempSchedulerData = pSchedulerData;
64 if ( pSchedulerData->mpTask )
66 pSchedulerData->mpTask->mbActive = false;
67 pSchedulerData->mpTask->mpSchedulerData = nullptr;
69 pSchedulerData = pSchedulerData->mpNext;
70 delete pTempSchedulerData;
72 while ( pSchedulerData );
74 pSVData->mpFirstSchedulerData = nullptr;
75 pSVData->mnTimerPeriod = 0;
78 delete pSVData->mpSalTimer;
79 pSVData->mpSalTimer = nullptr;
82 /**
83 * Start a new timer if we need to for nMS duration.
85 * if this is longer than the existing duration we're
86 * waiting for, do nothing - unless bForce - which means
87 * to reset the minimum period; used by the scheduled itself.
89 void Scheduler::ImplStartTimer(sal_uInt64 nMS, bool bForce)
91 ImplSVData* pSVData = ImplGetSVData();
92 if (pSVData->mbDeInit)
94 // do not start new timers during shutdown - if that happens after
95 // ImplSalStopTimer() on WNT the timer queue is restarted and never ends
96 return;
99 DBG_TESTSOLARMUTEX();
101 if (!pSVData->mpSalTimer)
103 pSVData->mnTimerPeriod = MaximumTimeoutMs;
104 pSVData->mpSalTimer = pSVData->mpDefInst->CreateSalTimer();
105 pSVData->mpSalTimer->SetCallback(Scheduler::CallbackTaskScheduling);
108 if ( !nMS )
109 nMS = 1;
111 // Only if smaller timeout, to avoid skipping.
112 if (bForce || nMS < pSVData->mnTimerPeriod)
114 pSVData->mnTimerPeriod = nMS;
115 pSVData->mpSalTimer->Start(nMS);
119 void Scheduler::CallbackTaskScheduling( bool bIdle )
121 // this function is for the saltimer callback
122 Scheduler::ProcessTaskScheduling( bIdle );
125 bool Scheduler::ProcessTaskScheduling( bool bIdle )
127 ImplSVData *pSVData = ImplGetSVData();
128 if ( pSVData->mbDeInit )
129 return false;
130 ImplSchedulerData *pMostUrgent = nullptr;
131 sal_uInt64 nTime = tools::Time::GetSystemTicks();
133 DBG_TESTSOLARMUTEX();
135 for ( ImplSchedulerData *pSchedulerData = pSVData->mpFirstSchedulerData;
136 pSchedulerData; pSchedulerData = pSchedulerData->mpNext )
138 if ( !pSchedulerData->mpTask || pSchedulerData->mbDelete || pSchedulerData->mbInScheduler ||
139 !pSchedulerData->mpTask->ReadyForSchedule( bIdle, nTime ) ||
140 !pSchedulerData->mpTask->IsActive())
141 continue;
142 if (!pMostUrgent)
143 pMostUrgent = pSchedulerData;
144 else
146 // Find the highest priority.
147 // If the priority of the current task is higher (numerical value is lower) than
148 // the priority of the most urgent, the current task gets the new most urgent.
149 if ( pSchedulerData->mpTask->GetPriority() < pMostUrgent->mpTask->GetPriority() )
150 pMostUrgent = pSchedulerData;
154 if ( pMostUrgent )
156 SAL_INFO("vcl.schedule", "Invoke task " << pMostUrgent->GetDebugName());
158 pMostUrgent->mnUpdateTime = nTime;
159 pMostUrgent->Invoke();
160 return true;
162 else
163 return false;
166 static bool g_bDeterministicMode = false;
168 void Scheduler::SetDeterministicMode(bool bDeterministic)
170 g_bDeterministicMode = bDeterministic;
173 bool Scheduler::GetDeterministicMode()
175 return g_bDeterministicMode;
178 sal_uInt64 Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles )
180 ImplSchedulerData* pSchedulerData = nullptr;
181 ImplSchedulerData* pPrevSchedulerData = nullptr;
182 ImplSVData* pSVData = ImplGetSVData();
183 sal_uInt64 nTime = tools::Time::GetSystemTicks();
184 sal_uInt64 nMinPeriod = MaximumTimeoutMs;
186 DBG_TESTSOLARMUTEX();
188 SAL_INFO("vcl.schedule", "Calculating minimum timeout:");
189 pSchedulerData = pSVData->mpFirstSchedulerData;
190 while ( pSchedulerData )
192 ImplSchedulerData *pNext = pSchedulerData->mpNext;
194 // Should Task be released from scheduling?
195 if ( !pSchedulerData->mbInScheduler &&
196 pSchedulerData->mbDelete )
198 if ( pPrevSchedulerData )
199 pPrevSchedulerData->mpNext = pSchedulerData->mpNext;
200 else
201 pSVData->mpFirstSchedulerData = pSchedulerData->mpNext;
202 if ( pSchedulerData->mpTask )
203 pSchedulerData->mpTask->mpSchedulerData = nullptr;
204 pNext = pSchedulerData->mpNext;
205 delete pSchedulerData;
207 else
209 if (!pSchedulerData->mbInScheduler)
211 if ( !pSchedulerData->mpTask->IsIdle() )
213 sal_uInt64 nOldMinPeriod = nMinPeriod;
214 nMinPeriod = pSchedulerData->mpTask->UpdateMinPeriod(
215 nOldMinPeriod, nTime );
216 SAL_INFO("vcl.schedule", "Have active timer '" <<
217 pSchedulerData->GetDebugName() <<
218 "' update min period from " << nOldMinPeriod <<
219 " to " << nMinPeriod);
220 assert( nMinPeriod <= nOldMinPeriod );
221 if ( nMinPeriod > nOldMinPeriod )
223 nMinPeriod = nOldMinPeriod;
224 SAL_WARN("vcl.schedule",
225 "New update min period > old period - using old");
228 else
230 SAL_INFO("vcl.schedule", "Have active idle '" <<
231 pSchedulerData->GetDebugName() << "'");
232 bHasActiveIdles = true;
235 pPrevSchedulerData = pSchedulerData;
237 pSchedulerData = pNext;
240 // delete clock if no more timers available,
241 if ( !pSVData->mpFirstSchedulerData )
243 if ( pSVData->mpSalTimer )
244 pSVData->mpSalTimer->Stop();
245 nMinPeriod = MaximumTimeoutMs;
246 pSVData->mnTimerPeriod = nMinPeriod;
247 SAL_INFO("vcl.schedule", "Unusual - no more timers available - stop timer");
249 else
251 Scheduler::ImplStartTimer(nMinPeriod, true);
252 SAL_INFO("vcl.schedule", "Calculated minimum timeout as " << nMinPeriod << " and " <<
253 (bHasActiveIdles ? "has active idles" : "no idles"));
256 return nMinPeriod;
259 const char *ImplSchedulerData::GetDebugName() const
261 return mpTask && mpTask->GetDebugName() ?
262 mpTask->GetDebugName() : "unknown";
265 void Task::StartTimer( sal_uInt64 nMS )
267 Scheduler::ImplStartTimer( nMS, false );
270 void Task::SetDeletionFlags()
272 mpSchedulerData->mbDelete = true;
273 mbActive = false;
276 void Task::Start()
278 ImplSVData *const pSVData = ImplGetSVData();
279 if (pSVData->mbDeInit)
281 return;
284 DBG_TESTSOLARMUTEX();
286 // Mark timer active
287 mbActive = true;
289 if ( !mpSchedulerData )
291 // insert Task
292 mpSchedulerData = new ImplSchedulerData;
293 mpSchedulerData->mpTask = this;
294 mpSchedulerData->mbInScheduler = false;
296 // insert last due to SFX!
297 ImplSchedulerData* pPrev = nullptr;
298 ImplSchedulerData* pData = pSVData->mpFirstSchedulerData;
299 while ( pData )
301 pPrev = pData;
302 pData = pData->mpNext;
304 mpSchedulerData->mpNext = nullptr;
305 if ( pPrev )
306 pPrev->mpNext = mpSchedulerData;
307 else
308 pSVData->mpFirstSchedulerData = mpSchedulerData;
310 mpSchedulerData->mbDelete = false;
311 mpSchedulerData->mnUpdateTime = tools::Time::GetSystemTicks();
314 void Task::Stop()
316 mbActive = false;
318 if ( mpSchedulerData )
319 mpSchedulerData->mbDelete = true;
322 Task& Task::operator=( const Task& rTask )
324 if ( IsActive() )
325 Stop();
327 mbActive = false;
328 mePriority = rTask.mePriority;
330 if ( rTask.IsActive() )
331 Start();
333 return *this;
336 Task::Task( const sal_Char *pDebugName )
337 : mpSchedulerData( nullptr )
338 , mpDebugName( pDebugName )
339 , mePriority( TaskPriority::HIGH )
340 , mbActive( false )
344 Task::Task( const Task& rTask )
345 : mpSchedulerData( nullptr )
346 , mpDebugName( rTask.mpDebugName )
347 , mePriority( rTask.mePriority )
348 , mbActive( false )
350 if ( rTask.IsActive() )
351 Start();
354 Task::~Task()
356 if ( mpSchedulerData )
358 mpSchedulerData->mbDelete = true;
359 mpSchedulerData->mpTask = nullptr;
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */