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>
25 #include <salinst.hxx>
28 const sal_uInt64 MaximumTimeoutMs
= 1000 * 60; // 1 minute
29 void InitSystemTimer(ImplSVData
* pSVData
);
32 void ImplSchedulerData::Invoke()
36 assert(!mbInScheduler
);
37 if (mbDelete
|| mbInScheduler
)
40 // prepare Scheduler Object for deletion after handling
41 mpScheduler
->SetDeletionFlags();
43 // tdf#92036 Reset the period to avoid re-firing immediately.
44 mpScheduler
->mpSchedulerData
->mnUpdateTime
= tools::Time::GetSystemTicks();
48 mpScheduler
->Invoke();
49 mbInScheduler
= false;
52 ImplSchedulerData
*ImplSchedulerData::GetMostImportantTask( bool bTimerOnly
)
54 ImplSVData
* pSVData
= ImplGetSVData();
55 ImplSchedulerData
*pMostUrgent
= nullptr;
57 sal_uInt64 nTimeNow
= tools::Time::GetSystemTicks();
58 for ( ImplSchedulerData
*pSchedulerData
= pSVData
->mpFirstSchedulerData
; pSchedulerData
; pSchedulerData
= pSchedulerData
->mpNext
)
60 if ( !pSchedulerData
->mpScheduler
|| pSchedulerData
->mbDelete
|| pSchedulerData
->mbInScheduler
||
61 !pSchedulerData
->mpScheduler
->ReadyForSchedule( bTimerOnly
, nTimeNow
) ||
62 !pSchedulerData
->mpScheduler
->IsActive())
65 pMostUrgent
= pSchedulerData
;
68 // Find the highest priority.
69 // If the priority of the current task is higher (numerical value is lower) than
70 // the priority of the most urgent, the current task gets the new most urgent.
71 if ( pSchedulerData
->mpScheduler
->GetPriority() < pMostUrgent
->mpScheduler
->GetPriority() )
72 pMostUrgent
= pSchedulerData
;
79 void Scheduler::SetDeletionFlags()
81 mpSchedulerData
->mbDelete
= true;
85 void Scheduler::ImplDeInitScheduler()
87 ImplSVData
* pSVData
= ImplGetSVData();
88 ImplSchedulerData
* pSchedulerData
= pSVData
->mpFirstSchedulerData
;
89 if (pSVData
->mpSalTimer
)
91 pSVData
->mpSalTimer
->Stop();
98 ImplSchedulerData
* pTempSchedulerData
= pSchedulerData
;
99 if ( pSchedulerData
->mpScheduler
)
101 pSchedulerData
->mpScheduler
->mbActive
= false;
102 pSchedulerData
->mpScheduler
->mpSchedulerData
= nullptr;
104 pSchedulerData
= pSchedulerData
->mpNext
;
105 delete pTempSchedulerData
;
107 while ( pSchedulerData
);
109 pSVData
->mpFirstSchedulerData
= nullptr;
110 pSVData
->mnTimerPeriod
= 0;
113 delete pSVData
->mpSalTimer
;
114 pSVData
->mpSalTimer
= nullptr;
118 * Start a new timer if we need to for nMS duration.
120 * if this is longer than the existing duration we're
121 * waiting for, do nothing - unless bForce - which means
122 * to reset the minimum period; used by the scheduled itself.
124 void Scheduler::ImplStartTimer(sal_uInt64 nMS
, bool bForce
)
126 ImplSVData
* pSVData
= ImplGetSVData();
127 if (pSVData
->mbDeInit
)
129 // do not start new timers during shutdown - if that happens after
130 // ImplSalStopTimer() on WNT the timer queue is restarted and never ends
134 DBG_TESTSOLARMUTEX();
136 InitSystemTimer(pSVData
);
141 // Only if smaller timeout, to avoid skipping.
142 if (bForce
|| nMS
< pSVData
->mnTimerPeriod
)
144 pSVData
->mnTimerPeriod
= nMS
;
145 pSVData
->mpSalTimer
->Start(nMS
);
152 * Initialize the platform specific timer on which all the
153 * platform independent timers are built
155 void InitSystemTimer(ImplSVData
* pSVData
)
157 assert(pSVData
!= nullptr);
158 if (!pSVData
->mpSalTimer
)
160 pSVData
->mnTimerPeriod
= MaximumTimeoutMs
;
161 pSVData
->mpSalTimer
= pSVData
->mpDefInst
->CreateSalTimer();
162 pSVData
->mpSalTimer
->SetCallback(Scheduler::CallbackTaskScheduling
);
168 void Scheduler::CallbackTaskScheduling(bool)
170 // this function is for the saltimer callback
171 Scheduler::ProcessTaskScheduling( false );
174 bool Scheduler::ProcessTaskScheduling( bool bTimerOnly
)
176 ImplSchedulerData
* pSchedulerData
;
178 DBG_TESTSOLARMUTEX();
180 if ((pSchedulerData
= ImplSchedulerData::GetMostImportantTask(bTimerOnly
)))
182 SAL_INFO("vcl.schedule", "Invoke task " << pSchedulerData
->GetDebugName());
184 pSchedulerData
->mnUpdateTime
= tools::Time::GetSystemTicks();
185 pSchedulerData
->Invoke();
192 static bool g_bDeterministicMode
= false;
194 void Scheduler::SetDeterministicMode(bool bDeterministic
)
196 g_bDeterministicMode
= bDeterministic
;
199 bool Scheduler::GetDeterministicMode()
201 return g_bDeterministicMode
;
204 sal_uInt64
Scheduler::CalculateMinimumTimeout( bool &bHasActiveIdles
)
206 // process all pending Tasks
207 // if bTimer True, only handle timer
208 ImplSchedulerData
* pSchedulerData
= nullptr;
209 ImplSchedulerData
* pPrevSchedulerData
= nullptr;
210 ImplSVData
* pSVData
= ImplGetSVData();
211 sal_uInt64 nTime
= tools::Time::GetSystemTicks();
212 sal_uInt64 nMinPeriod
= MaximumTimeoutMs
;
214 DBG_TESTSOLARMUTEX();
216 SAL_INFO("vcl.schedule", "Calculating minimum timeout:");
217 pSchedulerData
= pSVData
->mpFirstSchedulerData
;
218 while ( pSchedulerData
)
220 ImplSchedulerData
*pNext
= pSchedulerData
->mpNext
;
222 // Should Task be released from scheduling?
223 if ( !pSchedulerData
->mbInScheduler
&&
224 pSchedulerData
->mbDelete
)
226 if ( pPrevSchedulerData
)
227 pPrevSchedulerData
->mpNext
= pSchedulerData
->mpNext
;
229 pSVData
->mpFirstSchedulerData
= pSchedulerData
->mpNext
;
230 if ( pSchedulerData
->mpScheduler
)
231 pSchedulerData
->mpScheduler
->mpSchedulerData
= nullptr;
232 pNext
= pSchedulerData
->mpNext
;
233 delete pSchedulerData
;
237 if (!pSchedulerData
->mbInScheduler
)
239 if ( !pSchedulerData
->mpScheduler
->IsIdle() )
241 sal_uInt64 nOldMinPeriod
= nMinPeriod
;
242 nMinPeriod
= pSchedulerData
->mpScheduler
->UpdateMinPeriod(
243 nOldMinPeriod
, nTime
);
244 SAL_INFO("vcl.schedule", "Have active timer " <<
245 pSchedulerData
->GetDebugName() <<
246 "update min period from " << nOldMinPeriod
<<
247 " to " << nMinPeriod
);
251 SAL_INFO("vcl.schedule", "Have active idle " <<
252 pSchedulerData
->GetDebugName());
253 bHasActiveIdles
= true;
256 pPrevSchedulerData
= pSchedulerData
;
258 pSchedulerData
= pNext
;
261 // delete clock if no more timers available,
262 if ( !pSVData
->mpFirstSchedulerData
)
264 if ( pSVData
->mpSalTimer
)
265 pSVData
->mpSalTimer
->Stop();
266 nMinPeriod
= MaximumTimeoutMs
;
267 pSVData
->mnTimerPeriod
= nMinPeriod
;
268 SAL_INFO("vcl.schedule", "Unusual - no more timers available - stop timer");
272 Scheduler::ImplStartTimer(nMinPeriod
, true);
273 SAL_INFO("vcl.schedule", "Calculated minimum timeout as " << nMinPeriod
<< " and " <<
274 (bHasActiveIdles
? "has active idles" : "no idles"));
280 void Scheduler::Start()
282 ImplSVData
*const pSVData
= ImplGetSVData();
283 if (pSVData
->mbDeInit
)
288 DBG_TESTSOLARMUTEX();
293 if ( !mpSchedulerData
)
296 mpSchedulerData
= new ImplSchedulerData
;
297 mpSchedulerData
->mpScheduler
= this;
298 mpSchedulerData
->mbInScheduler
= false;
300 // insert last due to SFX!
301 ImplSchedulerData
* pPrev
= nullptr;
302 ImplSchedulerData
* pData
= pSVData
->mpFirstSchedulerData
;
306 pData
= pData
->mpNext
;
308 mpSchedulerData
->mpNext
= nullptr;
310 pPrev
->mpNext
= mpSchedulerData
;
312 pSVData
->mpFirstSchedulerData
= mpSchedulerData
;
314 mpSchedulerData
->mbDelete
= false;
315 mpSchedulerData
->mnUpdateTime
= tools::Time::GetSystemTicks();
318 void Scheduler::Stop()
322 if ( mpSchedulerData
)
323 mpSchedulerData
->mbDelete
= true;
326 Scheduler
& Scheduler::operator=( const Scheduler
& rScheduler
)
332 mePriority
= rScheduler
.mePriority
;
334 if ( rScheduler
.IsActive() )
340 Scheduler::Scheduler(const sal_Char
*pDebugName
):
341 mpSchedulerData(nullptr),
342 mpDebugName(pDebugName
),
343 mePriority(SchedulerPriority::HIGH
),
348 Scheduler::Scheduler( const Scheduler
& rScheduler
):
349 mpSchedulerData(nullptr),
350 mpDebugName(rScheduler
.mpDebugName
),
351 mePriority(rScheduler
.mePriority
),
354 if ( rScheduler
.IsActive() )
358 Scheduler::~Scheduler()
360 if ( mpSchedulerData
)
362 mpSchedulerData
->mbDelete
= true;
363 mpSchedulerData
->mpScheduler
= nullptr;
367 const char *ImplSchedulerData::GetDebugName() const
369 return mpScheduler
&& mpScheduler
->GetDebugName() ?
370 mpScheduler
->GetDebugName() : "unknown";
374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */