1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/worker_pool_mac.h"
#include "base/histogram.h"
#include "base/logging.h"
#import "base/scoped_nsautorelease_pool.h"
#include "base/scoped_ptr.h"
#import "base/singleton_objc.h"
#include "base/task.h"
namespace {
Lock lock_;
base::Time last_check_; // Last hung-test check.
std::vector<id> outstanding_ops_; // Outstanding operations at last check.
size_t running_ = 0; // Operations in |Run()|.
size_t outstanding_ = 0; // Operations posted but not completed.
} // namespace
@implementation WorkerPoolObjC
+ (NSOperationQueue*)sharedOperationQueue {
return SingletonObjC<NSOperationQueue>::get();
}
@end // @implementation WorkerPoolObjC
// TaskOperation adapts Task->Run() for use in an NSOperationQueue.
@interface TaskOperation : NSOperation {
@private
scoped_ptr<Task> task_;
}
// Returns an autoreleased instance of TaskOperation. See -initWithTask: for
// details.
+ (id)taskOperationWithTask:(Task*)task;
// Designated initializer. |task| is adopted as the Task* whose Run method
// this operation will call when executed.
- (id)initWithTask:(Task*)task;
@end // @interface TaskOperation
@implementation TaskOperation
+ (id)taskOperationWithTask:(Task*)task {
return [[[TaskOperation alloc] initWithTask:task] autorelease];
}
- (id)init {
return [self initWithTask:NULL];
}
- (id)initWithTask:(Task*)task {
if ((self = [super init])) {
task_.reset(task);
}
return self;
}
- (void)main {
DCHECK(task_.get()) << "-[TaskOperation main] called with no task";
if (!task_.get()) {
return;
}
{
AutoLock locked(lock_);
++running_;
}
base::ScopedNSAutoreleasePool autoreleasePool;
task_->Run();
task_.reset(NULL);
{
AutoLock locked(lock_);
--running_;
--outstanding_;
}
}
- (void)dealloc {
DCHECK(!task_.get())
<< "-[TaskOperation dealloc] called without running task";
[super dealloc];
}
@end // @implementation TaskOperation
bool WorkerPool::PostTask(const tracked_objects::Location& from_here,
Task* task, bool task_is_slow) {
base::ScopedNSAutoreleasePool autorelease_pool;
// Ignore |task_is_slow|, it doesn't map directly to any tunable aspect of
// an NSOperation.
DCHECK(task) << "WorkerPool::PostTask called with no task";
if (!task) {
return false;
}
task->SetBirthPlace(from_here);
NSOperationQueue* operation_queue = [WorkerPoolObjC sharedOperationQueue];
[operation_queue addOperation:[TaskOperation taskOperationWithTask:task]];
// Periodically calculate the set of operations which have not made
// progress and report how many there are. This should provide a
// sense of how many clients are seeing hung operations of any sort,
// and a sense of how many clients are seeing "too many" hung
// operations.
std::vector<id> hung_ops;
size_t outstanding_delta = 0;
size_t running_ops = 0;
{
const base::TimeDelta kCheckPeriod(base::TimeDelta::FromMinutes(10));
base::Time now = base::Time::Now();
AutoLock locked(lock_);
++outstanding_;
running_ops = running_;
if (last_check_.is_null() || now - last_check_ > kCheckPeriod) {
base::ScopedNSAutoreleasePool autoreleasePool;
std::vector<id> ops;
for (id op in [operation_queue operations]) {
// DO NOT RETAIN.
ops.push_back(op);
}
std::sort(ops.begin(), ops.end());
outstanding_delta = outstanding_ - ops.size();
std::set_intersection(outstanding_ops_.begin(), outstanding_ops_.end(),
ops.begin(), ops.end(),
std::back_inserter(hung_ops));
outstanding_ops_.swap(ops);
last_check_ = now;
}
}
// Don't report "nothing to report".
const size_t kUnaccountedOpsDelta = 10;
if (hung_ops.size() > 0 || outstanding_delta > kUnaccountedOpsDelta) {
UMA_HISTOGRAM_COUNTS_100("OSX.HungWorkers", hung_ops.size());
UMA_HISTOGRAM_COUNTS_100("OSX.OutstandingDelta", outstanding_delta);
UMA_HISTOGRAM_COUNTS_100("OSX.RunningOps", running_ops);
}
return true;
}
|