Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Side by Side Diff: chrome/browser/chromeos/drive/drive_scheduler.cc

Issue 14493009: drive: Rename DriveScheduler to JobScheduler (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix indent Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/drive/drive_scheduler.h"
6
7 #include <math.h>
8
9 #include "base/message_loop.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/rand_util.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "chrome/browser/chromeos/drive/drive_file_system_util.h"
15 #include "chrome/browser/chromeos/drive/logging.h"
16 #include "chrome/browser/google_apis/drive_api_parser.h"
17 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/pref_names.h"
20 #include "content/public/browser/browser_thread.h"
21
22 using content::BrowserThread;
23
24 namespace drive {
25
26 namespace {
27 const int kMaxThrottleCount = 5;
28 }
29
30 const int DriveScheduler::kMaxJobCount[] = {
31 5, // METADATA_QUEUE
32 1, // FILE_QUEUE
33 };
34
35 DriveScheduler::QueueEntry::QueueEntry()
36 : job_id(-1),
37 context(DriveClientContext(USER_INITIATED)) {
38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
39 }
40
41 DriveScheduler::QueueEntry::~QueueEntry() {
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
43 }
44
45 bool DriveScheduler::QueueEntry::Compare(
46 const DriveScheduler::QueueEntry* left,
47 const DriveScheduler::QueueEntry* right) {
48 return (left->context.type < right->context.type);
49 }
50
51 DriveScheduler::DriveScheduler(
52 Profile* profile,
53 google_apis::DriveServiceInterface* drive_service)
54 : throttle_count_(0),
55 disable_throttling_(false),
56 drive_service_(drive_service),
57 uploader_(new google_apis::DriveUploader(drive_service)),
58 profile_(profile),
59 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61 for (int i = 0; i < NUM_QUEUES; ++i) {
62 jobs_running_[i] = 0;
63 }
64
65 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
66 }
67
68 DriveScheduler::~DriveScheduler() {
69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
70
71 size_t num_pending_jobs = 0;
72 size_t num_running_jobs = 0;
73 for (int i = 0; i < NUM_QUEUES; ++i) {
74 num_pending_jobs += queue_[i].size();
75 num_running_jobs += jobs_running_[i];
76 }
77 DCHECK_EQ(num_pending_jobs + num_running_jobs, job_map_.size());
78
79 for (int i = 0; i < NUM_QUEUES; ++i) {
80 STLDeleteElements(&queue_[i]);
81 }
82 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
83 }
84
85 std::vector<JobInfo> DriveScheduler::GetJobInfoList() {
86 std::vector<JobInfo> job_info_list;
87 for (JobIDMap::iterator iter(&job_map_); !iter.IsAtEnd(); iter.Advance())
88 job_info_list.push_back(*iter.GetCurrentValue());
89 return job_info_list;
90 }
91
92 void DriveScheduler::AddObserver(JobListObserver* observer) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94 observer_list_.AddObserver(observer);
95 }
96
97 void DriveScheduler::RemoveObserver(JobListObserver* observer) {
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
99 observer_list_.RemoveObserver(observer);
100 }
101
102 void DriveScheduler::CancelJob(JobID job_id) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104
105 // TODO(kinaba): Move the cancellation feature from DriveService
106 // to DriveScheduler. In particular, implement cancel based on job_id.
107 // crbug.com/231029
108 JobInfo* info = job_map_.Lookup(job_id);
109 if (info)
110 drive_service_->CancelForFilePath(info->file_path);
111 }
112
113 void DriveScheduler::CancelAllJobs() {
114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
115
116 // TODO(kinaba): Move the cancellation feature from DriveService
117 // to DriveScheduler.
118 drive_service_->CancelAll();
119 }
120
121 void DriveScheduler::GetAccountMetadata(
122 const google_apis::GetAccountMetadataCallback& callback) {
123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
124 DCHECK(!callback.is_null());
125
126 scoped_ptr<QueueEntry> new_job(new QueueEntry);
127 new_job->get_account_metadata_callback = callback;
128
129 StartNewJob(new_job.Pass(), TYPE_GET_ACCOUNT_METADATA);
130 }
131
132 void DriveScheduler::GetAboutResource(
133 const google_apis::GetAboutResourceCallback& callback) {
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
135 DCHECK(!callback.is_null());
136
137 scoped_ptr<QueueEntry> new_job(new QueueEntry);
138 new_job->get_about_resource_callback = callback;
139
140 StartNewJob(new_job.Pass(), TYPE_GET_ABOUT_RESOURCE);
141 }
142
143 void DriveScheduler::GetAppList(
144 const google_apis::GetAppListCallback& callback) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
146 DCHECK(!callback.is_null());
147
148 scoped_ptr<QueueEntry> new_job(new QueueEntry);
149 new_job->get_app_list_callback = callback;
150
151 StartNewJob(new_job.Pass(), TYPE_GET_APP_LIST);
152 }
153
154 void DriveScheduler::GetAllResourceList(
155 const google_apis::GetResourceListCallback& callback) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
157 DCHECK(!callback.is_null());
158
159 scoped_ptr<QueueEntry> new_job(new QueueEntry);
160 new_job->get_resource_list_callback = callback;
161
162 StartNewJob(new_job.Pass(), TYPE_GET_ALL_RESOURCE_LIST);
163 }
164
165 void DriveScheduler::GetResourceListInDirectory(
166 const std::string& directory_resource_id,
167 const google_apis::GetResourceListCallback& callback) {
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
169 DCHECK(!callback.is_null());
170
171 scoped_ptr<QueueEntry> new_job(new QueueEntry);
172 new_job->directory_resource_id = directory_resource_id;
173 new_job->get_resource_list_callback = callback;
174
175 StartNewJob(new_job.Pass(), TYPE_GET_RESOURCE_LIST_IN_DIRECTORY);
176 }
177
178 void DriveScheduler::Search(
179 const std::string& search_query,
180 const google_apis::GetResourceListCallback& callback) {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
182 DCHECK(!callback.is_null());
183
184 scoped_ptr<QueueEntry> new_job(new QueueEntry);
185 new_job->search_query = search_query;
186 new_job->get_resource_list_callback = callback;
187
188 StartNewJob(new_job.Pass(), TYPE_SEARCH);
189 }
190
191 void DriveScheduler::GetChangeList(
192 int64 start_changestamp,
193 const google_apis::GetResourceListCallback& callback) {
194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195 DCHECK(!callback.is_null());
196
197 scoped_ptr<QueueEntry> new_job(new QueueEntry);
198 new_job->start_changestamp = start_changestamp;
199 new_job->get_resource_list_callback = callback;
200
201 StartNewJob(new_job.Pass(), TYPE_GET_CHANGE_LIST);
202 }
203
204 void DriveScheduler::ContinueGetResourceList(
205 const GURL& feed_url,
206 const google_apis::GetResourceListCallback& callback) {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208 DCHECK(!callback.is_null());
209
210 scoped_ptr<QueueEntry> new_job(new QueueEntry);
211 new_job->feed_url = feed_url;
212 new_job->get_resource_list_callback = callback;
213
214 StartNewJob(new_job.Pass(), TYPE_CONTINUE_GET_RESOURCE_LIST);
215 }
216
217 void DriveScheduler::GetResourceEntry(
218 const std::string& resource_id,
219 const DriveClientContext& context,
220 const google_apis::GetResourceEntryCallback& callback) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
222 DCHECK(!callback.is_null());
223
224 scoped_ptr<QueueEntry> new_job(new QueueEntry);
225 new_job->resource_id = resource_id;
226 new_job->context = context;
227 new_job->get_resource_entry_callback = callback;
228
229 StartNewJob(new_job.Pass(), TYPE_GET_RESOURCE_ENTRY);
230 }
231
232 void DriveScheduler::DeleteResource(
233 const std::string& resource_id,
234 const google_apis::EntryActionCallback& callback) {
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236 DCHECK(!callback.is_null());
237
238 scoped_ptr<QueueEntry> new_job(new QueueEntry);
239 new_job->resource_id = resource_id;
240 new_job->entry_action_callback = callback;
241
242 StartNewJob(new_job.Pass(), TYPE_DELETE_RESOURCE);
243 }
244
245
246 void DriveScheduler::CopyHostedDocument(
247 const std::string& resource_id,
248 const std::string& new_name,
249 const google_apis::GetResourceEntryCallback& callback) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251 DCHECK(!callback.is_null());
252
253 scoped_ptr<QueueEntry> new_job(new QueueEntry);
254 new_job->resource_id = resource_id;
255 new_job->new_name = new_name;
256 new_job->get_resource_entry_callback = callback;
257
258 StartNewJob(new_job.Pass(), TYPE_COPY_HOSTED_DOCUMENT);
259 }
260
261 void DriveScheduler::RenameResource(
262 const std::string& resource_id,
263 const std::string& new_name,
264 const google_apis::EntryActionCallback& callback) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266 DCHECK(!callback.is_null());
267
268 scoped_ptr<QueueEntry> new_job(new QueueEntry);
269 new_job->resource_id = resource_id;
270 new_job->new_name = new_name;
271 new_job->entry_action_callback = callback;
272
273 StartNewJob(new_job.Pass(), TYPE_RENAME_RESOURCE);
274 }
275
276 void DriveScheduler::AddResourceToDirectory(
277 const std::string& parent_resource_id,
278 const std::string& resource_id,
279 const google_apis::EntryActionCallback& callback) {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
281 DCHECK(!callback.is_null());
282
283 scoped_ptr<QueueEntry> new_job(new QueueEntry);
284 new_job->parent_resource_id = parent_resource_id;
285 new_job->resource_id = resource_id;
286 new_job->entry_action_callback = callback;
287
288 StartNewJob(new_job.Pass(), TYPE_ADD_RESOURCE_TO_DIRECTORY);
289 }
290
291 void DriveScheduler::RemoveResourceFromDirectory(
292 const std::string& parent_resource_id,
293 const std::string& resource_id,
294 const google_apis::EntryActionCallback& callback) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
296
297 scoped_ptr<QueueEntry> new_job(new QueueEntry);
298 new_job->parent_resource_id = parent_resource_id;
299 new_job->resource_id = resource_id;
300 new_job->entry_action_callback = callback;
301
302 StartNewJob(new_job.Pass(), TYPE_REMOVE_RESOURCE_FROM_DIRECTORY);
303 }
304
305 void DriveScheduler::AddNewDirectory(
306 const std::string& parent_resource_id,
307 const std::string& directory_name,
308 const google_apis::GetResourceEntryCallback& callback) {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
310
311 scoped_ptr<QueueEntry> new_job(new QueueEntry);
312 new_job->parent_resource_id = parent_resource_id;
313 new_job->directory_name = directory_name;
314 new_job->get_resource_entry_callback = callback;
315
316 StartNewJob(new_job.Pass(), TYPE_ADD_NEW_DIRECTORY);
317 }
318
319 void DriveScheduler::DownloadFile(
320 const base::FilePath& virtual_path,
321 const base::FilePath& local_cache_path,
322 const GURL& download_url,
323 const DriveClientContext& context,
324 const google_apis::DownloadActionCallback& download_action_callback,
325 const google_apis::GetContentCallback& get_content_callback) {
326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
327
328 scoped_ptr<QueueEntry> new_job(new QueueEntry);
329 new_job->drive_file_path = virtual_path;
330 new_job->local_file_path = local_cache_path;
331 new_job->download_url = download_url;
332 new_job->context = context;
333 new_job->download_action_callback = download_action_callback;
334 new_job->get_content_callback = get_content_callback;
335
336 StartNewJob(new_job.Pass(), TYPE_DOWNLOAD_FILE);
337 }
338
339 void DriveScheduler::UploadNewFile(
340 const std::string& parent_resource_id,
341 const base::FilePath& drive_file_path,
342 const base::FilePath& local_file_path,
343 const std::string& title,
344 const std::string& content_type,
345 const DriveClientContext& context,
346 const google_apis::UploadCompletionCallback& callback) {
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
348
349 scoped_ptr<QueueEntry> new_job(new QueueEntry);
350 new_job->resource_id = parent_resource_id;
351 new_job->drive_file_path = drive_file_path;
352 new_job->local_file_path = local_file_path;
353 new_job->title = title;
354 new_job->content_type = content_type;
355 new_job->upload_completion_callback = callback;
356 new_job->context = context;
357
358 StartNewJob(new_job.Pass(), TYPE_UPLOAD_NEW_FILE);
359 }
360
361 void DriveScheduler::UploadExistingFile(
362 const std::string& resource_id,
363 const base::FilePath& drive_file_path,
364 const base::FilePath& local_file_path,
365 const std::string& content_type,
366 const std::string& etag,
367 const DriveClientContext& context,
368 const google_apis::UploadCompletionCallback& upload_completion_callback) {
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
370
371 scoped_ptr<QueueEntry> new_job(new QueueEntry);
372 new_job->resource_id = resource_id;
373 new_job->drive_file_path = drive_file_path;
374 new_job->local_file_path = local_file_path;
375 new_job->content_type = content_type;
376 new_job->etag = etag;
377 new_job->upload_completion_callback = upload_completion_callback;
378 new_job->context = context;
379
380 StartNewJob(new_job.Pass(), TYPE_UPLOAD_EXISTING_FILE);
381 }
382
383 void DriveScheduler::StartNewJob(scoped_ptr<QueueEntry> job, JobType type) {
384 // job_info is owned by job_map_ and released when it is removed in OnJobDone.
385 JobInfo* job_info = new JobInfo(type);
386 job->job_id = job_info->job_id = job_map_.Add(job_info);
387 job_info->file_path = job->drive_file_path;
388
389 QueueJob(job.Pass());
390 NotifyJobAdded(*job_info);
391 StartJobLoop(GetJobQueueType(type));
392 }
393
394 void DriveScheduler::QueueJob(scoped_ptr<QueueEntry> job) {
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
396
397 JobInfo* job_info = job_map_.Lookup(job->job_id);
398 DCHECK(job_info);
399 util::Log("Queue job: %s [%d]",
400 JobTypeToString(job_info->job_type).c_str(),
401 job_info->job_id);
402
403 QueueType queue_type = GetJobQueueType(job_info->job_type);
404 std::list<QueueEntry*>& queue = queue_[queue_type];
405
406 queue.push_back(job.release());
407 queue.sort(&QueueEntry::Compare);
408 }
409
410 void DriveScheduler::StartJobLoop(QueueType queue_type) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
412
413 if (jobs_running_[queue_type] < kMaxJobCount[queue_type])
414 DoJobLoop(queue_type);
415 }
416
417 void DriveScheduler::DoJobLoop(QueueType queue_type) {
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
419
420 if (queue_[queue_type].empty()) {
421 return;
422 }
423
424 // Check if we should defer based on the first item in the queue
425 if (ShouldStopJobLoop(queue_type, queue_[queue_type].front()->context)) {
426 return;
427 }
428
429 // Increment the number of jobs.
430 ++jobs_running_[queue_type];
431
432 // Should copy before calling queue_.pop_front().
433 scoped_ptr<QueueEntry> queue_entry(queue_[queue_type].front());
434 queue_[queue_type].pop_front();
435
436 JobInfo* job_info = job_map_.Lookup(queue_entry->job_id);
437 DCHECK(job_info);
438 job_info->state = STATE_RUNNING;
439 job_info->start_time = base::Time::Now();
440 NotifyJobUpdated(*job_info);
441
442 // The some arguments are evaluated after bind, so we copy the pointer to the
443 // QueueEntry
444 QueueEntry* entry = queue_entry.get();
445 util::Log("Start job: %s [%d]",
446 JobTypeToString(job_info->job_type).c_str(),
447 job_info->job_id);
448
449 switch (job_info->job_type) {
450 case TYPE_GET_ABOUT_RESOURCE: {
451 drive_service_->GetAboutResource(
452 base::Bind(&DriveScheduler::OnGetAboutResourceJobDone,
453 weak_ptr_factory_.GetWeakPtr(),
454 base::Passed(&queue_entry)));
455 }
456 break;
457
458 case TYPE_GET_ACCOUNT_METADATA: {
459 drive_service_->GetAccountMetadata(
460 base::Bind(&DriveScheduler::OnGetAccountMetadataJobDone,
461 weak_ptr_factory_.GetWeakPtr(),
462 base::Passed(&queue_entry)));
463 }
464 break;
465
466 case TYPE_GET_APP_LIST: {
467 drive_service_->GetAppList(
468 base::Bind(&DriveScheduler::OnGetAppListJobDone,
469 weak_ptr_factory_.GetWeakPtr(),
470 base::Passed(&queue_entry)));
471 }
472 break;
473
474 case TYPE_GET_ALL_RESOURCE_LIST: {
475 drive_service_->GetAllResourceList(
476 base::Bind(&DriveScheduler::OnGetResourceListJobDone,
477 weak_ptr_factory_.GetWeakPtr(),
478 base::Passed(&queue_entry)));
479 }
480 break;
481
482 case TYPE_GET_RESOURCE_LIST_IN_DIRECTORY: {
483 drive_service_->GetResourceListInDirectory(
484 entry->directory_resource_id,
485 base::Bind(&DriveScheduler::OnGetResourceListJobDone,
486 weak_ptr_factory_.GetWeakPtr(),
487 base::Passed(&queue_entry)));
488 }
489 break;
490
491 case TYPE_SEARCH: {
492 drive_service_->Search(
493 entry->search_query,
494 base::Bind(&DriveScheduler::OnGetResourceListJobDone,
495 weak_ptr_factory_.GetWeakPtr(),
496 base::Passed(&queue_entry)));
497 }
498 break;
499
500 case TYPE_GET_CHANGE_LIST: {
501 drive_service_->GetChangeList(
502 entry->start_changestamp,
503 base::Bind(&DriveScheduler::OnGetResourceListJobDone,
504 weak_ptr_factory_.GetWeakPtr(),
505 base::Passed(&queue_entry)));
506 }
507 break;
508
509 case TYPE_CONTINUE_GET_RESOURCE_LIST: {
510 drive_service_->ContinueGetResourceList(
511 entry->feed_url,
512 base::Bind(&DriveScheduler::OnGetResourceListJobDone,
513 weak_ptr_factory_.GetWeakPtr(),
514 base::Passed(&queue_entry)));
515 }
516 break;
517
518 case TYPE_GET_RESOURCE_ENTRY: {
519 drive_service_->GetResourceEntry(
520 entry->resource_id,
521 base::Bind(&DriveScheduler::OnGetResourceEntryJobDone,
522 weak_ptr_factory_.GetWeakPtr(),
523 base::Passed(&queue_entry)));
524 }
525 break;
526
527 case TYPE_DELETE_RESOURCE: {
528 drive_service_->DeleteResource(
529 entry->resource_id,
530 "", // etag
531 base::Bind(&DriveScheduler::OnEntryActionJobDone,
532 weak_ptr_factory_.GetWeakPtr(),
533 base::Passed(&queue_entry)));
534 }
535 break;
536
537
538 case TYPE_COPY_HOSTED_DOCUMENT: {
539 drive_service_->CopyHostedDocument(
540 entry->resource_id,
541 entry->new_name,
542 base::Bind(&DriveScheduler::OnGetResourceEntryJobDone,
543 weak_ptr_factory_.GetWeakPtr(),
544 base::Passed(&queue_entry)));
545 }
546 break;
547
548 case TYPE_RENAME_RESOURCE: {
549 drive_service_->RenameResource(
550 entry->resource_id,
551 entry->new_name,
552 base::Bind(&DriveScheduler::OnEntryActionJobDone,
553 weak_ptr_factory_.GetWeakPtr(),
554 base::Passed(&queue_entry)));
555 }
556 break;
557
558 case TYPE_ADD_RESOURCE_TO_DIRECTORY: {
559 drive_service_->AddResourceToDirectory(
560 entry->parent_resource_id,
561 entry->resource_id,
562 base::Bind(&DriveScheduler::OnEntryActionJobDone,
563 weak_ptr_factory_.GetWeakPtr(),
564 base::Passed(&queue_entry)));
565 }
566 break;
567
568 case TYPE_REMOVE_RESOURCE_FROM_DIRECTORY: {
569 drive_service_->RemoveResourceFromDirectory(
570 entry->parent_resource_id,
571 entry->resource_id,
572 base::Bind(&DriveScheduler::OnEntryActionJobDone,
573 weak_ptr_factory_.GetWeakPtr(),
574 base::Passed(&queue_entry)));
575 }
576 break;
577
578 case TYPE_ADD_NEW_DIRECTORY: {
579 drive_service_->AddNewDirectory(
580 entry->parent_resource_id,
581 entry->directory_name,
582 base::Bind(&DriveScheduler::OnGetResourceEntryJobDone,
583 weak_ptr_factory_.GetWeakPtr(),
584 base::Passed(&queue_entry)));
585 }
586 break;
587
588 case TYPE_DOWNLOAD_FILE: {
589 drive_service_->DownloadFile(
590 entry->drive_file_path,
591 entry->local_file_path,
592 entry->download_url,
593 base::Bind(&DriveScheduler::OnDownloadActionJobDone,
594 weak_ptr_factory_.GetWeakPtr(),
595 base::Passed(&queue_entry)),
596 entry->get_content_callback,
597 base::Bind(&DriveScheduler::UpdateProgress,
598 weak_ptr_factory_.GetWeakPtr(),
599 job_info->job_id));
600 }
601 break;
602
603 case TYPE_UPLOAD_NEW_FILE: {
604 uploader_->UploadNewFile(
605 entry->resource_id,
606 entry->drive_file_path,
607 entry->local_file_path,
608 entry->title,
609 entry->content_type,
610 base::Bind(&DriveScheduler::OnUploadCompletionJobDone,
611 weak_ptr_factory_.GetWeakPtr(),
612 base::Passed(&queue_entry)),
613 base::Bind(&DriveScheduler::UpdateProgress,
614 weak_ptr_factory_.GetWeakPtr(),
615 job_info->job_id));
616 }
617 break;
618
619 case TYPE_UPLOAD_EXISTING_FILE: {
620 uploader_->UploadExistingFile(
621 entry->resource_id,
622 entry->drive_file_path,
623 entry->local_file_path,
624 entry->content_type,
625 entry->etag,
626 base::Bind(&DriveScheduler::OnUploadCompletionJobDone,
627 weak_ptr_factory_.GetWeakPtr(),
628 base::Passed(&queue_entry)),
629 base::Bind(&DriveScheduler::UpdateProgress,
630 weak_ptr_factory_.GetWeakPtr(),
631 job_info->job_id));
632 }
633 break;
634
635 // There is no default case so that there will be a compiler error if a type
636 // is added but unhandled.
637 }
638 }
639
640 bool DriveScheduler::ShouldStopJobLoop(QueueType queue_type,
641 const DriveClientContext& context) {
642 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
643
644 // Should stop if the gdata feature was disabled while running the fetch
645 // loop.
646 if (profile_->GetPrefs()->GetBoolean(prefs::kDisableDrive))
647 return true;
648
649 // Should stop if the network is not online.
650 if (net::NetworkChangeNotifier::IsOffline())
651 return true;
652
653 // Should stop background jobs if the current connection is on cellular
654 // network, and fetching is disabled over cellular.
655 bool should_stop_on_cellular_network = false;
656 switch (context.type) {
657 case USER_INITIATED:
658 should_stop_on_cellular_network = false;
659 break;
660 case BACKGROUND:
661 case PREFETCH:
662 should_stop_on_cellular_network = (queue_type == FILE_QUEUE);
663 break;
664 }
665 if (should_stop_on_cellular_network &&
666 profile_->GetPrefs()->GetBoolean(prefs::kDisableDriveOverCellular) &&
667 net::NetworkChangeNotifier::IsConnectionCellular(
668 net::NetworkChangeNotifier::GetConnectionType())) {
669 return true;
670 }
671
672 return false;
673 }
674
675 void DriveScheduler::ThrottleAndContinueJobLoop(QueueType queue_type) {
676 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
677
678 if (throttle_count_ < kMaxThrottleCount)
679 throttle_count_++;
680
681 base::TimeDelta delay;
682 if (disable_throttling_) {
683 delay = base::TimeDelta::FromSeconds(0);
684 } else {
685 delay =
686 base::TimeDelta::FromSeconds(pow(2, throttle_count_ - 1)) +
687 base::TimeDelta::FromMilliseconds(base::RandInt(0, 1000));
688 }
689 VLOG(1) << "Throttling for " << delay.InMillisecondsF();
690
691 const bool posted = base::MessageLoopProxy::current()->PostDelayedTask(
692 FROM_HERE,
693 base::Bind(&DriveScheduler::DoJobLoop,
694 weak_ptr_factory_.GetWeakPtr(),
695 queue_type),
696 delay);
697 DCHECK(posted);
698 }
699
700 void DriveScheduler::ResetThrottleAndContinueJobLoop(QueueType queue_type) {
701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
702
703 // Post a task to continue the job loop. This allows us to finish handling
704 // the current job before starting the next one.
705 throttle_count_ = 0;
706 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
707 base::Bind(&DriveScheduler::DoJobLoop,
708 weak_ptr_factory_.GetWeakPtr(),
709 queue_type));
710 }
711
712 scoped_ptr<DriveScheduler::QueueEntry> DriveScheduler::OnJobDone(
713 scoped_ptr<DriveScheduler::QueueEntry> queue_entry,
714 FileError error) {
715 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
716
717 JobInfo* job_info = job_map_.Lookup(queue_entry->job_id);
718 DCHECK(job_info);
719 QueueType queue_type = GetJobQueueType(job_info->job_type);
720
721 const base::TimeDelta elapsed = base::Time::Now() - job_info->start_time;
722 util::Log("Job done: %s [%d] => %s (elapsed time: %sms)",
723 JobTypeToString(job_info->job_type).c_str(),
724 job_info->job_id,
725 FileErrorToString(error).c_str(),
726 base::Int64ToString(elapsed.InMilliseconds()).c_str());
727
728 // Decrement the number of jobs for this queue.
729 --jobs_running_[queue_type];
730
731 // Retry, depending on the error.
732 if (error == FILE_ERROR_THROTTLED) {
733 job_info->state = STATE_RETRY;
734 NotifyJobUpdated(*job_info);
735
736 // Requeue the job.
737 QueueJob(queue_entry.Pass());
738
739 ThrottleAndContinueJobLoop(queue_type);
740
741 return scoped_ptr<DriveScheduler::QueueEntry>();
742 } else {
743 NotifyJobDone(*job_info, error);
744 // The job has finished, no retry will happen in the scheduler. Now we can
745 // remove the job info from the map. This is the only place of the removal.
746 job_map_.Remove(queue_entry->job_id);
747
748 ResetThrottleAndContinueJobLoop(queue_type);
749
750 // Send the entry back.
751 return queue_entry.Pass();
752 }
753 }
754
755 void DriveScheduler::OnGetResourceListJobDone(
756 scoped_ptr<DriveScheduler::QueueEntry> queue_entry,
757 google_apis::GDataErrorCode error,
758 scoped_ptr<google_apis::ResourceList> resource_list) {
759 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
760
761 FileError drive_error(util::GDataToFileError(error));
762
763 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
764
765 if (!queue_entry)
766 return;
767
768 // Handle the callback.
769 base::MessageLoopProxy::current()->PostTask(
770 FROM_HERE,
771 base::Bind(queue_entry->get_resource_list_callback,
772 error,
773 base::Passed(&resource_list)));
774 }
775
776 void DriveScheduler::OnGetResourceEntryJobDone(
777 scoped_ptr<DriveScheduler::QueueEntry> queue_entry,
778 google_apis::GDataErrorCode error,
779 scoped_ptr<google_apis::ResourceEntry> entry) {
780 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
781
782 FileError drive_error(util::GDataToFileError(error));
783
784 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
785
786 if (!queue_entry)
787 return;
788
789 // Handle the callback.
790 base::MessageLoopProxy::current()->PostTask(
791 FROM_HERE,
792 base::Bind(queue_entry->get_resource_entry_callback,
793 error,
794 base::Passed(&entry)));
795 }
796
797 void DriveScheduler::OnGetAboutResourceJobDone(
798 scoped_ptr<QueueEntry> queue_entry,
799 google_apis::GDataErrorCode error,
800 scoped_ptr<google_apis::AboutResource> about_resource) {
801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
802
803 FileError drive_error(util::GDataToFileError(error));
804
805 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
806
807 if (!queue_entry)
808 return;
809
810 // Handle the callback.
811 queue_entry->get_about_resource_callback.Run(error, about_resource.Pass());
812 }
813
814 void DriveScheduler::OnGetAccountMetadataJobDone(
815 scoped_ptr<QueueEntry> queue_entry,
816 google_apis::GDataErrorCode error,
817 scoped_ptr<google_apis::AccountMetadata> account_metadata) {
818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
819
820 FileError drive_error(util::GDataToFileError(error));
821
822 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
823
824 if (!queue_entry)
825 return;
826
827 // Handle the callback.
828 queue_entry->get_account_metadata_callback.Run(error,
829 account_metadata.Pass());
830 }
831
832 void DriveScheduler::OnGetAppListJobDone(
833 scoped_ptr<DriveScheduler::QueueEntry> queue_entry,
834 google_apis::GDataErrorCode error,
835 scoped_ptr<google_apis::AppList> app_list) {
836 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
837
838 FileError drive_error(util::GDataToFileError(error));
839
840 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
841
842 if (!queue_entry)
843 return;
844
845 // Handle the callback.
846 queue_entry->get_app_list_callback.Run(error, app_list.Pass());
847 }
848
849 void DriveScheduler::OnEntryActionJobDone(
850 scoped_ptr<DriveScheduler::QueueEntry> queue_entry,
851 google_apis::GDataErrorCode error) {
852 FileError drive_error(util::GDataToFileError(error));
853
854 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
855
856 if (!queue_entry)
857 return;
858
859 // Handle the callback.
860 DCHECK(!queue_entry->entry_action_callback.is_null());
861 queue_entry->entry_action_callback.Run(error);
862 }
863
864 void DriveScheduler::OnDownloadActionJobDone(
865 scoped_ptr<DriveScheduler::QueueEntry> queue_entry,
866 google_apis::GDataErrorCode error,
867 const base::FilePath& temp_file) {
868 FileError drive_error(util::GDataToFileError(error));
869
870 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
871
872 if (!queue_entry)
873 return;
874
875 // Handle the callback.
876 DCHECK(!queue_entry->download_action_callback.is_null());
877 queue_entry->download_action_callback.Run(error, temp_file);
878 }
879
880 void DriveScheduler::OnUploadCompletionJobDone(
881 scoped_ptr<QueueEntry> queue_entry,
882 google_apis::DriveUploadError error,
883 const base::FilePath& drive_path,
884 const base::FilePath& file_path,
885 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
886 FileError drive_error(DriveUploadErrorToFileError(error));
887
888 queue_entry = OnJobDone(queue_entry.Pass(), drive_error);
889
890 if (!queue_entry)
891 return;
892
893 // Handle the callback.
894 DCHECK(!queue_entry->upload_completion_callback.is_null());
895 queue_entry->upload_completion_callback.Run(
896 error, drive_path, file_path, resource_entry.Pass());
897 }
898
899 void DriveScheduler::UpdateProgress(JobID job_id, int64 progress, int64 total) {
900 JobInfo* job_info = job_map_.Lookup(job_id);
901 DCHECK(job_info);
902
903 job_info->num_completed_bytes = progress;
904 job_info->num_total_bytes = total;
905 NotifyJobUpdated(*job_info);
906 }
907
908 void DriveScheduler::OnConnectionTypeChanged(
909 net::NetworkChangeNotifier::ConnectionType type) {
910 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
911
912 // Resume the job loop if the network is back online. Note that we don't
913 // need to check the type of the network as it will be checked in
914 // ShouldStopJobLoop() as soon as the loop is resumed.
915 if (!net::NetworkChangeNotifier::IsOffline()) {
916 for (int i = METADATA_QUEUE; i < NUM_QUEUES; ++i) {
917 StartJobLoop(static_cast<QueueType>(i));
918 }
919 }
920 }
921
922 DriveScheduler::QueueType DriveScheduler::GetJobQueueType(JobType type) {
923 switch (type) {
924 case TYPE_GET_ABOUT_RESOURCE:
925 case TYPE_GET_ACCOUNT_METADATA:
926 case TYPE_GET_APP_LIST:
927 case TYPE_GET_ALL_RESOURCE_LIST:
928 case TYPE_GET_RESOURCE_LIST_IN_DIRECTORY:
929 case TYPE_SEARCH:
930 case TYPE_GET_CHANGE_LIST:
931 case TYPE_CONTINUE_GET_RESOURCE_LIST:
932 case TYPE_GET_RESOURCE_ENTRY:
933 case TYPE_DELETE_RESOURCE:
934 case TYPE_COPY_HOSTED_DOCUMENT:
935 case TYPE_RENAME_RESOURCE:
936 case TYPE_ADD_RESOURCE_TO_DIRECTORY:
937 case TYPE_REMOVE_RESOURCE_FROM_DIRECTORY:
938 case TYPE_ADD_NEW_DIRECTORY:
939 return METADATA_QUEUE;
940
941 case TYPE_DOWNLOAD_FILE:
942 case TYPE_UPLOAD_NEW_FILE:
943 case TYPE_UPLOAD_EXISTING_FILE:
944 return FILE_QUEUE;
945 }
946 NOTREACHED();
947 return FILE_QUEUE;
948 }
949
950 void DriveScheduler::NotifyJobAdded(const JobInfo& job_info) {
951 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
952 FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobAdded(job_info));
953 }
954
955 void DriveScheduler::NotifyJobDone(const JobInfo& job_info,
956 FileError error) {
957 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
958 FOR_EACH_OBSERVER(JobListObserver, observer_list_,
959 OnJobDone(job_info, error));
960 }
961
962 void DriveScheduler::NotifyJobUpdated(const JobInfo& job_info) {
963 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
964 FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobUpdated(job_info));
965 }
966
967 } // namespace drive
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/drive/drive_scheduler.h ('k') | chrome/browser/chromeos/drive/drive_scheduler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698