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

Side by Side Diff: base/tracked_objects.h

Issue 8597017: Switch to a simple linked-list for worker thread pool (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month 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
« no previous file with comments | « no previous file | base/tracked_objects.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 #ifndef BASE_TRACKED_OBJECTS_H_ 5 #ifndef BASE_TRACKED_OBJECTS_H_
6 #define BASE_TRACKED_OBJECTS_H_ 6 #define BASE_TRACKED_OBJECTS_H_
7 #pragma once 7 #pragma once
8 8
9 #include <map> 9 #include <map>
10 #include <stack> 10 #include <stack>
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 // purposes, we need to construct Snapshot instances for each combination of 130 // purposes, we need to construct Snapshot instances for each combination of
131 // birth thread, death thread, and location, along with the count of such 131 // birth thread, death thread, and location, along with the count of such
132 // lifetimes. We gather such data into a Snapshot instances, so that such 132 // lifetimes. We gather such data into a Snapshot instances, so that such
133 // instances can be sorted and aggregated (and remain frozen during our 133 // instances can be sorted and aggregated (and remain frozen during our
134 // processing). Snapshot instances use pointers to constant portions of the 134 // processing). Snapshot instances use pointers to constant portions of the
135 // birth and death datastructures, but have local (frozen) copies of the actual 135 // birth and death datastructures, but have local (frozen) copies of the actual
136 // statistics (birth count, durations, etc. etc.). 136 // statistics (birth count, durations, etc. etc.).
137 // 137 //
138 // A DataCollector is a container object that holds a set of Snapshots. The 138 // A DataCollector is a container object that holds a set of Snapshots. The
139 // statistics in a snapshot are gathered asynhcronously relative to their 139 // statistics in a snapshot are gathered asynhcronously relative to their
140 // ongoing updates. It is possible, though highly unlikely, that stats such 140 // ongoing updates. It is possible, though highly unlikely, that stats could be
141 // as a 64bit counter could be incorrectly recorded by this process. The 141 // incorrectly recorded by this process (all data is held in 32 bit ints, but we
142 // are not atomically collecting all data, so we could have count that does not,
143 // for example, match with the number of durations we accumulated). The
142 // advantage to having fast (non-atomic) updates of the data outweighs the 144 // advantage to having fast (non-atomic) updates of the data outweighs the
143 // minimal risk of a singular corrupt statistic snapshot (only the snapshot 145 // minimal risk of a singular corrupt statistic snapshot (only the snapshot
144 // could be corrupt, not the underlying and ongoing statistic). In constrast, 146 // could be corrupt, not the underlying and ongoing statistic). In constrast,
145 // pointer data that is accessed during snapshotting is completely invariant, 147 // pointer data that is accessed during snapshotting is completely invariant,
146 // and hence is perfectly acquired (i.e., no potential corruption, and no risk 148 // and hence is perfectly acquired (i.e., no potential corruption, and no risk
147 // of a bad memory reference). 149 // of a bad memory reference).
148 // 150 //
149 // After an array of Snapshots instances are collected into a DataCollector, 151 // After an array of Snapshots instances are collected into a DataCollector,
150 // they need to be prepared for displaying our output. We currently implement a 152 // they need to be prepared for displaying our output. We currently implement a
151 // direct rendering to HTML, but we will soon have a JSON serialization as well. 153 // serialization into a Value hierarchy, which is automatically translated to
152 154 // JSON when supplied to rendering Java Scirpt.
153 // For direct HTML display, the data must be sorted, and possibly aggregated
154 // (example: how many threads are in a specific consecutive set of Snapshots?
155 // What was the total birth count for that set? etc.). Aggregation instances
156 // collect running sums of any set of snapshot instances, and are used to print
157 // sub-totals in an about:profiler page.
158 // 155 //
159 // TODO(jar): I need to store DataCollections, and provide facilities for taking 156 // TODO(jar): We can implement a Snapshot system that *tries* to grab the
160 // the difference between two gathered DataCollections. For now, I'm just 157 // snapshots on the source threads *when* they have MessageLoops available
161 // adding a hack that Reset()s to zero all counts and stats. This is also 158 // (worker threads don't have message loops generally, and hence gathering from
159 // them will continue to be asynchronous). We had an implementation of this in
160 // the past, but the difficulty is dealing with message loops being terminated.
161 // We can *try* to spam the available threads via some message loop proxy to
162 // achieve this feat, and it *might* be valuable when we are colecting data for
163 // upload via UMA (where correctness of data may be more significant than for a
164 // single screen of about:profiler).
165 //
166 // TODO(jar): We need to save a single sample in each DeathData instance of the
167 // times recorded. This sample should be selected in a uniformly random way.
168 //
169 // TODO(jar): We should support (optionally) the recording of parent-child
170 // relationships for tasks. This should be done by detecting what tasks are
171 // Born during the running of a parent task. The resulting data can be used by
172 // a smarter profiler to aggregate the cost of a series of child tasks into
173 // the ancestor task. It can also be used to illuminate what child or parent is
174 // related to each task.
175 //
176 // TODO(jar): We need to store DataCollections, and provide facilities for
177 // taking the difference between two gathered DataCollections. For now, we're
178 // just adding a hack that Reset()s to zero all counts and stats. This is also
162 // done in a slighly thread-unsafe fashion, as the resetting is done 179 // done in a slighly thread-unsafe fashion, as the resetting is done
163 // asynchronously relative to ongoing updates (but all data is 32 bit in size). 180 // asynchronously relative to ongoing updates (but all data is 32 bit in size).
164 // For basic profiling, this will work "most of the time," and should be 181 // For basic profiling, this will work "most of the time," and should be
165 // sufficient... but storing away DataCollections is the "right way" to do this. 182 // sufficient... but storing away DataCollections is the "right way" to do this.
166 // We'll accomplish this via JavaScript storage of snapshots, and then we'll 183 // We'll accomplish this via JavaScript storage of snapshots, and then we'll
167 // remove the Reset() methods. 184 // remove the Reset() methods. We may also need a short-term-max value in
185 // DeathData that is reset (as synchronously as possible) during each snapshot.
186 // This will facilitate displaying a max value for each snapshot period.
168 187
169 class MessageLoop; 188 class MessageLoop;
170 189
171 namespace tracked_objects { 190 namespace tracked_objects {
172 191
173 //------------------------------------------------------------------------------ 192 //------------------------------------------------------------------------------
174 // For a specific thread, and a specific birth place, the collection of all 193 // For a specific thread, and a specific birth place, the collection of all
175 // death info (with tallies for each death thread, to prevent access conflicts). 194 // death info (with tallies for each death thread, to prevent access conflicts).
176 class ThreadData; 195 class ThreadData;
177 class BASE_EXPORT BirthOnThread { 196 class BASE_EXPORT BirthOnThread {
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 DurationInt AverageMsRunDuration() const; 266 DurationInt AverageMsRunDuration() const;
248 DurationInt run_duration_max() const { return run_time_.max(); } 267 DurationInt run_duration_max() const { return run_time_.max(); }
249 DurationInt queue_duration() const { return queue_time_.duration(); } 268 DurationInt queue_duration() const { return queue_time_.duration(); }
250 DurationInt AverageMsQueueDuration() const; 269 DurationInt AverageMsQueueDuration() const;
251 DurationInt queue_duration_max() const { return queue_time_.max(); } 270 DurationInt queue_duration_max() const { return queue_time_.max(); }
252 271
253 // Accumulate metrics from other into this. This method is never used on 272 // Accumulate metrics from other into this. This method is never used on
254 // realtime statistics, and only used in snapshots and aggregatinos. 273 // realtime statistics, and only used in snapshots and aggregatinos.
255 void AddDeathData(const DeathData& other); 274 void AddDeathData(const DeathData& other);
256 275
257 // Simple print of internal state for use in line of HTML.
258 void WriteHTML(std::string* output) const;
259
260 // Construct a DictionaryValue instance containing all our stats. The caller 276 // Construct a DictionaryValue instance containing all our stats. The caller
261 // assumes ownership of the returned instance. 277 // assumes ownership of the returned instance.
262 base::DictionaryValue* ToValue() const; 278 base::DictionaryValue* ToValue() const;
263 279
264 // Reset all tallies to zero. This is used as a hack on realtime data. 280 // Reset all tallies to zero. This is used as a hack on realtime data.
265 void Clear(); 281 void Clear();
266 282
267 private: 283 private:
268 // DeathData::Data is a helper class, useful when different metrics need to be 284 // DeathData::Data is a helper class, useful when different metrics need to be
269 // aggregated, such as queueing times, or run times. 285 // aggregated, such as queueing times, or run times.
270 class Data { 286 class Data {
271 public: 287 public:
272 Data() : duration_(0), max_(0) {} 288 Data() : duration_(0), max_(0) {}
273 ~Data() {} 289 ~Data() {}
274 290
275 DurationInt duration() const { return duration_; } 291 DurationInt duration() const { return duration_; }
276 DurationInt max() const { return max_; } 292 DurationInt max() const { return max_; }
277 293
278 // Emits HTML formated description of members, assuming |count| instances
279 // when calculating averages.
280 void WriteHTML(int count, std::string* output) const;
281
282 // Agggegate data into our state. 294 // Agggegate data into our state.
283 void AddData(const Data& other); 295 void AddData(const Data& other);
284 void AddDuration(DurationInt duration); 296 void AddDuration(DurationInt duration);
285 297
286 // Central helper function for calculating averages (correctly, in only one 298 // Central helper function for calculating averages (correctly, in only one
287 // place). 299 // place).
288 DurationInt AverageMsDuration(int count) const; 300 DurationInt AverageMsDuration(int count) const;
289 301
290 // Resets all members to zero. 302 // Resets all members to zero.
291 void Clear(); 303 void Clear();
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 // The following functions should all be private, and are only public because 491 // The following functions should all be private, and are only public because
480 // the collection is done externally. We need to relocate that code from the 492 // the collection is done externally. We need to relocate that code from the
481 // collection class into this class, and then all these methods can be made 493 // collection class into this class, and then all these methods can be made
482 // private. 494 // private.
483 // (Thread safe) Get start of list of all ThreadData instances. 495 // (Thread safe) Get start of list of all ThreadData instances.
484 static ThreadData* first(); 496 static ThreadData* first();
485 // Iterate through the null terminated list of ThreadData instances. 497 // Iterate through the null terminated list of ThreadData instances.
486 ThreadData* next() const { return next_; } 498 ThreadData* next() const { return next_; }
487 // Using our lock, make a copy of the specified maps. These calls may arrive 499 // Using our lock, make a copy of the specified maps. These calls may arrive
488 // from non-local threads, and are used to quickly scan data from all threads 500 // from non-local threads, and are used to quickly scan data from all threads
489 // in order to build an HTML page for about:profiler. 501 // in order to build JSON for about:profiler.
490 void SnapshotBirthMap(BirthMap *output) const; 502 void SnapshotBirthMap(BirthMap *output) const;
491 void SnapshotDeathMap(DeathMap *output) const; 503 void SnapshotDeathMap(DeathMap *output) const;
492 // -------- end of should be private methods. 504 // -------- end of should be private methods.
493 505
494 // Hack: asynchronously clear all birth counts and death tallies data values 506 // Hack: asynchronously clear all birth counts and death tallies data values
495 // in all ThreadData instances. The numerical (zeroing) part is done without 507 // in all ThreadData instances. The numerical (zeroing) part is done without
496 // use of a locks or atomics exchanges, and may (for int64 values) produce 508 // use of a locks or atomics exchanges, and may (for int64 values) produce
497 // bogus counts VERY rarely. 509 // bogus counts VERY rarely.
498 static void ResetAllThreadData(); 510 static void ResetAllThreadData();
499 511
(...skipping 18 matching lines...) Expand all
518 // the profiler enabled. It will generally be optimized away when it is 530 // the profiler enabled. It will generally be optimized away when it is
519 // ifdef'ed to be small enough (allowing the profiler to be "compiled out" of 531 // ifdef'ed to be small enough (allowing the profiler to be "compiled out" of
520 // the code). 532 // the code).
521 static TrackedTime Now(); 533 static TrackedTime Now();
522 534
523 private: 535 private:
524 // Allow only tests to call ShutdownSingleThreadedCleanup. We NEVER call it 536 // Allow only tests to call ShutdownSingleThreadedCleanup. We NEVER call it
525 // in production code. 537 // in production code.
526 friend class TrackedObjectsTest; 538 friend class TrackedObjectsTest;
527 539
528 // Implment a stack that avoids allocations during a push() by having enough
529 // space ahead of time.
530 class ThreadDataPool {
531 public:
532 ThreadDataPool();
533 ~ThreadDataPool();
534
535 // Make sure the stack is large enough to support the indicated number of
536 // elements.
537 void reserve(size_t largest_worker_pool_number);
538
539 bool empty() const;
540 const ThreadData* top() const;
541 void push(const ThreadData* thread_data);
542 void pop();
543
544 private:
545 std::vector<const ThreadData*> stack_;
546 size_t empty_slot_;
547 DISALLOW_COPY_AND_ASSIGN(ThreadDataPool);
548 };
549
550 // Worker thread construction creates a name since there is none. 540 // Worker thread construction creates a name since there is none.
551 explicit ThreadData(size_t thread_number); 541 explicit ThreadData(int thread_number);
552 542
553 // Message loop based construction should provide a name. 543 // Message loop based construction should provide a name.
554 explicit ThreadData(const std::string& suggested_name); 544 explicit ThreadData(const std::string& suggested_name);
555 545
556 ~ThreadData(); 546 ~ThreadData();
557 547
558 // Push this instance to the head of all_thread_data_list_head_, linking it to 548 // Push this instance to the head of all_thread_data_list_head_, linking it to
559 // the previous head. This is performed after each construction, and leaves 549 // the previous head. This is performed after each construction, and leaves
560 // the instance permanently on that list. 550 // the instance permanently on that list.
561 void PushToHeadOfList(); 551 void PushToHeadOfList();
562 552
563 // In this thread's data, record a new birth. 553 // In this thread's data, record a new birth.
564 Births* TallyABirth(const Location& location); 554 Births* TallyABirth(const Location& location);
565 555
566 // Find a place to record a death on this thread. 556 // Find a place to record a death on this thread.
567 void TallyADeath(const Births& birth, 557 void TallyADeath(const Births& birth,
568 DurationInt queue_duration, 558 DurationInt queue_duration,
569 DurationInt duration); 559 DurationInt duration);
570 560
571 // Using our lock to protect the iteration, Clear all birth and death data. 561 // Using our lock to protect the iteration, Clear all birth and death data.
572 void Reset(); 562 void Reset();
573 563
574 // This method is called by the TLS system when a thread terminates. 564 // This method is called by the TLS system when a thread terminates.
575 // The argument may be NULL if this thread has never tracked a birth or death. 565 // The argument may be NULL if this thread has never tracked a birth or death.
576 static void OnThreadTermination(void* thread_data); 566 static void OnThreadTermination(void* thread_data);
577 567
578 // This method should be called when a worker thread terminates, so that we 568 // This method should be called when a worker thread terminates, so that we
579 // can save all the thread data into a cache of reusable ThreadData instances. 569 // can save all the thread data into a cache of reusable ThreadData instances.
580 void OnThreadTerminationCleanup() const; 570 void OnThreadTerminationCleanup();
581 571
582 // Cleans up data structures, and returns statics to near pristine (mostly 572 // Cleans up data structures, and returns statics to near pristine (mostly
583 // uninitialized) state. If there is any chance that other threads are still 573 // uninitialized) state. If there is any chance that other threads are still
584 // using the data structures, then the |leak| argument should be passed in as 574 // using the data structures, then the |leak| argument should be passed in as
585 // true, and the data structures (birth maps, death maps, ThreadData 575 // true, and the data structures (birth maps, death maps, ThreadData
586 // insntances, etc.) will be leaked and not deleted. If you have joined all 576 // insntances, etc.) will be leaked and not deleted. If you have joined all
587 // threads since the time that InitializeAndSetTrackingStatus() was called, 577 // threads since the time that InitializeAndSetTrackingStatus() was called,
588 // then you can pass in a |leak| value of false, and this function will 578 // then you can pass in a |leak| value of false, and this function will
589 // delete recursively all data structures, starting with the list of 579 // delete recursively all data structures, starting with the list of
590 // ThreadData instances. 580 // ThreadData instances.
591 static void ShutdownSingleThreadedCleanup(bool leak); 581 static void ShutdownSingleThreadedCleanup(bool leak);
592 582
593 // We use thread local store to identify which ThreadData to interact with. 583 // We use thread local store to identify which ThreadData to interact with.
594 static base::ThreadLocalStorage::Slot tls_index_; 584 static base::ThreadLocalStorage::Slot tls_index_;
595 585
586 // List of ThreadData instances for use with worker threads. When a worker
587 // thread is done (terminated), we push it onto this llist. When a new worker
588 // thread is created, we first try to re-use a ThreadData instance from the
589 // list, and if none are available, construct a new one.
590 // This is only accessed while list_lock_ is held.
591 static ThreadData* first_retired_worker_;
592
596 // Link to the most recently created instance (starts a null terminated list). 593 // Link to the most recently created instance (starts a null terminated list).
597 // The list is traversed by about:profiler when it needs to snapshot data. 594 // The list is traversed by about:profiler when it needs to snapshot data.
598 // This is only accessed while list_lock_ is held. 595 // This is only accessed while list_lock_ is held.
599 static ThreadData* all_thread_data_list_head_; 596 static ThreadData* all_thread_data_list_head_;
600 // Set of ThreadData instances for use with worker threads. When a worker
601 // thread is done (terminating), we push it into this pool. When a new worker
602 // thread is created, we first try to re-use a ThreadData instance from the
603 // pool, and if none are available, construct a new one.
604 // This is only accessed while list_lock_ is held.
605 static ThreadDataPool* unregistered_thread_data_pool_;
606 // The next available thread number. This should only be accessed when the 597 // The next available thread number. This should only be accessed when the
607 // list_lock_ is held. 598 // list_lock_ is held.
608 static int thread_number_counter_; 599 static int thread_number_counter_;
609 // Incarnation sequence number, indicating how many times (during unittests) 600 // Incarnation sequence number, indicating how many times (during unittests)
610 // we've either transitioned out of UNINITIALIZED, or into that state. This 601 // we've either transitioned out of UNINITIALIZED, or into that state. This
611 // value is only accessed while the list_lock_ is held. 602 // value is only accessed while the list_lock_ is held.
612 static int incarnation_counter_; 603 static int incarnation_counter_;
613 // Protection for access to all_thread_data_list_head_, and to 604 // Protection for access to all_thread_data_list_head_, and to
614 // unregistered_thread_data_pool_. This lock is leaked at shutdown. 605 // unregistered_thread_data_pool_. This lock is leaked at shutdown.
615 // The lock is very infrequently used, so we can afford to just make a lazy 606 // The lock is very infrequently used, so we can afford to just make a lazy
616 // instance and be safe. 607 // instance and be safe.
617 static base::LazyInstance<base::Lock, 608 static base::LazyInstance<base::Lock,
618 base::LeakyLazyInstanceTraits<base::Lock> > list_lock_; 609 base::LeakyLazyInstanceTraits<base::Lock> > list_lock_;
619 610
620 // Record of what the incarnation_counter_ was when this instance was created. 611 // Record of what the incarnation_counter_ was when this instance was created.
621 // If the incarnation_counter_ has changed, then we avoid pushing into the 612 // If the incarnation_counter_ has changed, then we avoid pushing into the
622 // pool (this is only critical in tests which go through multiple 613 // pool (this is only critical in tests which go through multiple
623 // incarations). 614 // incarations).
624 int incarnation_count_for_pool_; 615 int incarnation_count_for_pool_;
625 616
626 // We set status_ to SHUTDOWN when we shut down the tracking service. 617 // We set status_ to SHUTDOWN when we shut down the tracking service.
627 static Status status_; 618 static Status status_;
628 619
629 // Link to next instance (null terminated list). Used to globally track all 620 // Link to next instance (null terminated list). Used to globally track all
630 // registered instances (corresponds to all registered threads where we keep 621 // registered instances (corresponds to all registered threads where we keep
631 // data). 622 // data).
632 ThreadData* next_; 623 ThreadData* next_;
633 624
625 // Pointer to another ThreadData instance for a Worker-Thread that has been
626 // retired (its thread was terminated). This value is non-NULL only for a
627 // retired ThreadData associated with a Worker-Thread.
628 ThreadData* next_retired_worker_;
629
634 // The name of the thread that is being recorded. If this thread has no 630 // The name of the thread that is being recorded. If this thread has no
635 // message_loop, then this is a worker thread, with a sequence number postfix. 631 // message_loop, then this is a worker thread, with a sequence number postfix.
636 std::string thread_name_; 632 std::string thread_name_;
637 633
638 // Indicate if this is a worker thread, and the ThreadData contexts should be 634 // Indicate if this is a worker thread, and the ThreadData contexts should be
639 // stored in the unregistered_thread_data_pool_ when not in use. 635 // stored in the unregistered_thread_data_pool_ when not in use.
640 // Value is zero when it is not a worker thread. Value is a positive integer 636 // Value is zero when it is not a worker thread. Value is a positive integer
641 // corresponding to the created thread name if it is a worker thread. 637 // corresponding to the created thread name if it is a worker thread.
642 size_t worker_thread_number_; 638 int worker_thread_number_;
643 639
644 // A map used on each thread to keep track of Births on this thread. 640 // A map used on each thread to keep track of Births on this thread.
645 // This map should only be accessed on the thread it was constructed on. 641 // This map should only be accessed on the thread it was constructed on.
646 // When a snapshot is needed, this structure can be locked in place for the 642 // When a snapshot is needed, this structure can be locked in place for the
647 // duration of the snapshotting activity. 643 // duration of the snapshotting activity.
648 BirthMap birth_map_; 644 BirthMap birth_map_;
649 645
650 // Similar to birth_map_, this records informations about death of tracked 646 // Similar to birth_map_, this records informations about death of tracked
651 // instances (i.e., when a tracked instance was destroyed on this thread). 647 // instances (i.e., when a tracked instance was destroyed on this thread).
652 // It is locked before changing, and hence other threads may access it by 648 // It is locked before changing, and hence other threads may access it by
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
684 } 680 }
685 681
686 private: 682 private:
687 683
688 DISALLOW_COPY_AND_ASSIGN(AutoTracking); 684 DISALLOW_COPY_AND_ASSIGN(AutoTracking);
689 }; 685 };
690 686
691 } // namespace tracked_objects 687 } // namespace tracked_objects
692 688
693 #endif // BASE_TRACKED_OBJECTS_H_ 689 #endif // BASE_TRACKED_OBJECTS_H_
OLDNEW
« no previous file with comments | « no previous file | base/tracked_objects.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698