Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
43e1dc9d
Commit
43e1dc9d
authored
Dec 02, 2015
by
Florian Loitsch
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #662 from floitschG/scheduler.merge_schedulers
Merge the two schedulers.
parents
3fe160d8
10c80f2e
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
149 additions
and
179 deletions
+149
-179
scheduler.dart
packages/flutter/lib/src/animation/scheduler.dart
+1
-154
tap.dart
packages/flutter/lib/src/gestures/tap.dart
+1
-1
scheduler.dart
packages/flutter/lib/src/scheduler/scheduler.dart
+138
-15
scheduler_test.dart
packages/unit/test/scheduler/scheduler_test.dart
+9
-9
No files found.
packages/flutter/lib/src/animation/scheduler.dart
View file @
43e1dc9d
...
@@ -2,157 +2,4 @@
...
@@ -2,157 +2,4 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:collection'
;
export
'../scheduler/scheduler.dart'
;
import
'dart:developer'
;
import
'dart:ui'
as
ui
;
/// Slows down animations by this factor to help in development.
double
timeDilation
=
1.0
;
/// A callback from the scheduler
///
/// The timeStamp is the number of milliseconds since the beginning of the
/// scheduler's epoch. Use timeStamp to determine how far to advance animation
/// timelines so that all the animations in the system are synchronized to a
/// common time base.
typedef
void
SchedulerCallback
(
Duration
timeStamp
);
typedef
void
SchedulerExceptionHandler
(
dynamic
exception
,
StackTrace
stack
);
/// This callback is invoked whenever an exception is caught by the scheduler.
/// The 'exception' argument contains the object that was thrown, and the
/// 'stack' argument contains the stack trace. If the callback is set, it is
/// invoked instead of printing the information to the console.
SchedulerExceptionHandler
debugSchedulerExceptionHandler
;
/// Schedules callbacks to run in concert with the engine's animation system
class
Scheduler
{
/// Requires clients to use the [scheduler] singleton
Scheduler
.
_
()
{
ui
.
window
.
onBeginFrame
=
beginFrame
;
}
bool
_haveScheduledVisualUpdate
=
false
;
int
_nextCallbackId
=
0
;
// positive
final
List
<
SchedulerCallback
>
_persistentCallbacks
=
new
List
<
SchedulerCallback
>();
Map
<
int
,
SchedulerCallback
>
_transientCallbacks
=
new
LinkedHashMap
<
int
,
SchedulerCallback
>();
final
Set
<
int
>
_removedIds
=
new
Set
<
int
>();
final
List
<
SchedulerCallback
>
_postFrameCallbacks
=
new
List
<
SchedulerCallback
>();
bool
_inFrame
=
false
;
int
get
transientCallbackCount
=>
_transientCallbacks
.
length
;
void
_invokeAnimationCallbacks
(
Duration
timeStamp
)
{
Timeline
.
startSync
(
'Animate'
);
assert
(
_inFrame
);
Map
<
int
,
SchedulerCallback
>
callbacks
=
_transientCallbacks
;
_transientCallbacks
=
new
Map
<
int
,
SchedulerCallback
>();
callbacks
.
forEach
((
int
id
,
SchedulerCallback
callback
)
{
if
(!
_removedIds
.
contains
(
id
))
invokeCallback
(
callback
,
timeStamp
);
});
_removedIds
.
clear
();
Timeline
.
finishSync
();
}
/// Called by the engine to produce a new frame.
///
/// This function first calls all the callbacks registered by
/// [requestAnimationFrame], then calls all the callbacks registered by
/// [addPersistentFrameCallback], which typically drive the rendering pipeline,
/// and finally calls the callbacks registered by [requestPostFrameCallback].
void
beginFrame
(
Duration
rawTimeStamp
)
{
Timeline
.
startSync
(
'Begin frame'
);
assert
(!
_inFrame
);
_inFrame
=
true
;
Duration
timeStamp
=
new
Duration
(
microseconds:
(
rawTimeStamp
.
inMicroseconds
/
timeDilation
).
round
());
_haveScheduledVisualUpdate
=
false
;
_invokeAnimationCallbacks
(
timeStamp
);
for
(
SchedulerCallback
callback
in
_persistentCallbacks
)
invokeCallback
(
callback
,
timeStamp
);
List
<
SchedulerCallback
>
localPostFrameCallbacks
=
new
List
<
SchedulerCallback
>.
from
(
_postFrameCallbacks
);
_postFrameCallbacks
.
clear
();
for
(
SchedulerCallback
callback
in
localPostFrameCallbacks
)
invokeCallback
(
callback
,
timeStamp
);
_inFrame
=
false
;
Timeline
.
finishSync
();
}
void
invokeCallback
(
SchedulerCallback
callback
,
Duration
timeStamp
)
{
assert
(
callback
!=
null
);
try
{
callback
(
timeStamp
);
}
catch
(
exception
,
stack
)
{
if
(
debugSchedulerExceptionHandler
!=
null
)
{
debugSchedulerExceptionHandler
(
exception
,
stack
);
}
else
{
print
(
'-- EXCEPTION IN SCHEDULER CALLBACK --'
);
print
(
'
$exception
'
);
print
(
'Stack trace:'
);
print
(
'
$stack
'
);
}
}
}
/// Call callback every frame.
void
addPersistentFrameCallback
(
SchedulerCallback
callback
)
{
_persistentCallbacks
.
add
(
callback
);
}
/// Schedule a callback for the next frame.
///
/// The callback will be run prior to flushing the main rendering pipeline.
/// Typically, requestAnimationFrame is used to throttle writes into the
/// rendering pipeline until the system is ready to accept a new frame. For
/// example, if you wanted to tick through an animation, you should use
/// requestAnimation frame to determine when to tick the animation. The callback
/// is passed a timeStamp that you can use to determine how far along the
/// timeline to advance your animation.
///
/// Callbacks in invoked in an arbitrary order.
///
/// Returns an id that can be used to unschedule this callback.
int
requestAnimationFrame
(
SchedulerCallback
callback
)
{
_nextCallbackId
+=
1
;
_transientCallbacks
[
_nextCallbackId
]
=
callback
;
ensureVisualUpdate
();
return
_nextCallbackId
;
}
/// Cancel the callback identified by id.
void
cancelAnimationFrame
(
int
id
)
{
assert
(
id
>
0
);
_transientCallbacks
.
remove
(
id
);
_removedIds
.
add
(
id
);
}
/// Schedule a callback for the end of this frame.
///
/// If a frame is in progress, the callback will be run just after the main
/// rendering pipeline has been flushed. In this case, order is preserved (the
/// callbacks are run in registration order).
///
/// If no frame is in progress, it will be called at the start of the next
/// frame. In this case, the registration order is not preserved. Callbacks
/// are called in an arbitrary order.
void
requestPostFrameCallback
(
SchedulerCallback
callback
)
{
_postFrameCallbacks
.
add
(
callback
);
}
/// Ensure that a frame will be produced after this function is called.
void
ensureVisualUpdate
()
{
if
(
_haveScheduledVisualUpdate
)
return
;
ui
.
window
.
scheduleFrame
();
_haveScheduledVisualUpdate
=
true
;
}
}
/// A singleton instance of Scheduler to coordinate all the callbacks.
final
Scheduler
scheduler
=
new
Scheduler
.
_
();
packages/flutter/lib/src/gestures/tap.dart
View file @
43e1dc9d
packages/flutter/lib/src/scheduler/scheduler.dart
View file @
43e1dc9d
...
@@ -3,16 +3,35 @@
...
@@ -3,16 +3,35 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:async'
;
import
'dart:ui'
show
VoidCallback
;
import
'dart:developer'
;
import
'dart:ui'
as
ui
;
import
'package:collection/priority_queue.dart'
;
import
'package:collection/priority_queue.dart'
;
import
'package:flutter/animation.dart'
as
animation
show
scheduler
;
import
'package:flutter/animation.dart'
as
animation
show
scheduler
;
/// Slows down animations by this factor to help in development.
double
timeDilation
=
1.0
;
/// A frame-related callback from the scheduler.
///
/// The timeStamp is the number of milliseconds since the beginning of the
/// scheduler's epoch. Use timeStamp to determine how far to advance animation
/// timelines so that all the animations in the system are synchronized to a
/// common time base.
typedef
void
SchedulerCallback
(
Duration
timeStamp
);
typedef
void
SchedulerExceptionHandler
(
dynamic
exception
,
StackTrace
stack
);
/// This callback is invoked whenever an exception is caught by the scheduler.
/// The 'exception' argument contains the object that was thrown, and the
/// 'stack' argument contains the stack trace. If the callback is set, it is
/// invoked instead of printing the information to the console.
SchedulerExceptionHandler
debugSchedulerExceptionHandler
;
/// An entry in the scheduler's priority queue.
/// An entry in the scheduler's priority queue.
///
///
/// Combines the task and its priority.
/// Combines the task and its priority.
class
_SchedulerEntry
{
class
_SchedulerEntry
{
final
VoidCallback
task
;
final
ui
.
VoidCallback
task
;
final
int
priority
;
final
int
priority
;
const
_SchedulerEntry
(
this
.
task
,
this
.
priority
);
const
_SchedulerEntry
(
this
.
task
,
this
.
priority
);
...
@@ -62,8 +81,11 @@ class Priority {
...
@@ -62,8 +81,11 @@ class Priority {
/// the task should be run.
/// the task should be run.
///
///
/// Tasks always run in the idle time after a frame has been committed.
/// Tasks always run in the idle time after a frame has been committed.
class
TaskScheduler
{
class
Scheduler
{
TaskScheduler
.
_
();
/// Requires clients to use the [scheduler] singleton
Scheduler
.
_
()
{
ui
.
window
.
onBeginFrame
=
beginFrame
;
}
SchedulingStrategy
schedulingStrategy
=
new
DefaultSchedulingStrategy
();
SchedulingStrategy
schedulingStrategy
=
new
DefaultSchedulingStrategy
();
...
@@ -82,7 +104,7 @@ class TaskScheduler {
...
@@ -82,7 +104,7 @@ class TaskScheduler {
bool
_wakingNextFrame
=
false
;
bool
_wakingNextFrame
=
false
;
/// Schedules the given [task] with the given [priority].
/// Schedules the given [task] with the given [priority].
void
schedule
(
VoidCallback
task
,
Priority
priority
)
{
void
schedule
Task
(
ui
.
VoidCallback
task
,
Priority
priority
)
{
bool
isFirstTask
=
_queue
.
isEmpty
;
bool
isFirstTask
=
_queue
.
isEmpty
;
_queue
.
add
(
new
_SchedulerEntry
(
task
,
priority
.
_value
));
_queue
.
add
(
new
_SchedulerEntry
(
task
,
priority
.
_value
));
if
(
isFirstTask
)
if
(
isFirstTask
)
...
@@ -106,6 +128,110 @@ class TaskScheduler {
...
@@ -106,6 +128,110 @@ class TaskScheduler {
}
}
}
}
int
_nextFrameCallbackId
=
0
;
// positive
Map
<
int
,
SchedulerCallback
>
_transientCallbacks
=
<
int
,
SchedulerCallback
>{};
final
Set
<
int
>
_removedIds
=
new
Set
<
int
>();
int
get
transientCallbackCount
=>
_transientCallbacks
.
length
;
/// Schedules the given frame callback.
int
requestAnimationFrame
(
SchedulerCallback
callback
)
{
_nextFrameCallbackId
+=
1
;
_transientCallbacks
[
_nextFrameCallbackId
]
=
callback
;
_wakeNextFrame
();
return
_nextFrameCallbackId
;
}
/// Cancels the callback of the given [id].
void
cancelAnimationFrame
(
int
id
)
{
assert
(
id
>
0
);
_transientCallbacks
.
remove
(
id
);
_removedIds
.
add
(
id
);
}
final
List
<
SchedulerCallback
>
_persistentCallbacks
=
new
List
<
SchedulerCallback
>();
void
addPersistentFrameCallback
(
SchedulerCallback
callback
)
{
_persistentCallbacks
.
add
(
callback
);
}
final
List
<
SchedulerCallback
>
_postFrameCallbacks
=
new
List
<
SchedulerCallback
>();
/// Schedule a callback for the end of this frame.
///
/// If a frame is in progress, the callback will be run just after the main
/// rendering pipeline has been flushed. In this case, order is preserved (the
/// callbacks are run in registration order).
///
/// If no frame is in progress, it will be called at the start of the next
/// frame. In this case, the registration order is not preserved. Callbacks
/// are called in an arbitrary order.
void
requestPostFrameCallback
(
SchedulerCallback
callback
)
{
_postFrameCallbacks
.
add
(
callback
);
}
bool
_inFrame
=
false
;
void
_invokeAnimationCallbacks
(
Duration
timeStamp
)
{
Timeline
.
startSync
(
'Animate'
);
assert
(
_inFrame
);
Map
<
int
,
SchedulerCallback
>
callbacks
=
_transientCallbacks
;
_transientCallbacks
=
new
Map
<
int
,
SchedulerCallback
>();
callbacks
.
forEach
((
int
id
,
SchedulerCallback
callback
)
{
if
(!
_removedIds
.
contains
(
id
))
invokeCallback
(
callback
,
timeStamp
);
});
_removedIds
.
clear
();
Timeline
.
finishSync
();
}
/// Called by the engine to produce a new frame.
///
/// This function first calls all the callbacks registered by
/// [requestAnimationFrame], then calls all the callbacks registered by
/// [addPersistentFrameCallback], which typically drive the rendering pipeline,
/// and finally calls the callbacks registered by [requestPostFrameCallback].
void
beginFrame
(
Duration
rawTimeStamp
)
{
Timeline
.
startSync
(
'Begin frame'
);
assert
(!
_inFrame
);
_inFrame
=
true
;
Duration
timeStamp
=
new
Duration
(
microseconds:
(
rawTimeStamp
.
inMicroseconds
/
timeDilation
).
round
());
_wakingNextFrame
=
false
;
_invokeAnimationCallbacks
(
timeStamp
);
for
(
SchedulerCallback
callback
in
_persistentCallbacks
)
invokeCallback
(
callback
,
timeStamp
);
List
<
SchedulerCallback
>
localPostFrameCallbacks
=
new
List
<
SchedulerCallback
>.
from
(
_postFrameCallbacks
);
_postFrameCallbacks
.
clear
();
for
(
SchedulerCallback
callback
in
localPostFrameCallbacks
)
invokeCallback
(
callback
,
timeStamp
);
_inFrame
=
false
;
Timeline
.
finishSync
();
// All frame-related callbacks have been executed. Run lower-priority tasks.
tick
();
}
void
invokeCallback
(
SchedulerCallback
callback
,
Duration
timeStamp
)
{
assert
(
callback
!=
null
);
try
{
callback
(
timeStamp
);
}
catch
(
exception
,
stack
)
{
if
(
debugSchedulerExceptionHandler
!=
null
)
{
debugSchedulerExceptionHandler
(
exception
,
stack
);
}
else
{
print
(
'-- EXCEPTION IN SCHEDULER CALLBACK --'
);
print
(
'
$exception
'
);
print
(
'Stack trace:'
);
print
(
'
$stack
'
);
}
}
}
/// Tells the system that the scheduler is awake and should be called as
/// Tells the system that the scheduler is awake and should be called as
/// soon a there is time.
/// soon a there is time.
void
_wakeNow
()
{
void
_wakeNow
()
{
...
@@ -118,23 +244,20 @@ class TaskScheduler {
...
@@ -118,23 +244,20 @@ class TaskScheduler {
});
});
}
}
/// Tells the system that the scheduler needs to run again (ideally next
void
ensureVisualUpdate
()
{
/// frame).
_wakeNextFrame
();
}
/// Schedules a new frame.
void
_wakeNextFrame
()
{
void
_wakeNextFrame
()
{
if
(
_wakingNextFrame
)
if
(
_wakingNextFrame
)
return
;
return
;
_wakingNextFrame
=
true
;
_wakingNextFrame
=
true
;
animation
.
scheduler
.
requestAnimationFrame
((
_
)
{
ui
.
window
.
scheduleFrame
();
_wakingNextFrame
=
false
;
// RequestAnimationFrame calls back at the beginning of a frame. We want
// to run in the idle-phase of an animation. We therefore request to be
// woken up as soon as possible.
_wakeNow
();
});
}
}
}
}
final
TaskScheduler
tasks
=
new
Task
Scheduler
.
_
();
final
Scheduler
scheduler
=
new
Scheduler
.
_
();
abstract
class
SchedulingStrategy
{
abstract
class
SchedulingStrategy
{
bool
shouldRunTaskWithPriority
(
int
priority
);
bool
shouldRunTaskWithPriority
(
int
priority
);
...
...
packages/unit/test/scheduler/scheduler_test.dart
View file @
43e1dc9d
...
@@ -16,29 +16,29 @@ class TestStrategy implements SchedulingStrategy {
...
@@ -16,29 +16,29 @@ class TestStrategy implements SchedulingStrategy {
void
main
(
)
{
void
main
(
)
{
test
(
"Tasks are executed in the right order"
,
()
{
test
(
"Tasks are executed in the right order"
,
()
{
var
strategy
=
new
TestStrategy
();
var
strategy
=
new
TestStrategy
();
tasks
.
schedulingStrategy
=
strategy
;
scheduler
.
schedulingStrategy
=
strategy
;
List
input
=
[
2
,
23
,
23
,
11
,
0
,
80
,
3
];
List
input
=
[
2
,
23
,
23
,
11
,
0
,
80
,
3
];
List
executedTasks
=
[];
List
executedTasks
=
[];
void
scheduleAddingTask
(
int
x
)
{
void
scheduleAddingTask
(
int
x
)
{
tasks
.
schedule
(()
{
executedTasks
.
add
(
x
);
},
Priority
.
idle
+
x
);
scheduler
.
scheduleTask
(()
{
executedTasks
.
add
(
x
);
},
Priority
.
idle
+
x
);
}
}
for
(
int
x
in
input
)
{
for
(
int
x
in
input
)
{
scheduleAddingTask
(
x
);
scheduleAddingTask
(
x
);
}
}
strategy
.
allowedPriority
=
100
;
strategy
.
allowedPriority
=
100
;
for
(
int
i
=
0
;
i
<
3
;
i
++)
tasks
.
tick
();
for
(
int
i
=
0
;
i
<
3
;
i
++)
scheduler
.
tick
();
expect
(
executedTasks
.
isEmpty
,
isTrue
);
expect
(
executedTasks
.
isEmpty
,
isTrue
);
strategy
.
allowedPriority
=
50
;
strategy
.
allowedPriority
=
50
;
for
(
int
i
=
0
;
i
<
3
;
i
++)
tasks
.
tick
();
for
(
int
i
=
0
;
i
<
3
;
i
++)
scheduler
.
tick
();
expect
(
executedTasks
.
length
,
equals
(
1
));
expect
(
executedTasks
.
length
,
equals
(
1
));
expect
(
executedTasks
.
single
,
equals
(
80
));
expect
(
executedTasks
.
single
,
equals
(
80
));
executedTasks
.
clear
();
executedTasks
.
clear
();
strategy
.
allowedPriority
=
20
;
strategy
.
allowedPriority
=
20
;
for
(
int
i
=
0
;
i
<
3
;
i
++)
tasks
.
tick
();
for
(
int
i
=
0
;
i
<
3
;
i
++)
scheduler
.
tick
();
expect
(
executedTasks
.
length
,
equals
(
2
));
expect
(
executedTasks
.
length
,
equals
(
2
));
expect
(
executedTasks
[
0
],
equals
(
23
));
expect
(
executedTasks
[
0
],
equals
(
23
));
expect
(
executedTasks
[
1
],
equals
(
23
));
expect
(
executedTasks
[
1
],
equals
(
23
));
...
@@ -48,21 +48,21 @@ void main() {
...
@@ -48,21 +48,21 @@ void main() {
scheduleAddingTask
(
19
);
scheduleAddingTask
(
19
);
scheduleAddingTask
(
5
);
scheduleAddingTask
(
5
);
scheduleAddingTask
(
97
);
scheduleAddingTask
(
97
);
for
(
int
i
=
0
;
i
<
3
;
i
++)
tasks
.
tick
();
for
(
int
i
=
0
;
i
<
3
;
i
++)
scheduler
.
tick
();
expect
(
executedTasks
.
length
,
equals
(
2
));
expect
(
executedTasks
.
length
,
equals
(
2
));
expect
(
executedTasks
[
0
],
equals
(
99
));
expect
(
executedTasks
[
0
],
equals
(
99
));
expect
(
executedTasks
[
1
],
equals
(
97
));
expect
(
executedTasks
[
1
],
equals
(
97
));
executedTasks
.
clear
();
executedTasks
.
clear
();
strategy
.
allowedPriority
=
10
;
strategy
.
allowedPriority
=
10
;
for
(
int
i
=
0
;
i
<
3
;
i
++)
tasks
.
tick
();
for
(
int
i
=
0
;
i
<
3
;
i
++)
scheduler
.
tick
();
expect
(
executedTasks
.
length
,
equals
(
2
));
expect
(
executedTasks
.
length
,
equals
(
2
));
expect
(
executedTasks
[
0
],
equals
(
19
));
expect
(
executedTasks
[
0
],
equals
(
19
));
expect
(
executedTasks
[
1
],
equals
(
11
));
expect
(
executedTasks
[
1
],
equals
(
11
));
executedTasks
.
clear
();
executedTasks
.
clear
();
strategy
.
allowedPriority
=
1
;
strategy
.
allowedPriority
=
1
;
for
(
int
i
=
0
;
i
<
4
;
i
++)
tasks
.
tick
();
for
(
int
i
=
0
;
i
<
4
;
i
++)
scheduler
.
tick
();
expect
(
executedTasks
.
length
,
equals
(
3
));
expect
(
executedTasks
.
length
,
equals
(
3
));
expect
(
executedTasks
[
0
],
equals
(
5
));
expect
(
executedTasks
[
0
],
equals
(
5
));
expect
(
executedTasks
[
1
],
equals
(
3
));
expect
(
executedTasks
[
1
],
equals
(
3
));
...
@@ -70,7 +70,7 @@ void main() {
...
@@ -70,7 +70,7 @@ void main() {
executedTasks
.
clear
();
executedTasks
.
clear
();
strategy
.
allowedPriority
=
0
;
strategy
.
allowedPriority
=
0
;
tasks
.
tick
();
scheduler
.
tick
();
expect
(
executedTasks
.
length
,
equals
(
1
));
expect
(
executedTasks
.
length
,
equals
(
1
));
expect
(
executedTasks
[
0
],
equals
(
0
));
expect
(
executedTasks
[
0
],
equals
(
0
));
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment