blob: b213448762a7df1d1a446a8f5fd0368edce7bdab [file] [log] [blame] [view]
Hosseindc984e72021-10-20 10:55:25 +02001# VCL Scheduler
2
3## Introduction
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +02004
5The VCL scheduler handles LOs primary event queue. It is simple by design,
6currently just a single-linked list, processed in list-order by priority
Andrea Gelmini64a31242017-08-17 16:41:20 +02007using round-robin for reoccurring tasks.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +02008
9The scheduler has the following behaviour:
10
11B.1. Tasks are scheduled just priority based
Hossein67615c82022-07-30 20:30:19 +020012
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020013B.2. Implicitly cooperative AKA non-preemptive
Hossein67615c82022-07-30 20:30:19 +020014
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020015B.3. It's not "fair" in any way (a consequence of B.2)
Hossein67615c82022-07-30 20:30:19 +020016
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020017B.4. Tasks are handled round-robin (per priority)
Hossein67615c82022-07-30 20:30:19 +020018
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020019B.5. Higher priorities have lower values
Hossein67615c82022-07-30 20:30:19 +020020
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020021B.6. A small set of priorities instead of an flexible value AKA int
22
23There are some consequences due to this design.
24
Andrea Gelmini64a31242017-08-17 16:41:20 +020025C.1. Higher priority tasks starve lower priority tasks
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020026 As long as a higher task is available, lower tasks are never run!
27 See Anti-pattern.
28
29C.2. Tasks should be split into sensible blocks
30 If this can't really be done, process pending tasks by calling
Hosseindc984e72021-10-20 10:55:25 +020031 `Application::Reschedule()`. Or use a thread.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020032
33C.3. This is not an OS scheduler
34 There is no real way to "fix" B.2. and B.3.
Andrea Gelmini64a31242017-08-17 16:41:20 +020035 If you need to do a preemptive task, use a thread!
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +020036 Otherwise make your task suspendable.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020037
38
Hosseindc984e72021-10-20 10:55:25 +020039## Driving the scheduler AKA the system timer
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020040
41 1. There is just one system timer, which drives LO event loop
42 2. The timer has to run in the main window thread
43 3. The scheduler is run with the Solar mutex acquired
44 4. The system timer is a single-shot timer
45 5. The scheduler system event / message has a low system priority.
46 All system events should have a higher priority.
47
Andrea Gelmini64a31242017-08-17 16:41:20 +020048Every time a task is started, the scheduler timer is adjusted. When the timer
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020049fires, it posts an event to the system message queue. If the next most
Andrea Gelmini64a31242017-08-17 16:41:20 +020050important task is an Idle (AKA instant, 0ms timeout), the event is pushed to
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020051the back of the queue, so we don't starve system messages, otherwise to the
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +020052front.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020053
Andrea Gelmini64a31242017-08-17 16:41:20 +020054Every time the scheduler is invoked it searches for the next task to process,
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020055restarts the timer with the timeout for the next event and then invokes the
56task. After invoking the task and if the task is still active, it is pushed
57to the end of the queue and the timeout is eventually adjusted.
58
59
Hosseindc984e72021-10-20 10:55:25 +020060## Locking
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020061
Andrea Gelmini64a31242017-08-17 16:41:20 +020062The locking is quite primitive: all interaction with internal Scheduler
Hosseindc984e72021-10-20 10:55:25 +020063structures are locked. This includes the `ImplSchedulerContext` and the
64`Task::mpSchedulerData`, which is actually a part of the scheduler.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020065Before invoking the task, we have to release the lock, so others can
66Start new Tasks.
67
Andrea Gelmini1d3421c2020-12-07 22:42:13 +010068The Scheduler just processes its own Tasks in the main thread and needs
Hosseindc984e72021-10-20 10:55:25 +020069the `SolarMutex` for it and for `DeInit` (tested by `DBG_TESTSOLARMUTEX`). All
Stephan Bergmann69a9b482020-12-08 16:04:48 +010070the other interaction just take the scheduler mutex or don't need locking
Jan-Marek Glogowskie4b70272020-12-04 21:05:28 +010071at all.
72
Andrea Gelmini1d3421c2020-12-07 22:42:13 +010073There is a "workaround" for static Task objects, which would crash LO on
Hosseindc984e72021-10-20 10:55:25 +020074destruction, because `Task::~Task` would try to de-register itself in the
75Scheduler, while the `SchedulerLock` would be long gone. OTOH this makes
Jan-Marek Glogowskie4b70272020-12-04 21:05:28 +010076Task handling less error-prone, than doing "special" manual cleanup.
77There is also a "= TODOs and ideas =" to get rid if static Tasks.
78
Stephan Bergmann69a9b482020-12-08 16:04:48 +010079Actually the scheduler mutex should never be locked when calling into
80non-scheduler code, so it was converted to a non-recursive
Hosseindc984e72021-10-20 10:55:25 +020081`std::mutex`.
Jan-Marek Glogowski84af20e2020-12-05 12:42:11 +010082
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +020083
Hosseindc984e72021-10-20 10:55:25 +020084## Idle processing
Luboš Luňákd3b498c2021-02-07 16:34:38 +010085
86Confusingly, there are 2 concepts that are called 'idle':
87
88* Instant (zero timeout) tasks, represented e.g. by the Idle class. This is
89a misnomer, as these tasks are processed after returning to the main loop.
90This is not necessarily when LO is idle, in fact such tasks may be invoked
91while there is input in the OS event queue pending.
92(TODO: This case should be fixed by renaming.)
93
Hosseindc984e72021-10-20 10:55:25 +020094* Low priority tasks, represented by priorities `TaskPriority::HIGH_IDLE` and
95lower. In addition to being invoked only when there is no task with a higher
96priority, pending input in the OS event queue also takes precedence.
Luboš Luňákd3b498c2021-02-07 16:34:38 +010097
98
Hosseindc984e72021-10-20 10:55:25 +020099## Lifecycle / thread-safety of Scheduler-based objects
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200100
101A scheduler object it thread-safe in the way, that it can be associated to
102any thread and any thread is free to call any functions on it. The owner must
Hosseindc984e72021-10-20 10:55:25 +0200103guarantee that the `Invoke()` function can be called, while the Scheduler object
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200104exists / is not disposed.
105
106
Hosseindc984e72021-10-20 10:55:25 +0200107## Anti-pattern: Dependencies via (fine grained) priorities
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200108
109"Idle 1" should run before "Idle 2", therefore give "Idle 1" a higher priority
110then "Idle 2". This just works correct for low frequency idles, but otherwise
111always breaks!
112
113If you have some longer work - even if it can be split by into schedulable,
114smaller blocks - you normally don't want to schedule it with a non-default
115priority, as it starves all lower priority tasks. Even if a block was processed
116in "Idle 1", it is scheduled with the same (higher) priority again. Changing
117the "Idle" to a "Timer" also won't work, as this breaks the dependency.
118
119What is needed is task based dependency handling, so if "Task 1" is done, it
120has to start "Task 2" and if "Task 1" is started again, it has to stop
121"Task 2". This currently has to be done by the implementor, but this feature
122can be added to the scheduler reasonably.
123
124
Hosseindc984e72021-10-20 10:55:25 +0200125## Implementation details
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200126
Hosseindc984e72021-10-20 10:55:25 +0200127### General: event priority for `DoYield`
Jan-Marek Glogowski47ee8092017-10-16 16:59:45 +0200128
129There are three types of events, with different priority:
130
1311. LO user events
1322. System events
1333. LO Scheduler event
134
135They should be processed according to the following code:
136
Hosseindc984e72021-10-20 10:55:25 +0200137```
Jan-Marek Glogowski5fbc7ab2021-06-25 09:55:20 +0200138bool ImplYield(bool bWait, bool bAllCurrent)
Jan-Marek Glogowski47ee8092017-10-16 16:59:45 +0200139{
Jan-Marek Glogowski5fbc7ab2021-06-25 09:55:20 +0200140 DBG_TESTSOLARMUTEX();
141 assert(IsMainThread());
142
Jan-Marek Glogowski47ee8092017-10-16 16:59:45 +0200143 bool bWasEvent = ProcessUserEvents( bAllCurrent );
144 if ( !bAllCurrent && bWasEvent )
145 return true;
Jan-Marek Glogowski5fbc7ab2021-06-25 09:55:20 +0200146
147 SolarMutexReleaser();
Jan-Marek Glogowski47ee8092017-10-16 16:59:45 +0200148 bWasEvent = ProcessSystemEvents( bAllCurrent, &bWasSchedulerEvent ) || bWasEvent;
149 if ( !bWasSchedulerEvent && IsSchedulerEvent() )
150 {
151 ProcessSchedulerEvent()
152 bWasEvent = true;
153 }
154 if ( !bWasEvent && bWait )
155 {
156 WaitForSystemEvents();
157 bWasEvent = true;
158 }
159 return bWasEvent;
160}
Hosseindc984e72021-10-20 10:55:25 +0200161```
Jan-Marek Glogowski47ee8092017-10-16 16:59:45 +0200162
Hosseindc984e72021-10-20 10:55:25 +0200163### General: main thread deferral
Jan-Marek Glogowski4baec722017-08-28 19:58:32 +0200164
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200165In almost all VCL backends, we run main thread deferrals by disabling the
Hosseindc984e72021-10-20 10:55:25 +0200166`SolarMutex` using a boolean. In the case of the redirect, this makes
167`tryToAcquire` and `doAcquire` return `true` or `1`, while a release is ignored.
168Also the `IsCurrentThread()` mutex check function will act accordingly, so all
169the `DBG_TESTSOLARMUTEX` won't fail.
Jan-Marek Glogowski4baec722017-08-28 19:58:32 +0200170
171Since we just disable the locks when we start running the deferred code in the
172main thread, we won't let the main thread run into stuff, where it would
173normally wait for the SolarMutex.
174
Hosseindc984e72021-10-20 10:55:25 +0200175Eventually this will move into the `SolarMutex`. KDE / Qt also does main
176thread redirects using `Qt::BlockingQueuedConnection`.
Jan-Marek Glogowski4baec722017-08-28 19:58:32 +0200177
Hosseindc984e72021-10-20 10:55:25 +0200178### General: processing all current events for `DoYield`
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200179
180This is easily implemented for all non-priority queue based implementations.
Jan-Marek Glogowskida5cdcd2017-09-29 21:02:17 +0200181Windows and macOS both have a timestamp attached to their events / messages,
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200182so simply get the current time and just process anything < timestamp.
Hosseindc984e72021-10-20 10:55:25 +0200183For the **KDE** backend this is already the default behaviour - single event
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200184processing isn't even supported. The headless backend accomplishes this by
185just processing a copy of the list of current events.
186
Hosseindc984e72021-10-20 10:55:25 +0200187Problematic in this regard is the **Gtk+** backend. `g_main_context_iteration`
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200188dispatches "only those highest priority event sources". There is no real way
189to tell, when these became ready. I've added a workaround idea to the TODO
Hosseindc984e72021-10-20 10:55:25 +0200190list. FWIW: **Qt** runs just a single timer source in the glib main context,
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200191basically the same we're doing with the LO scheduler as a system event.
192
Hosseindc984e72021-10-20 10:55:25 +0200193The gen **X11** backend has some levels of redirection, but needs quite some work
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200194to get this fixed.
195
Hosseindc984e72021-10-20 10:55:25 +0200196### General: non-main thread yield
Jan-Marek Glogowskice8bbb72017-08-29 09:40:01 +0200197
198Yielding from a non-main thread must not wait in the main thread, as this
199may block the main thread until some events happen.
200
201Currently we wait on an extra conditional, which is cleared by the main event
202loop.
203
Hosseindc984e72021-10-20 10:55:25 +0200204### General: invalidation of elapsed timer event messages
Jan-Marek Glogowskida5cdcd2017-09-29 21:02:17 +0200205
206Since the system timer to run the scheduler is single-shot, there should never
Andrea Gelminie6c03692019-08-09 17:13:59 +0200207be more than one elapsed timer event in system event queue. When stopping or
Jan-Marek Glogowskida5cdcd2017-09-29 21:02:17 +0200208restarting the timer, we eventually have to remove the now invalid event from
209the queue.
210
211But for the Windows and macOS backends this may fail as they have delayed
212posting of events, so a consecutive remove after a post will actually yield no
213remove. On Windows we even get unwanted processing of events outside of the
214main event loop, which may call the Scheduler, as timer management is handled
215in critical scheduler code.
216
217To prevent these problems, we don't even try to remove these events, but
218invalidate them by versioning the timer events. Timer events with invalid
219versions are processed but simply don't run the scheduler.
220
Hosseindc984e72021-10-20 10:55:25 +0200221### General: track time of long running tasks
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200222
Hosseindc984e72021-10-20 10:55:25 +0200223There is `TaskStopwatch` class. It'll track the time and report a timeout either
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200224when the tasks time slice is finished or some system event did occur.
225
226Eventually it will be merged into the main scheduler, so each invoked task can
227easily track it's runtime and eventually this can be used to "blame" / find
228other long running tasks, so interactivity can be improved.
229
230There were some questions coming up when implementing it:
231
Hossein67615c82022-07-30 20:30:19 +0200232#### Why does the scheduler not detect that we only have idle tasks pending, and skip the instant timeout?
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200233
234You never know how long a task will run. Currently the scheduler simply asks
Andrea Gelmini08097a22019-08-13 17:05:37 +0200235each task when it'll be ready to run, until two runnable tasks are found.
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200236Normally this is very quick, as LO has a lot of one-shot instant tasks / Idles
237and just a very few long term pending Timers.
238
239Especially UNO calls add a lot of Idles to the task list, which just need to
240be processed in order.
241
Hosseindc984e72021-10-20 10:55:25 +0200242#### Why not use things like Linux timer wheels?
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200243
244LO has relatively few timers and a lot one-shot Idles. 99% of time the search
245for the next task is quick, because there are just ~5 long term timers per
246document (cache invalidation, cursor blinking etc.).
247
248This might become a problem, if you have a lot of open documents, so the long
249term timer list increases AKA for highly loaded LOOL instances.
250
251But the Linux timer wheel mainly relies on the facts that the OS timers are
252expected to not expire, as they are use to catch "error" timeouts, which rarely
Andrea Gelmini08097a22019-08-13 17:05:37 +0200253happen, so this definitely not matches LO's usage.
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200254
Hosseindc984e72021-10-20 10:55:25 +0200255#### Not really usable to find misbehaving tasks
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200256
257The TaskStopwatch class is just a little time keeper + detecting of input
258events. This is not about misbehaving Tasks, but long running tasks, which
259have to yield to the Scheduler, so other Tasks and System events can be
260processed.
261
262There is the TODO to merge the functionality into the Scheduler itself, at
263which point we can think about profiling individual Tasks to improve
264interactivity.
265
Hosseindc984e72021-10-20 10:55:25 +0200266### macOS implementation details
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200267
Jan-Marek Glogowski33b094a2017-08-15 08:23:31 +0200268Generally the Scheduler is handled as expected, except on resize, which is
Jan-Marek Glogowskida5cdcd2017-09-29 21:02:17 +0200269handled with different runloop-modes in macOS. In case of a resize, the normal
Hosseindc984e72021-10-20 10:55:25 +0200270`runloop` is suspended in `sendEvent`, so we can't call the scheduler via posted
Jan-Marek Glogowski9796f792017-08-08 15:03:37 +0200271main loop-events. Instead the scheduler uses the timer again.
272
273Like the Windows backend, all Cocoa / GUI handling also has to be run in
274the main thread. We're emulating Windows out-of-order PeekMessage processing,
Hosseindc984e72021-10-20 10:55:25 +0200275via a `YieldWakeupEvent` and two conditionals. When in a `RUNINMAIN` call, all
276the `DBG_TESTSOLARMUTEX` calls are disabled, as we can't release the
277`SolarMutex`, but we can prevent running any other `SolarMutex` based code.
278Those wakeup events must be ignored to prevent busy-locks. For more info read
279the "General: main thread deferral" section.
Jan-Marek Glogowski9796f792017-08-08 15:03:37 +0200280
Hossein67615c82022-07-30 20:30:19 +0200281We can neither rely on macOS `dispatch_sync` code block execution nor the
Andrea Gelminiddcdd4a2017-09-22 11:23:45 +0200282message handling, as both can't be prioritized or filtered and the first
Jan-Marek Glogowski9796f792017-08-08 15:03:37 +0200283does also not allow nested execution and is just processed in sequence.
Jan-Marek Glogowski33b094a2017-08-15 08:23:31 +0200284
285There is also a workaround for a problem for pushing tasks to an empty queue,
Hossein67615c82022-07-30 20:30:19 +0200286as `[NSApp postEvent: ... atStart: NO]` doesn't append the event, if the
Jan-Marek Glogowski33b094a2017-08-15 08:23:31 +0200287message queue is empty.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200288
Jan-Marek Glogowskic69c1ee2017-10-14 01:43:07 +0200289An additional problem is the filtering of events on Window close. This drops
290posted timer events, when a Window is closed resulting in a busy DoYield loop,
291so we have to re-post the event, after closing a window.
292
Hosseindc984e72021-10-20 10:55:25 +0200293### Windows implementation details
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200294
295Posted or sent event messages often trigger processing of WndProc in
Hosseindc984e72021-10-20 10:55:25 +0200296`PeekMessage`, `GetMessage` or `DispatchMessage`, independently from the message
297to fetch, remove or dispatch ("During this call, the system delivers pending,
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200298nonqueued messages..."). Additionally messages have an inherited priority
Hosseindc984e72021-10-20 10:55:25 +0200299based on the function used to generate them. Even if `WM_TIMER` messages should
300have the lowest priority, a manually posted `WM_TIMER` is processed with the
Jan-Marek Glogowskida5cdcd2017-09-29 21:02:17 +0200301priority of a PostMessage message.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200302
Jan-Marek Glogowskifb4e7be2017-10-12 16:00:42 +0200303So we're giving up on processing all our Scheduler events as a message in the
304system message loop. Instead we just indicate a 0ms timer message by setting
Hosseindc984e72021-10-20 10:55:25 +0200305the `m_bDirectTimeout` in the timer object. This timer is always processed, if
Jan-Marek Glogowskifb4e7be2017-10-12 16:00:42 +0200306the system message wasn't already our timer. As a result we can also skip the
307polling. All this is one more reason to drop the single message processing
308in favour of always processing all pending (system) events.
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200309
Andrea Gelminid71859e2018-09-02 06:16:28 +0200310There is another special case, we have to handle: window updates during move
Jan-Marek Glogowskiabe3af82017-10-12 18:19:12 +0200311and resize of windows. These system actions run in their own nested message
Hosseindc984e72021-10-20 10:55:25 +0200312loop. So we have to completely switch to timers, even for 0 ms. But these
Jan-Marek Glogowskiabe3af82017-10-12 18:19:12 +0200313posted events prevent any event processing, while we're busy. The only viable
Hosseindc984e72021-10-20 10:55:25 +0200314solution seems to be to switch to `WM_TIMER` based timers, as these generate
315messages with the lowest system priority (but they don't allow 0 ms timeouts).
Jan-Marek Glogowskiabe3af82017-10-12 18:19:12 +0200316So processing slows down during resize and move, but we gain working painting,
317even when busy.
318
Jan-Marek Glogowski448e9da2017-08-24 13:41:37 +0200319An additional workaround is implemented for the delayed queuing of posted
Hosseindc984e72021-10-20 10:55:25 +0200320messages, where `PeekMessage` in `WinSalTimer::Stop()` won't be able remove the
Jan-Marek Glogowskida5cdcd2017-09-29 21:02:17 +0200321just posted timer callback message. See "General: invalidation of elapsed
322timer event messages" for the details.
Jan-Marek Glogowski448e9da2017-08-24 13:41:37 +0200323
Jan-Marek Glogowski4baec722017-08-28 19:58:32 +0200324To run the required GUI code in the main thread without unlocking the
Hosseindc984e72021-10-20 10:55:25 +0200325`SolarMutex`, we "disable" it. For more infos read the "General: main thread
Jan-Marek Glogowski4baec722017-08-28 19:58:32 +0200326deferral" section.
327
Hosseindc984e72021-10-20 10:55:25 +0200328### KDE implementation details
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200329
Andrea Gelmini64a31242017-08-17 16:41:20 +0200330This implementation also works as intended. But there is a different Yield
Hosseindc984e72021-10-20 10:55:25 +0200331handling, because Qts `QAbstractEventDispatcher::processEvents` will always
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200332process all pending events.
333
334
Hosseindc984e72021-10-20 10:55:25 +0200335## TODOs and ideas
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200336
Hosseindc984e72021-10-20 10:55:25 +0200337### Task dependencies AKA children
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200338
339Every task can have a list of children / a child.
340
341 * When a task is stopped, the children are started.
342 * When a task is started, the children are stopped.
343
344This should be easy to implement.
345
Hosseindc984e72021-10-20 10:55:25 +0200346### Per priority time-sorted queues
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200347
348This would result in O(1) scheduler. It was used in the Linux kernel for some
Andrea Gelmini64a31242017-08-17 16:41:20 +0200349time (search Ingo Molnar's O(1) scheduler). This can be a scheduling
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200350optimization, which would prevent walking longer event list. But probably the
351management overhead would be too large, as we have many one-shot events.
352
353To find the next task the scheduler just walks the (constant) list of priority
354queues and schedules the first ready event of any queue.
355
356The downside of this approach: Insert / Start / Reschedule(for "auto" tasks)
357now need O(log(n)) to find the position in the queue of the priority.
358
Hosseindc984e72021-10-20 10:55:25 +0200359### Always process all (higher priority) pending events
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200360
Hosseindc984e72021-10-20 10:55:25 +0200361Currently `Application::Reschedule()` processes a single event or "all" events,
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200362with "all" defined as "100 events" in most backends. This already is ignored
Hosseindc984e72021-10-20 10:55:25 +0200363by the KDE backend, as Qt defines its `QAbstractEventDispatcher::processEvents`
Jan-Marek Glogowskifa1be532017-08-04 13:46:44 +0200364processing all pending events (there are ways to skip event classes, but no
365easy way to process just a single event).
366
367Since the Scheduler is always handled by the system message queue, there is
368really no more reasoning to stop after 100 events to prevent LO Scheduler
369starvation.
Jan-Marek Glogowski9796f792017-08-08 15:03:37 +0200370
Hosseindc984e72021-10-20 10:55:25 +0200371### Drop static inherited or composed Task objects
Jan-Marek Glogowski06ce3122017-08-23 16:07:50 +0200372
373The sequence of destruction of static objects is not defined. So a static Task
374can not be guaranteed to happen before the Scheduler. When dynamic unloading
375is involved, this becomes an even worse problem. This way we could drop the
376mbStatic workaround from the Task class.
377
Hosseindc984e72021-10-20 10:55:25 +0200378### Run the LO application in its own thread
Jan-Marek Glogowski9796f792017-08-08 15:03:37 +0200379
Jan-Marek Glogowskida5cdcd2017-09-29 21:02:17 +0200380This would probably get rid of most of the macOS and Windows implementation
Jan-Marek Glogowski9796f792017-08-08 15:03:37 +0200381details / workarounds, but is quite probably a large amount of work.
382
383Instead of LO running in the main process / thread, we run it in a 2nd thread
384and defer al GUI calls to the main thread. This way it'll hopefully not block
385and can process system events.
386
Andrea Gelminiddcdd4a2017-09-22 11:23:45 +0200387That's just a theory - it definitely needs more analysis before even attending
Jan-Marek Glogowski9796f792017-08-08 15:03:37 +0200388an implementation.
Jan-Marek Glogowski448e9da2017-08-24 13:41:37 +0200389
Hosseindc984e72021-10-20 10:55:25 +0200390### Re-evaluate the macOS `ImplNSAppPostEvent`
Jan-Marek Glogowski448e9da2017-08-24 13:41:37 +0200391
392Probably a solution comparable to the Windows backends delayed PostMessage
393workaround using a validation timestamp is better then the current peek,
394remove, re-postEvent, which has to run in the main thread.
395
396Originally I didn't evaluate, if the event is actually lost or just delayed.
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200397
Hosseindc984e72021-10-20 10:55:25 +0200398### Drop `nMaxEvents` from Gtk+ based backends
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200399
Hosseindc984e72021-10-20 10:55:25 +0200400```
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200401gint last_priority = G_MAXINT;
402bool bWasEvent = false;
403do {
404 gint max_priority;
405 g_main_context_acquire( NULL );
406 bool bHasPending = g_main_context_prepare( NULL, &max_priority );
407 g_main_context_release( NULL );
408 if ( bHasPending )
409 {
410 if ( last_priority > max_priority )
411 {
412 bHasPending = g_main_context_iteration( NULL, bWait );
413 bWasEvent = bWasEvent || bHasPending;
414 }
415 else
416 bHasPending = false;
417 }
418}
419while ( bHasPending )
Hosseindc984e72021-10-20 10:55:25 +0200420```
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200421
Hossein67615c82022-07-30 20:30:19 +0200422The idea is to use `g_main_context_prepare` and keep the `max_priority` as an
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200423indicator. We cannot prevent running newer lower events, but we can prevent
424running new higher events, which should be sufficient for most stuff.
425
426This also touches user event processing, which currently runs as a high
427priority idle in the event loop.
428
Hosseindc984e72021-10-20 10:55:25 +0200429### Drop nMaxEvents from gen (X11) backend
Jan-Marek Glogowski9679fb22017-08-29 10:29:51 +0200430
Hosseindc984e72021-10-20 10:55:25 +0200431A few layers of indirection make this code hard to follow. The `SalXLib::Yield`
432and `SalX11Display::Yield` architecture makes it impossible to process just the
Andrea Gelmini2f066d62017-09-26 17:22:56 +0200433current events. This really needs a refactoring and rearchitecture step, which
Thorsten Behrens410bf592018-09-05 02:53:07 +0200434will also affect the Gtk+ and KDE backend for the user event handling.
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200435
Hosseindc984e72021-10-20 10:55:25 +0200436### Merge TaskStopwatch functionality into the Scheduler
Jan-Marek Glogowski6e135852019-07-07 23:09:15 +0200437
438This way it can be easier used to profile Tasks, eventually to improve LO's
439interactivity.
Hossein67615c82022-07-30 20:30:19 +0200440
441## See Also
442
443- [Solar Mutex](https://wiki.openoffice.org/wiki/Terms/Solar_Mutex)
444- [LibreOffice Main Loop](https://wiki.documentfoundation.org/Development/LHM_LiMux/Main_Loop)
445- [AOO Advanced Threading-Architecture (proposal)](https://wiki.openoffice.org/wiki/Architecture/Proposal/Advanced_Threading-Architecture)
446- [Revise OOo Multi-Threading Efforts](https://wiki.openoffice.org/wiki/Effort/Revise_OOo_Multi-Threading)
447- [Multi-Threading Analysis](https://wiki.openoffice.org/wiki/Analysis/Multi-Threading)
448- [AOO Wiki - Category:Multi-Threading](https://wiki.openoffice.org/wiki/Category:Multi-Threading)