1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <tools/time.hxx>
22 #include <vcl/scheduler.hxx>
23 #include <saltimer.hxx>
24 #include <salinst.hxx>
27 const sal_uInt64 MaximumTimeoutMs
= 1000 * 60; // 1 minute
30 void ImplSchedulerData::Invoke()
34 assert(!mbInScheduler
);
35 if (mbDelete
|| mbInScheduler
)
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();
47 mbInScheduler
= false;
50 void Scheduler::ImplDeInitScheduler()
52 ImplSVData
* pSVData
= ImplGetSVData();
53 ImplSchedulerData
* pSchedulerData
= pSVData
->mpFirstSchedulerData
;
54 if (pSVData
->mpSalTimer
)
56 pSVData
->mpSalTimer
->Stop();
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;
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
101 if (!pSVData
->mpSalTimer
)
103 pSVData
->mnTimerPeriod
= MaximumTimeoutMs
;
104 pSVData
->mpSalTimer
= pSVData
->mpDefInst
->CreateSalTimer();
105 pSVData
->mpSalTimer
->SetCallback(Scheduler::CallbackTaskScheduling
);
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
)
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())
143 pMostUrgent
= pSchedulerData
;
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
;
156 SAL_INFO("vcl.schedule", "Invoke task " << pMostUrgent
->GetDebugName());
158 pMostUrgent
->mnUpdateTime
= nTime
;
159 pMostUrgent
->Invoke();
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
;
201 pSVData
->mpFirstSchedulerData
= pSchedulerData
->mpNext
;
202 if ( pSchedulerData
->mpTask
)
203 pSchedulerData
->mpTask
->mpSchedulerData
= nullptr;
204 pNext
= pSchedulerData
->mpNext
;
205 delete pSchedulerData
;
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");
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");
251 Scheduler::ImplStartTimer(nMinPeriod
, true);
252 SAL_INFO("vcl.schedule", "Calculated minimum timeout as " << nMinPeriod
<< " and " <<
253 (bHasActiveIdles
? "has active idles" : "no idles"));
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;
278 ImplSVData
*const pSVData
= ImplGetSVData();
279 if (pSVData
->mbDeInit
)
284 DBG_TESTSOLARMUTEX();
289 if ( !mpSchedulerData
)
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
;
302 pData
= pData
->mpNext
;
304 mpSchedulerData
->mpNext
= nullptr;
306 pPrev
->mpNext
= mpSchedulerData
;
308 pSVData
->mpFirstSchedulerData
= mpSchedulerData
;
310 mpSchedulerData
->mbDelete
= false;
311 mpSchedulerData
->mnUpdateTime
= tools::Time::GetSystemTicks();
318 if ( mpSchedulerData
)
319 mpSchedulerData
->mbDelete
= true;
322 Task
& Task::operator=( const Task
& rTask
)
328 mePriority
= rTask
.mePriority
;
330 if ( rTask
.IsActive() )
336 Task::Task( const sal_Char
*pDebugName
)
337 : mpSchedulerData( nullptr )
338 , mpDebugName( pDebugName
)
339 , mePriority( TaskPriority::HIGH
)
344 Task::Task( const Task
& rTask
)
345 : mpSchedulerData( nullptr )
346 , mpDebugName( rTask
.mpDebugName
)
347 , mePriority( rTask
.mePriority
)
350 if ( rTask
.IsActive() )
356 if ( mpSchedulerData
)
358 mpSchedulerData
->mbDelete
= true;
359 mpSchedulerData
->mpTask
= nullptr;
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */