Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 "base/task_scheduler/scheduler_single_thread_task_runner_manager.h" | 5 #include "base/task_scheduler/scheduler_single_thread_task_runner_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <string> | 9 #include <string> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 239 } // namespace | 239 } // namespace |
| 240 | 240 |
| 241 class SchedulerSingleThreadTaskRunnerManager::SchedulerSingleThreadTaskRunner | 241 class SchedulerSingleThreadTaskRunnerManager::SchedulerSingleThreadTaskRunner |
| 242 : public SingleThreadTaskRunner { | 242 : public SingleThreadTaskRunner { |
| 243 public: | 243 public: |
| 244 // Constructs a SchedulerSingleThreadTaskRunner that indirectly controls the | 244 // Constructs a SchedulerSingleThreadTaskRunner that indirectly controls the |
| 245 // lifetime of a dedicated |worker| for |traits|. | 245 // lifetime of a dedicated |worker| for |traits|. |
| 246 SchedulerSingleThreadTaskRunner( | 246 SchedulerSingleThreadTaskRunner( |
| 247 SchedulerSingleThreadTaskRunnerManager* const outer, | 247 SchedulerSingleThreadTaskRunnerManager* const outer, |
| 248 const TaskTraits& traits, | 248 const TaskTraits& traits, |
| 249 SchedulerWorker* worker) | 249 SchedulerWorker* worker, |
| 250 : outer_(outer), traits_(traits), worker_(worker) { | 250 SingleThreadTaskRunnerThreadMode thread_mode) |
| 251 : outer_(outer), | |
| 252 traits_(traits), | |
| 253 worker_(worker), | |
| 254 thread_mode_(thread_mode) { | |
| 251 DCHECK(outer_); | 255 DCHECK(outer_); |
| 252 DCHECK(worker_); | 256 DCHECK(worker_); |
| 253 } | 257 } |
| 254 | 258 |
| 255 // SingleThreadTaskRunner: | 259 // SingleThreadTaskRunner: |
| 256 bool PostDelayedTask(const tracked_objects::Location& from_here, | 260 bool PostDelayedTask(const tracked_objects::Location& from_here, |
| 257 OnceClosure closure, | 261 OnceClosure closure, |
| 258 TimeDelta delay) override { | 262 TimeDelta delay) override { |
| 259 auto task = MakeUnique<Task>(from_here, std::move(closure), traits_, delay); | 263 auto task = MakeUnique<Task>(from_here, std::move(closure), traits_, delay); |
| 260 task->single_thread_task_runner_ref = this; | 264 task->single_thread_task_runner_ref = this; |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 278 // Tasks are never nested within the task scheduler. | 282 // Tasks are never nested within the task scheduler. |
| 279 return PostDelayedTask(from_here, std::move(closure), delay); | 283 return PostDelayedTask(from_here, std::move(closure), delay); |
| 280 } | 284 } |
| 281 | 285 |
| 282 bool RunsTasksInCurrentSequence() const override { | 286 bool RunsTasksInCurrentSequence() const override { |
| 283 return GetDelegate()->RunsTasksInCurrentSequence(); | 287 return GetDelegate()->RunsTasksInCurrentSequence(); |
| 284 } | 288 } |
| 285 | 289 |
| 286 private: | 290 private: |
| 287 ~SchedulerSingleThreadTaskRunner() override { | 291 ~SchedulerSingleThreadTaskRunner() override { |
| 288 // Note: This will crash if SchedulerSingleThreadTaskRunnerManager is | 292 // Only unregister if this is a DEDICATED SingleThreadTaskRunner. SHARED |
| 289 // incorrectly destroyed first in tests (in production the TaskScheduler and | 293 // task runner SchedulerWorkers are managed separately as they are reused. |
| 290 // all of its state are intentionally leaked after | 294 if (thread_mode_ == SingleThreadTaskRunnerThreadMode::DEDICATED) { |
| 291 // TaskScheduler::Shutdown(). See ~SchedulerSingleThreadTaskRunnerManager() | 295 // Note: This will crash if SchedulerSingleThreadTaskRunnerManager is |
| 292 // for more details. | 296 // incorrectly destroyed first in tests (in production the TaskScheduler |
| 293 outer_->UnregisterSchedulerWorker(worker_); | 297 // and all of its state are intentionally leaked after |
| 298 // TaskScheduler::Shutdown(). See | |
| 299 // ~SchedulerSingleThreadTaskRunnerManager() for more details. | |
| 300 outer_->UnregisterSchedulerWorker(worker_); | |
| 301 } | |
| 294 } | 302 } |
| 295 | 303 |
| 296 void PostTaskNow(std::unique_ptr<Task> task) { | 304 void PostTaskNow(std::unique_ptr<Task> task) { |
| 297 scoped_refptr<Sequence> sequence = GetDelegate()->sequence(); | 305 scoped_refptr<Sequence> sequence = GetDelegate()->sequence(); |
| 298 // If |sequence| is null, then the thread is effectively gone (either | 306 // If |sequence| is null, then the thread is effectively gone (either |
| 299 // shutdown or joined). | 307 // shutdown or joined). |
| 300 if (!sequence) | 308 if (!sequence) |
| 301 return; | 309 return; |
| 302 | 310 |
| 303 const bool sequence_was_empty = sequence->PushTask(std::move(task)); | 311 const bool sequence_was_empty = sequence->PushTask(std::move(task)); |
| 304 if (sequence_was_empty) { | 312 if (sequence_was_empty) { |
| 305 GetDelegate()->ReEnqueueSequence(std::move(sequence)); | 313 GetDelegate()->ReEnqueueSequence(std::move(sequence)); |
| 306 worker_->WakeUp(); | 314 worker_->WakeUp(); |
| 307 } | 315 } |
| 308 } | 316 } |
| 309 | 317 |
| 310 SchedulerWorkerDelegate* GetDelegate() const { | 318 SchedulerWorkerDelegate* GetDelegate() const { |
| 311 return static_cast<SchedulerWorkerDelegate*>(worker_->delegate()); | 319 return static_cast<SchedulerWorkerDelegate*>(worker_->delegate()); |
| 312 } | 320 } |
| 313 | 321 |
| 314 SchedulerSingleThreadTaskRunnerManager* const outer_; | 322 SchedulerSingleThreadTaskRunnerManager* const outer_; |
| 315 const TaskTraits traits_; | 323 const TaskTraits traits_; |
| 316 SchedulerWorker* const worker_; | 324 SchedulerWorker* const worker_; |
| 325 const SingleThreadTaskRunnerThreadMode thread_mode_; | |
| 317 | 326 |
| 318 DISALLOW_COPY_AND_ASSIGN(SchedulerSingleThreadTaskRunner); | 327 DISALLOW_COPY_AND_ASSIGN(SchedulerSingleThreadTaskRunner); |
| 319 }; | 328 }; |
| 320 | 329 |
| 321 SchedulerSingleThreadTaskRunnerManager::SchedulerSingleThreadTaskRunnerManager( | 330 SchedulerSingleThreadTaskRunnerManager::SchedulerSingleThreadTaskRunnerManager( |
| 322 TaskTracker* task_tracker, | 331 TaskTracker* task_tracker, |
| 323 DelayedTaskManager* delayed_task_manager) | 332 DelayedTaskManager* delayed_task_manager) |
| 324 : task_tracker_(task_tracker), delayed_task_manager_(delayed_task_manager) { | 333 : task_tracker_(task_tracker), |
| 334 delayed_task_manager_(delayed_task_manager), | |
| 335 shared_scheduler_workers_ {} | |
| 336 #if defined(OS_WIN) | |
| 337 , | |
| 338 shared_com_scheduler_workers_ {} | |
|
gab
2017/05/25 18:47:00
Use member initialization for these two as mention
robliao
2017/05/30 20:01:06
Done.
| |
| 339 #endif // defined(OS_WIN) | |
| 340 { | |
| 325 DCHECK(task_tracker_); | 341 DCHECK(task_tracker_); |
| 326 DCHECK(delayed_task_manager_); | 342 DCHECK(delayed_task_manager_); |
| 343 static_assert( | |
| 344 arraysize(shared_scheduler_workers_) == ENVIRONMENT_COUNT, | |
|
fdoray
2017/05/25 17:02:59
The static_assert is redundant if this is defined
gab
2017/05/25 18:47:00
Agreed and with the initialization moving to membe
robliao
2017/05/30 20:01:06
Done. Keeping the one below though since that cert
| |
| 345 "The size of |shared_scheduler_workers_| must match ENVIRONMENT_COUNT"); | |
| 346 #if defined(OS_WIN) | |
| 347 static_assert(arraysize(shared_com_scheduler_workers_) == | |
| 348 arraysize(shared_scheduler_workers_), | |
| 349 "The size of |shared_com_scheduler_workers_| must match " | |
| 350 "|shared_scheduler_workers_|"); | |
| 351 #endif // defined(OS_WIN) | |
| 327 } | 352 } |
| 328 | 353 |
| 329 SchedulerSingleThreadTaskRunnerManager:: | 354 SchedulerSingleThreadTaskRunnerManager:: |
| 330 ~SchedulerSingleThreadTaskRunnerManager() { | 355 ~SchedulerSingleThreadTaskRunnerManager() { |
| 331 #if DCHECK_IS_ON() | 356 #if DCHECK_IS_ON() |
| 332 size_t workers_unregistered_during_join = | 357 size_t workers_unregistered_during_join = |
| 333 subtle::NoBarrier_Load(&workers_unregistered_during_join_); | 358 subtle::NoBarrier_Load(&workers_unregistered_during_join_); |
| 334 // Log an ERROR instead of DCHECK'ing as it's often useful to have both the | 359 // Log an ERROR instead of DCHECK'ing as it's often useful to have both the |
| 335 // stack trace of this call and the crash stack trace of the upcoming | 360 // stack trace of this call and the crash stack trace of the upcoming |
| 336 // out-of-order ~SchedulerSingleThreadTaskRunner() call to know what to flip. | 361 // out-of-order ~SchedulerSingleThreadTaskRunner() call to know what to flip. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 368 // workers are started as they are created. | 393 // workers are started as they are created. |
| 369 for (scoped_refptr<SchedulerWorker> worker : workers_to_start) { | 394 for (scoped_refptr<SchedulerWorker> worker : workers_to_start) { |
| 370 worker->Start(); | 395 worker->Start(); |
| 371 worker->WakeUp(); | 396 worker->WakeUp(); |
| 372 } | 397 } |
| 373 } | 398 } |
| 374 | 399 |
| 375 scoped_refptr<SingleThreadTaskRunner> | 400 scoped_refptr<SingleThreadTaskRunner> |
| 376 SchedulerSingleThreadTaskRunnerManager::CreateSingleThreadTaskRunnerWithTraits( | 401 SchedulerSingleThreadTaskRunnerManager::CreateSingleThreadTaskRunnerWithTraits( |
| 377 const std::string& name, | 402 const std::string& name, |
| 378 ThreadPriority priority_hint, | 403 const TaskTraits& traits, |
| 379 const TaskTraits& traits) { | 404 SingleThreadTaskRunnerThreadMode thread_mode) { |
| 380 return CreateSingleThreadTaskRunnerWithDelegate<SchedulerWorkerDelegate>( | 405 return CreateTaskRunnerWithTraitsImpl<SchedulerWorkerDelegate>(name, traits, |
| 381 name, priority_hint, traits); | 406 thread_mode); |
| 382 } | 407 } |
| 383 | 408 |
| 384 #if defined(OS_WIN) | 409 #if defined(OS_WIN) |
| 385 scoped_refptr<SingleThreadTaskRunner> | 410 scoped_refptr<SingleThreadTaskRunner> |
| 386 SchedulerSingleThreadTaskRunnerManager::CreateCOMSTATaskRunnerWithTraits( | 411 SchedulerSingleThreadTaskRunnerManager::CreateCOMSTATaskRunnerWithTraits( |
| 387 const std::string& name, | 412 const std::string& name, |
| 388 ThreadPriority priority_hint, | 413 const TaskTraits& traits, |
| 389 const TaskTraits& traits) { | 414 SingleThreadTaskRunnerThreadMode thread_mode) { |
| 390 return CreateSingleThreadTaskRunnerWithDelegate<SchedulerWorkerCOMDelegate>( | 415 return CreateTaskRunnerWithTraitsImpl<SchedulerWorkerCOMDelegate>( |
| 391 name, priority_hint, traits); | 416 name, traits, thread_mode); |
| 392 } | 417 } |
| 393 #endif // defined(OS_WIN) | 418 #endif // defined(OS_WIN) |
| 394 | 419 |
| 420 template <typename DelegateType> | |
| 421 scoped_refptr< | |
| 422 SchedulerSingleThreadTaskRunnerManager::SchedulerSingleThreadTaskRunner> | |
| 423 SchedulerSingleThreadTaskRunnerManager::CreateTaskRunnerWithTraitsImpl( | |
| 424 const std::string& name, | |
| 425 const TaskTraits& traits, | |
| 426 SingleThreadTaskRunnerThreadMode thread_mode) { | |
| 427 DCHECK(thread_mode != SingleThreadTaskRunnerThreadMode::SHARED || | |
| 428 !traits.with_base_sync_primitives()) | |
| 429 << "Using WithBaseSyncPrimitives() on a shared SingleThreadTaskRunner " | |
| 430 "may cause deadlocks. Either reevaluate your usage pattern or use " | |
|
gab
2017/05/25 18:47:00
"... your usage patterns (e.g. use SequencedTaskRu
robliao
2017/05/30 20:01:06
added the e.g. The singular usage is appropriate h
| |
| 431 "SingleThreadTaskRunnerThreadMode::DEDICATED."; | |
| 432 // To simplify the code, |dedicated_worker| is a local only variable that | |
| 433 // allows the code to treat both the DEDICATED and SHARED cases similarly for | |
| 434 // SingleThreadTaskRunnerThreadMode. In DEDICATED, the scoped_refptr is backed | |
| 435 // by a local variable and in SHARED, the scoped_refptr is backed by a member | |
| 436 // variable. | |
| 437 SchedulerWorker* dedicated_worker = nullptr; | |
| 438 SchedulerWorker*& worker = | |
| 439 thread_mode == SingleThreadTaskRunnerThreadMode::DEDICATED | |
| 440 ? dedicated_worker | |
| 441 : GetSharedSchedulerWorkerForTraits<DelegateType>(traits); | |
| 442 bool new_worker = false; | |
| 443 bool started; | |
| 444 { | |
| 445 AutoSchedulerLock auto_lock(lock_); | |
| 446 if (!worker) { | |
| 447 const auto& environment_params = | |
| 448 kEnvironmentParams[GetEnvironmentIndexForTraits(traits)]; | |
| 449 std::string processed_name = | |
| 450 thread_mode == SingleThreadTaskRunnerThreadMode::DEDICATED | |
| 451 ? name + environment_params.name_suffix | |
| 452 : "Shared" + name + environment_params.name_suffix; | |
| 453 worker = CreateAndRegisterSchedulerWorker<DelegateType>( | |
| 454 processed_name, environment_params.priority_hint); | |
| 455 new_worker = true; | |
| 456 } | |
| 457 started = started_; | |
| 458 } | |
| 459 | |
| 460 if (new_worker && started) | |
| 461 worker->Start(); | |
| 462 | |
| 463 return new SchedulerSingleThreadTaskRunner(this, traits, worker, thread_mode); | |
|
fdoray
2017/05/25 17:02:59
MakeRefCounted<SchedulerSingleThreadTaskRunner>
robliao
2017/05/30 20:01:06
Done.
| |
| 464 } | |
| 465 | |
| 395 void SchedulerSingleThreadTaskRunnerManager::JoinForTesting() { | 466 void SchedulerSingleThreadTaskRunnerManager::JoinForTesting() { |
| 467 ReleaseSharedSchedulerWorkers(); | |
| 468 | |
| 396 decltype(workers_) local_workers; | 469 decltype(workers_) local_workers; |
| 397 { | 470 { |
| 398 AutoSchedulerLock auto_lock(lock_); | 471 AutoSchedulerLock auto_lock(lock_); |
| 399 local_workers = std::move(workers_); | 472 local_workers = std::move(workers_); |
| 400 } | 473 } |
| 401 | 474 |
| 402 for (const auto& worker : local_workers) | 475 for (const auto& worker : local_workers) |
| 403 worker->JoinForTesting(); | 476 worker->JoinForTesting(); |
| 404 | 477 |
| 405 { | 478 { |
| 406 AutoSchedulerLock auto_lock(lock_); | 479 AutoSchedulerLock auto_lock(lock_); |
| 407 DCHECK(workers_.empty()) | 480 DCHECK(workers_.empty()) |
| 408 << "New worker(s) unexpectedly registered during join."; | 481 << "New worker(s) unexpectedly registered during join."; |
| 409 workers_ = std::move(local_workers); | 482 workers_ = std::move(local_workers); |
| 410 } | 483 } |
| 411 } | 484 } |
| 412 | 485 |
| 413 template <typename DelegateType> | |
| 414 scoped_refptr<SingleThreadTaskRunner> SchedulerSingleThreadTaskRunnerManager:: | |
| 415 CreateSingleThreadTaskRunnerWithDelegate(const std::string& name, | |
| 416 ThreadPriority priority_hint, | |
| 417 const TaskTraits& traits) { | |
| 418 return new SchedulerSingleThreadTaskRunner( | |
| 419 this, traits, | |
| 420 CreateAndRegisterSchedulerWorker<DelegateType>(name, priority_hint)); | |
| 421 } | |
| 422 | |
| 423 template <> | 486 template <> |
| 424 std::unique_ptr<SchedulerWorkerDelegate> | 487 std::unique_ptr<SchedulerWorkerDelegate> |
| 425 SchedulerSingleThreadTaskRunnerManager::CreateSchedulerWorkerDelegate< | 488 SchedulerSingleThreadTaskRunnerManager::CreateSchedulerWorkerDelegate< |
| 426 SchedulerWorkerDelegate>(const std::string& name, int id) { | 489 SchedulerWorkerDelegate>(const std::string& name, int id) { |
| 427 return MakeUnique<SchedulerWorkerDelegate>( | 490 return MakeUnique<SchedulerWorkerDelegate>( |
| 428 StringPrintf("TaskSchedulerSingleThread%s%d", name.c_str(), id)); | 491 StringPrintf("TaskSchedulerSingleThread%s%d", name.c_str(), id)); |
| 429 } | 492 } |
| 430 | 493 |
| 431 #if defined(OS_WIN) | 494 #if defined(OS_WIN) |
| 432 template <> | 495 template <> |
| 433 std::unique_ptr<SchedulerWorkerDelegate> | 496 std::unique_ptr<SchedulerWorkerDelegate> |
| 434 SchedulerSingleThreadTaskRunnerManager::CreateSchedulerWorkerDelegate< | 497 SchedulerSingleThreadTaskRunnerManager::CreateSchedulerWorkerDelegate< |
| 435 SchedulerWorkerCOMDelegate>(const std::string& name, int id) { | 498 SchedulerWorkerCOMDelegate>(const std::string& name, int id) { |
| 436 return MakeUnique<SchedulerWorkerCOMDelegate>( | 499 return MakeUnique<SchedulerWorkerCOMDelegate>( |
| 437 StringPrintf("TaskSchedulerSingleThreadCOMSTA%s%d", name.c_str(), id), | 500 StringPrintf("TaskSchedulerSingleThreadCOMSTA%s%d", name.c_str(), id), |
| 438 task_tracker_); | 501 task_tracker_); |
| 439 } | 502 } |
| 440 #endif // defined(OS_WIN) | 503 #endif // defined(OS_WIN) |
| 441 | 504 |
| 442 template <typename DelegateType> | 505 template <typename DelegateType> |
| 443 SchedulerWorker* | 506 SchedulerWorker* |
| 444 SchedulerSingleThreadTaskRunnerManager::CreateAndRegisterSchedulerWorker( | 507 SchedulerSingleThreadTaskRunnerManager::CreateAndRegisterSchedulerWorker( |
| 445 const std::string& name, | 508 const std::string& name, |
| 446 ThreadPriority priority_hint) { | 509 ThreadPriority priority_hint) { |
| 447 SchedulerWorker* worker; | 510 lock_.AssertAcquired(); |
| 448 bool start_worker; | 511 int id = next_worker_id_++; |
| 512 workers_.emplace_back(make_scoped_refptr(new SchedulerWorker( | |
| 513 priority_hint, CreateSchedulerWorkerDelegate<DelegateType>(name, id), | |
| 514 task_tracker_))); | |
| 515 return workers_.back().get(); | |
| 516 } | |
| 449 | 517 |
| 450 { | 518 template <> |
| 451 AutoSchedulerLock auto_lock(lock_); | 519 SchedulerWorker*& |
| 452 int id = next_worker_id_++; | 520 SchedulerSingleThreadTaskRunnerManager::GetSharedSchedulerWorkerForTraits< |
| 453 workers_.emplace_back(make_scoped_refptr(new SchedulerWorker( | 521 SchedulerWorkerDelegate>(const TaskTraits& traits) { |
| 454 priority_hint, CreateSchedulerWorkerDelegate<DelegateType>(name, id), | 522 return shared_scheduler_workers_[GetEnvironmentIndexForTraits(traits)]; |
| 455 task_tracker_))); | 523 } |
| 456 worker = workers_.back().get(); | |
| 457 start_worker = started_; | |
| 458 } | |
| 459 | 524 |
| 460 if (start_worker) | 525 #if defined(OS_WIN) |
| 461 worker->Start(); | 526 template <> |
| 462 | 527 SchedulerWorker*& |
| 463 return worker; | 528 SchedulerSingleThreadTaskRunnerManager::GetSharedSchedulerWorkerForTraits< |
| 529 SchedulerWorkerCOMDelegate>(const TaskTraits& traits) { | |
| 530 return shared_com_scheduler_workers_[GetEnvironmentIndexForTraits(traits)]; | |
| 464 } | 531 } |
| 532 #endif // defined(OS_WIN) | |
| 465 | 533 |
| 466 void SchedulerSingleThreadTaskRunnerManager::UnregisterSchedulerWorker( | 534 void SchedulerSingleThreadTaskRunnerManager::UnregisterSchedulerWorker( |
| 467 SchedulerWorker* worker) { | 535 SchedulerWorker* worker) { |
| 468 // Cleanup uses a SchedulerLock, so call Cleanup() after releasing | 536 // Cleanup uses a SchedulerLock, so call Cleanup() after releasing |
| 469 // |lock_|. | 537 // |lock_|. |
| 470 scoped_refptr<SchedulerWorker> worker_to_destroy; | 538 scoped_refptr<SchedulerWorker> worker_to_destroy; |
| 471 { | 539 { |
| 472 AutoSchedulerLock auto_lock(lock_); | 540 AutoSchedulerLock auto_lock(lock_); |
| 473 | 541 |
| 474 // We might be joining, so record that a worker was unregistered for | 542 // We might be joining, so record that a worker was unregistered for |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 485 [worker](const scoped_refptr<SchedulerWorker>& candidate) { | 553 [worker](const scoped_refptr<SchedulerWorker>& candidate) { |
| 486 return candidate.get() == worker; | 554 return candidate.get() == worker; |
| 487 }); | 555 }); |
| 488 DCHECK(worker_iter != workers_.end()); | 556 DCHECK(worker_iter != workers_.end()); |
| 489 worker_to_destroy = std::move(*worker_iter); | 557 worker_to_destroy = std::move(*worker_iter); |
| 490 workers_.erase(worker_iter); | 558 workers_.erase(worker_iter); |
| 491 } | 559 } |
| 492 worker_to_destroy->Cleanup(); | 560 worker_to_destroy->Cleanup(); |
| 493 } | 561 } |
| 494 | 562 |
| 563 void SchedulerSingleThreadTaskRunnerManager::ReleaseSharedSchedulerWorkers() { | |
| 564 decltype(shared_scheduler_workers_) local_shared_scheduler_workers; | |
| 565 #if defined(OS_WIN) | |
| 566 decltype(shared_com_scheduler_workers_) local_shared_com_scheduler_workers; | |
| 567 #endif | |
| 568 { | |
| 569 AutoSchedulerLock auto_lock(lock_); | |
| 570 for (size_t i = 0; i < arraysize(shared_scheduler_workers_); ++i) { | |
| 571 local_shared_scheduler_workers[i] = shared_scheduler_workers_[i]; | |
| 572 #if defined(OS_WIN) | |
| 573 local_shared_com_scheduler_workers[i] = shared_com_scheduler_workers_[i]; | |
|
gab
2017/05/25 18:47:00
Why do we need a local list? Doesn't seem like any
robliao
2017/05/30 20:01:06
The local list is needed to avoid unregistration w
| |
| 574 #endif | |
| 575 } | |
| 576 } | |
| 577 | |
| 578 for (size_t i = 0; i < arraysize(local_shared_scheduler_workers); ++i) { | |
| 579 if (local_shared_scheduler_workers[i]) | |
| 580 UnregisterSchedulerWorker(local_shared_scheduler_workers[i]); | |
| 581 #if defined(OS_WIN) | |
| 582 if (local_shared_com_scheduler_workers[i]) | |
| 583 UnregisterSchedulerWorker(local_shared_com_scheduler_workers[i]); | |
| 584 #endif | |
| 585 } | |
| 586 } | |
| 587 | |
| 495 } // namespace internal | 588 } // namespace internal |
| 496 } // namespace base | 589 } // namespace base |
| OLD | NEW |