| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/scheduler/renderer/renderer_scheduler_impl.h" | 5 #include "components/scheduler/renderer/renderer_scheduler_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/debug/stack_trace.h" | 8 #include "base/debug/stack_trace.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/trace_event/trace_event.h" | 10 #include "base/trace_event/trace_event.h" |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 42 base::TimeDelta()), | 42 base::TimeDelta()), |
| 43 render_widget_scheduler_signals_(this), | 43 render_widget_scheduler_signals_(this), |
| 44 control_task_runner_(helper_.ControlTaskRunner()), | 44 control_task_runner_(helper_.ControlTaskRunner()), |
| 45 compositor_task_runner_( | 45 compositor_task_runner_( |
| 46 helper_.NewTaskQueue(TaskQueue::Spec("compositor_tq") | 46 helper_.NewTaskQueue(TaskQueue::Spec("compositor_tq") |
| 47 .SetShouldMonitorQuiescence(true))), | 47 .SetShouldMonitorQuiescence(true))), |
| 48 delayed_update_policy_runner_( | 48 delayed_update_policy_runner_( |
| 49 base::Bind(&RendererSchedulerImpl::UpdatePolicy, | 49 base::Bind(&RendererSchedulerImpl::UpdatePolicy, |
| 50 base::Unretained(this)), | 50 base::Unretained(this)), |
| 51 helper_.ControlTaskRunner()), | 51 helper_.ControlTaskRunner()), |
| 52 main_thread_only_(compositor_task_runner_, helper_.tick_clock()), | 52 main_thread_only_(compositor_task_runner_, |
| 53 helper_.task_queue_manager_delegate()), |
| 53 policy_may_need_update_(&any_thread_lock_), | 54 policy_may_need_update_(&any_thread_lock_), |
| 54 weak_factory_(this) { | 55 weak_factory_(this) { |
| 55 update_policy_closure_ = base::Bind(&RendererSchedulerImpl::UpdatePolicy, | 56 update_policy_closure_ = base::Bind(&RendererSchedulerImpl::UpdatePolicy, |
| 56 weak_factory_.GetWeakPtr()); | 57 weak_factory_.GetWeakPtr()); |
| 57 end_renderer_hidden_idle_period_closure_.Reset(base::Bind( | 58 end_renderer_hidden_idle_period_closure_.Reset(base::Bind( |
| 58 &RendererSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr())); | 59 &RendererSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr())); |
| 59 | 60 |
| 60 suspend_timers_when_backgrounded_closure_.Reset( | 61 suspend_timers_when_backgrounded_closure_.Reset( |
| 61 base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded, | 62 base::Bind(&RendererSchedulerImpl::SuspendTimerQueueWhenBackgrounded, |
| 62 weak_factory_.GetWeakPtr())); | 63 weak_factory_.GetWeakPtr())); |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 } | 244 } |
| 244 } | 245 } |
| 245 | 246 |
| 246 void RendererSchedulerImpl::DidCommitFrameToCompositor() { | 247 void RendererSchedulerImpl::DidCommitFrameToCompositor() { |
| 247 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | 248 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
| 248 "RendererSchedulerImpl::DidCommitFrameToCompositor"); | 249 "RendererSchedulerImpl::DidCommitFrameToCompositor"); |
| 249 helper_.CheckOnValidThread(); | 250 helper_.CheckOnValidThread(); |
| 250 if (helper_.IsShutdown()) | 251 if (helper_.IsShutdown()) |
| 251 return; | 252 return; |
| 252 | 253 |
| 253 base::TimeTicks now(helper_.tick_clock()->NowTicks()); | 254 base::TimeTicks now(helper_.task_queue_manager_delegate()->NowTicks()); |
| 254 if (now < MainThreadOnly().estimated_next_frame_begin) { | 255 if (now < MainThreadOnly().estimated_next_frame_begin) { |
| 255 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of | 256 // TODO(rmcilroy): Consider reducing the idle period based on the runtime of |
| 256 // the next pending delayed tasks (as currently done in for long idle times) | 257 // the next pending delayed tasks (as currently done in for long idle times) |
| 257 idle_helper_.StartIdlePeriod( | 258 idle_helper_.StartIdlePeriod( |
| 258 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, now, | 259 IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, now, |
| 259 MainThreadOnly().estimated_next_frame_begin); | 260 MainThreadOnly().estimated_next_frame_begin); |
| 260 } | 261 } |
| 261 | 262 |
| 262 MainThreadOnly().idle_time_estimator.DidCommitFrameToCompositor(); | 263 MainThreadOnly().idle_time_estimator.DidCommitFrameToCompositor(); |
| 263 } | 264 } |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 end_idle_when_hidden_delay); | 301 end_idle_when_hidden_delay); |
| 301 MainThreadOnly().renderer_hidden = true; | 302 MainThreadOnly().renderer_hidden = true; |
| 302 } else { | 303 } else { |
| 303 MainThreadOnly().renderer_hidden = false; | 304 MainThreadOnly().renderer_hidden = false; |
| 304 EndIdlePeriod(); | 305 EndIdlePeriod(); |
| 305 } | 306 } |
| 306 | 307 |
| 307 // TODO(alexclarke): Should we update policy here? | 308 // TODO(alexclarke): Should we update policy here? |
| 308 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( | 309 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
| 309 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", | 310 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "RendererScheduler", |
| 310 this, AsValue(helper_.tick_clock()->NowTicks())); | 311 this, AsValue(helper_.task_queue_manager_delegate()->NowTicks())); |
| 311 } | 312 } |
| 312 | 313 |
| 313 void RendererSchedulerImpl::SetHasVisibleRenderWidgetWithTouchHandler( | 314 void RendererSchedulerImpl::SetHasVisibleRenderWidgetWithTouchHandler( |
| 314 bool has_visible_render_widget_with_touch_handler) { | 315 bool has_visible_render_widget_with_touch_handler) { |
| 315 helper_.CheckOnValidThread(); | 316 helper_.CheckOnValidThread(); |
| 316 if (has_visible_render_widget_with_touch_handler == | 317 if (has_visible_render_widget_with_touch_handler == |
| 317 MainThreadOnly().has_visible_render_widget_with_touch_handler) | 318 MainThreadOnly().has_visible_render_widget_with_touch_handler) |
| 318 return; | 319 return; |
| 319 | 320 |
| 320 MainThreadOnly().has_visible_render_widget_with_touch_handler = | 321 MainThreadOnly().has_visible_render_widget_with_touch_handler = |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 400 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread"); | 401 "RendererSchedulerImpl::DidAnimateForInputOnCompositorThread"); |
| 401 UpdateForInputEventOnCompositorThread( | 402 UpdateForInputEventOnCompositorThread( |
| 402 blink::WebInputEvent::Undefined, | 403 blink::WebInputEvent::Undefined, |
| 403 InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); | 404 InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| 404 } | 405 } |
| 405 | 406 |
| 406 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread( | 407 void RendererSchedulerImpl::UpdateForInputEventOnCompositorThread( |
| 407 blink::WebInputEvent::Type type, | 408 blink::WebInputEvent::Type type, |
| 408 InputEventState input_event_state) { | 409 InputEventState input_event_state) { |
| 409 base::AutoLock lock(any_thread_lock_); | 410 base::AutoLock lock(any_thread_lock_); |
| 410 base::TimeTicks now = helper_.tick_clock()->NowTicks(); | 411 base::TimeTicks now = helper_.task_queue_manager_delegate()->NowTicks(); |
| 411 | 412 |
| 412 // TODO(alexclarke): Move WebInputEventTraits where we can access it from here | 413 // TODO(alexclarke): Move WebInputEventTraits where we can access it from here |
| 413 // and record the name rather than the integer representation. | 414 // and record the name rather than the integer representation. |
| 414 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | 415 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
| 415 "RendererSchedulerImpl::UpdateForInputEventOnCompositorThread", | 416 "RendererSchedulerImpl::UpdateForInputEventOnCompositorThread", |
| 416 "type", static_cast<int>(type), "input_event_state", | 417 "type", static_cast<int>(type), "input_event_state", |
| 417 InputEventStateToString(input_event_state)); | 418 InputEventStateToString(input_event_state)); |
| 418 | 419 |
| 419 bool gesture_already_in_progress = InputSignalsSuggestGestureInProgress(now); | 420 bool gesture_already_in_progress = InputSignalsSuggestGestureInProgress(now); |
| 420 bool was_awaiting_touch_start_response = | 421 bool was_awaiting_touch_start_response = |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 } | 470 } |
| 470 | 471 |
| 471 void RendererSchedulerImpl::DidHandleInputEventOnMainThread( | 472 void RendererSchedulerImpl::DidHandleInputEventOnMainThread( |
| 472 const blink::WebInputEvent& web_input_event) { | 473 const blink::WebInputEvent& web_input_event) { |
| 473 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | 474 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
| 474 "RendererSchedulerImpl::DidHandleInputEventOnMainThread"); | 475 "RendererSchedulerImpl::DidHandleInputEventOnMainThread"); |
| 475 helper_.CheckOnValidThread(); | 476 helper_.CheckOnValidThread(); |
| 476 if (ShouldPrioritizeInputEvent(web_input_event)) { | 477 if (ShouldPrioritizeInputEvent(web_input_event)) { |
| 477 base::AutoLock lock(any_thread_lock_); | 478 base::AutoLock lock(any_thread_lock_); |
| 478 AnyThread().user_model.DidFinishProcessingInputEvent( | 479 AnyThread().user_model.DidFinishProcessingInputEvent( |
| 479 helper_.tick_clock()->NowTicks()); | 480 helper_.task_queue_manager_delegate()->NowTicks()); |
| 480 } | 481 } |
| 481 } | 482 } |
| 482 | 483 |
| 483 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() { | 484 bool RendererSchedulerImpl::IsHighPriorityWorkAnticipated() { |
| 484 helper_.CheckOnValidThread(); | 485 helper_.CheckOnValidThread(); |
| 485 if (helper_.IsShutdown()) | 486 if (helper_.IsShutdown()) |
| 486 return false; | 487 return false; |
| 487 | 488 |
| 488 MaybeUpdatePolicy(); | 489 MaybeUpdatePolicy(); |
| 489 // The touchstart and main-thread gesture use cases indicate a strong | 490 // The touchstart and main-thread gesture use cases indicate a strong |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 560 base::AutoLock lock(any_thread_lock_); | 561 base::AutoLock lock(any_thread_lock_); |
| 561 UpdatePolicyLocked(UpdateType::FORCE_UPDATE); | 562 UpdatePolicyLocked(UpdateType::FORCE_UPDATE); |
| 562 } | 563 } |
| 563 | 564 |
| 564 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) { | 565 void RendererSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) { |
| 565 helper_.CheckOnValidThread(); | 566 helper_.CheckOnValidThread(); |
| 566 any_thread_lock_.AssertAcquired(); | 567 any_thread_lock_.AssertAcquired(); |
| 567 if (helper_.IsShutdown()) | 568 if (helper_.IsShutdown()) |
| 568 return; | 569 return; |
| 569 | 570 |
| 570 base::TimeTicks now = helper_.tick_clock()->NowTicks(); | 571 base::TimeTicks now = helper_.task_queue_manager_delegate()->NowTicks(); |
| 571 policy_may_need_update_.SetWhileLocked(false); | 572 policy_may_need_update_.SetWhileLocked(false); |
| 572 | 573 |
| 573 base::TimeDelta expected_use_case_duration; | 574 base::TimeDelta expected_use_case_duration; |
| 574 UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration); | 575 UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration); |
| 575 MainThreadOnly().current_use_case = use_case; | 576 MainThreadOnly().current_use_case = use_case; |
| 576 | 577 |
| 577 base::TimeDelta touchstart_expected_flag_valid_for_duration; | 578 base::TimeDelta touchstart_expected_flag_valid_for_duration; |
| 578 bool touchstart_expected_soon = false; | 579 bool touchstart_expected_soon = false; |
| 579 if (MainThreadOnly().has_visible_render_widget_with_touch_handler) { | 580 if (MainThreadOnly().has_visible_render_widget_with_touch_handler) { |
| 580 touchstart_expected_soon = AnyThread().user_model.IsGestureExpectedSoon( | 581 touchstart_expected_soon = AnyThread().user_model.IsGestureExpectedSoon( |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 819 base::AutoLock lock(any_thread_lock_); | 820 base::AutoLock lock(any_thread_lock_); |
| 820 return AsValueLocked(optional_now); | 821 return AsValueLocked(optional_now); |
| 821 } | 822 } |
| 822 | 823 |
| 823 scoped_refptr<base::trace_event::ConvertableToTraceFormat> | 824 scoped_refptr<base::trace_event::ConvertableToTraceFormat> |
| 824 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const { | 825 RendererSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const { |
| 825 helper_.CheckOnValidThread(); | 826 helper_.CheckOnValidThread(); |
| 826 any_thread_lock_.AssertAcquired(); | 827 any_thread_lock_.AssertAcquired(); |
| 827 | 828 |
| 828 if (optional_now.is_null()) | 829 if (optional_now.is_null()) |
| 829 optional_now = helper_.tick_clock()->NowTicks(); | 830 optional_now = helper_.task_queue_manager_delegate()->NowTicks(); |
| 830 scoped_refptr<base::trace_event::TracedValue> state = | 831 scoped_refptr<base::trace_event::TracedValue> state = |
| 831 new base::trace_event::TracedValue(); | 832 new base::trace_event::TracedValue(); |
| 832 | 833 |
| 833 state->SetBoolean( | 834 state->SetBoolean( |
| 834 "has_visible_render_widget_with_touch_handler", | 835 "has_visible_render_widget_with_touch_handler", |
| 835 MainThreadOnly().has_visible_render_widget_with_touch_handler); | 836 MainThreadOnly().has_visible_render_widget_with_touch_handler); |
| 836 state->SetString("current_use_case", | 837 state->SetString("current_use_case", |
| 837 UseCaseToString(MainThreadOnly().current_use_case)); | 838 UseCaseToString(MainThreadOnly().current_use_case)); |
| 838 state->SetBoolean("loading_tasks_seem_expensive", | 839 state->SetBoolean("loading_tasks_seem_expensive", |
| 839 MainThreadOnly().loading_tasks_seem_expensive); | 840 MainThreadOnly().loading_tasks_seem_expensive); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 891 } | 892 } |
| 892 | 893 |
| 893 void RendererSchedulerImpl::OnIdlePeriodStarted() { | 894 void RendererSchedulerImpl::OnIdlePeriodStarted() { |
| 894 base::AutoLock lock(any_thread_lock_); | 895 base::AutoLock lock(any_thread_lock_); |
| 895 AnyThread().in_idle_period = true; | 896 AnyThread().in_idle_period = true; |
| 896 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); | 897 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); |
| 897 } | 898 } |
| 898 | 899 |
| 899 void RendererSchedulerImpl::OnIdlePeriodEnded() { | 900 void RendererSchedulerImpl::OnIdlePeriodEnded() { |
| 900 base::AutoLock lock(any_thread_lock_); | 901 base::AutoLock lock(any_thread_lock_); |
| 901 AnyThread().last_idle_period_end_time = helper_.tick_clock()->NowTicks(); | 902 AnyThread().last_idle_period_end_time = |
| 903 helper_.task_queue_manager_delegate()->NowTicks(); |
| 902 AnyThread().in_idle_period = false; | 904 AnyThread().in_idle_period = false; |
| 903 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); | 905 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); |
| 904 } | 906 } |
| 905 | 907 |
| 906 void RendererSchedulerImpl::AddPendingNavigation() { | 908 void RendererSchedulerImpl::AddPendingNavigation() { |
| 907 helper_.CheckOnValidThread(); | 909 helper_.CheckOnValidThread(); |
| 908 MainThreadOnly().navigation_task_expected_count++; | 910 MainThreadOnly().navigation_task_expected_count++; |
| 909 UpdatePolicy(); | 911 UpdatePolicy(); |
| 910 } | 912 } |
| 911 | 913 |
| 912 void RendererSchedulerImpl::RemovePendingNavigation() { | 914 void RendererSchedulerImpl::RemovePendingNavigation() { |
| 913 helper_.CheckOnValidThread(); | 915 helper_.CheckOnValidThread(); |
| 914 DCHECK_GT(MainThreadOnly().navigation_task_expected_count, 0); | 916 DCHECK_GT(MainThreadOnly().navigation_task_expected_count, 0); |
| 915 if (MainThreadOnly().navigation_task_expected_count > 0) | 917 if (MainThreadOnly().navigation_task_expected_count > 0) |
| 916 MainThreadOnly().navigation_task_expected_count--; | 918 MainThreadOnly().navigation_task_expected_count--; |
| 917 UpdatePolicy(); | 919 UpdatePolicy(); |
| 918 } | 920 } |
| 919 | 921 |
| 920 void RendererSchedulerImpl::OnNavigationStarted() { | 922 void RendererSchedulerImpl::OnNavigationStarted() { |
| 921 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), | 923 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), |
| 922 "RendererSchedulerImpl::OnNavigationStarted"); | 924 "RendererSchedulerImpl::OnNavigationStarted"); |
| 923 base::AutoLock lock(any_thread_lock_); | 925 base::AutoLock lock(any_thread_lock_); |
| 924 AnyThread().rails_loading_priority_deadline = | 926 AnyThread().rails_loading_priority_deadline = |
| 925 helper_.tick_clock()->NowTicks() + | 927 helper_.task_queue_manager_delegate()->NowTicks() + |
| 926 base::TimeDelta::FromMilliseconds( | 928 base::TimeDelta::FromMilliseconds( |
| 927 kRailsInitialLoadingPrioritizationMillis); | 929 kRailsInitialLoadingPrioritizationMillis); |
| 928 ResetForNavigationLocked(); | 930 ResetForNavigationLocked(); |
| 929 } | 931 } |
| 930 | 932 |
| 931 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now) const { | 933 bool RendererSchedulerImpl::HadAnIdlePeriodRecently(base::TimeTicks now) const { |
| 932 return (now - AnyThread().last_idle_period_end_time) <= | 934 return (now - AnyThread().last_idle_period_end_time) <= |
| 933 base::TimeDelta::FromMilliseconds( | 935 base::TimeDelta::FromMilliseconds( |
| 934 kIdlePeriodStarvationThresholdMillis); | 936 kIdlePeriodStarvationThresholdMillis); |
| 935 } | 937 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 951 MainThreadOnly().timer_queue_suspended_when_backgrounded = false; | 953 MainThreadOnly().timer_queue_suspended_when_backgrounded = false; |
| 952 ForceUpdatePolicy(); | 954 ForceUpdatePolicy(); |
| 953 } | 955 } |
| 954 | 956 |
| 955 void RendererSchedulerImpl::ResetForNavigationLocked() { | 957 void RendererSchedulerImpl::ResetForNavigationLocked() { |
| 956 helper_.CheckOnValidThread(); | 958 helper_.CheckOnValidThread(); |
| 957 any_thread_lock_.AssertAcquired(); | 959 any_thread_lock_.AssertAcquired(); |
| 958 MainThreadOnly().loading_task_cost_estimator.Clear(); | 960 MainThreadOnly().loading_task_cost_estimator.Clear(); |
| 959 MainThreadOnly().timer_task_cost_estimator.Clear(); | 961 MainThreadOnly().timer_task_cost_estimator.Clear(); |
| 960 MainThreadOnly().idle_time_estimator.Clear(); | 962 MainThreadOnly().idle_time_estimator.Clear(); |
| 961 AnyThread().user_model.Reset(helper_.tick_clock()->NowTicks()); | 963 AnyThread().user_model.Reset( |
| 964 helper_.task_queue_manager_delegate()->NowTicks()); |
| 962 MainThreadOnly().have_seen_a_begin_main_frame = false; | 965 MainThreadOnly().have_seen_a_begin_main_frame = false; |
| 963 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); | 966 UpdatePolicyLocked(UpdateType::MAY_EARLY_OUT_IF_POLICY_UNCHANGED); |
| 964 } | 967 } |
| 965 | 968 |
| 969 double RendererSchedulerImpl::currentTime() const { |
| 970 return helper_.task_queue_manager_delegate()->currentTime(); |
| 971 } |
| 972 |
| 973 double RendererSchedulerImpl::monotonicallyIncreasingTime() const { |
| 974 return helper_.task_queue_manager_delegate()->NowTicks().ToInternalValue() / |
| 975 static_cast<double>(base::Time::kMicrosecondsPerSecond); |
| 976 } |
| 977 |
| 966 } // namespace scheduler | 978 } // namespace scheduler |
| OLD | NEW |