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

Unified Diff: base/tracked_objects.h

Issue 8233037: Update task tracking to not depend on message_loop_ singleton (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/threading/worker_pool_win.cc ('k') | base/tracked_objects.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/tracked_objects.h
===================================================================
--- base/tracked_objects.h (revision 104925)
+++ base/tracked_objects.h (working copy)
@@ -31,9 +31,7 @@
// marginal allocation cost associated with construction or destruction of
// tracked objects, no locks are generally employed, and probably the largest
// computational cost is associated with obtaining start and stop times for
-// instances as they are created and destroyed. The introduction of worker
-// threads had a slight impact on this approach, and required use of some locks
-// when accessing data from the worker threads.
+// instances as they are created and destroyed.
//
// The following describes the lifecycle of tracking an instance.
//
@@ -54,9 +52,10 @@
// The derived Births class contains slots for recording statistics about all
// instances born at the same location. Statistics currently include only the
// count of instances constructed.
+//
// Since the base class BirthOnThread contains only constant data, it can be
// freely accessed by any thread at any time (i.e., only the statistic needs to
-// be handled carefully, and it is ONLY read or written by the birth thread).
+// be handled carefully, and stats are updated exclusively on the birth thread).
//
// For Tasks, having now either constructed or found the Births instance
// described above, a pointer to the Births instance is then recorded into the
@@ -67,7 +66,7 @@
// can find out a Task's location of birth, and thread of birth, without using
// any locks, as all that data is constant across the life of the process.
//
-// This can also be done for any other object as well by calling
+// The above work *could* also be done for any other object as well by calling
// TallyABirthIfActive() and TallyADeathIfActive() as appropriate.
//
// The amount of memory used in the above data structures depends on how many
@@ -81,10 +80,11 @@
// carefully accumulated. That tallying wrties into slots (members) in a
// collection of DeathData instances. For each birth place Location that is
// destroyed on a thread, there is a DeathData instance to record the additional
-// death count, as well as accumulate the lifetime duration of the instance as
-// it is destroyed (dies). By maintaining a single place to aggregate this
-// addition *only* for the given thread, we avoid the need to lock such
-// DeathData instances.
+// death count, as well as accumulate the run-time and queue-time durations for
+// the instance as it is destroyed (dies). By maintaining a single place to
+// aggregate this running sum *only* for the given thread, we avoid the need to
+// lock such DeathData instances. (i.e., these accumulated stats in a DeathData
+// instance are exclusively updated by the singular owning thread).
//
// With the above lifecycle description complete, the major remaining detail is
// explaining how each thread maintains a list of DeathData instances, and of
@@ -129,18 +129,26 @@
// birth and death datastructures, but have local (frozen) copies of the actual
// statistics (birth count, durations, etc. etc.).
//
-// A DataCollector is a container object that holds a set of Snapshots. A
-// DataCollector can be passed from thread to thread, and each thread
-// contributes to it by adding or updating Snapshot instances. DataCollector
-// instances are thread safe containers which are passed to various threads to
-// accumulate all Snapshot instances.
+// A DataCollector is a container object that holds a set of Snapshots. The
+// statistics in a snapshot are gathered asynhcronously relative to their
+// ongoing updates. It is possible, though highly unlikely, that stats such
+// as a 64bit counter could incorrectly recorded by this process. The advantage
+// to having fast (non-atomic) updates of the data outweighs the minimal risk
+// of a singular corrupt statistic snapshot (only the snapshot could be corrupt,
+// not the underlying and ongoing stistic). In constrast, pointer data that is
+// accessed during snapshotting is completely invariant, and hence is perfectly
+// acquired (i.e., no potential corruption, and no risk of a bad memory
+// reference).
//
// After an array of Snapshots instances are colleted into a DataCollector, they
-// need to be sorted, and possibly aggregated (example: how many threads are in
-// a specific consecutive set of Snapshots? What was the total birth count for
-// that set? etc.). Aggregation instances collect running sums of any set of
-// snapshot instances, and are used to print sub-totals in an about:tracking
-// page.
+// need to be prepared for display our output. We currently implement a direct
+// renderin to HTML, but we will soon also have a JSON serialization as well.
+
+// For direct HTML display, the data must be sorted, and possibly aggregated
+// (example: how many threads are in a specific consecutive set of Snapshots?
+// What was the total birth count for that set? etc.). Aggregation instances
+// collect running sums of any set of snapshot instances, and are used to print
+// sub-totals in an about:tracking page.
//
// TODO(jar): I need to store DataCollections, and provide facilities for taking
// the difference between two gathered DataCollections. For now, I'm just
@@ -167,7 +175,7 @@
const ThreadData* birth_thread() const { return birth_thread_; }
private:
- // File/lineno of birth. This defines the essence of the type, as the context
+ // File/lineno of birth. This defines the essence of the task, as the context
// of the birth (construction) often tell what the item is for. This field
// is const, and hence safe to access from any thread.
const Location location_;
@@ -213,35 +221,39 @@
class BASE_EXPORT DeathData {
public:
// Default initializer.
- DeathData() : count_(0), square_duration_(0) {}
+ DeathData() : count_(0) {}
// When deaths have not yet taken place, and we gather data from all the
// threads, we create DeathData stats that tally the number of births without
// a corrosponding death.
- explicit DeathData(int count) : count_(count), square_duration_(0) {}
+ explicit DeathData(int count) : count_(count) {}
- void RecordDeath(const base::TimeDelta& duration);
+ // Update stats for a task destruction (death) that had a Run() time of
+ // |duration|, and has had a queueing delay of |queue_duration|.
+ void RecordDeath(const base::TimeDelta& queue_duration,
+ const base::TimeDelta& run_duration);
// Metrics accessors.
int count() const { return count_; }
- base::TimeDelta life_duration() const { return life_duration_; }
- int64 square_duration() const { return square_duration_; }
- int AverageMsDuration() const;
- double StandardDeviation() const;
+ base::TimeDelta run_duration() const { return run_duration_; }
+ int AverageMsRunDuration() const;
+ base::TimeDelta queue_duration() const { return queue_duration_; }
+ int AverageMsQueueDuration() const;
- // Accumulate metrics from other into this.
+ // Accumulate metrics from other into this. This method is never used on
+ // realtime statistics, and only used in snapshots and aggregatinos.
void AddDeathData(const DeathData& other);
// Simple print of internal state.
void Write(std::string* output) const;
- // Reset all tallies to zero.
+ // Reset all tallies to zero. This is used as a hack on realtime data.
void Clear();
private:
- int count_; // Number of destructions.
- base::TimeDelta life_duration_; // Sum of all lifetime durations.
- int64 square_duration_; // Sum of squares in milliseconds.
+ int count_; // Number of destructions.
+ base::TimeDelta run_duration_; // Sum of all Run()time durations.
+ base::TimeDelta queue_duration_; // Sum of all queue time durations.
};
//------------------------------------------------------------------------------
@@ -260,7 +272,6 @@
// When snapshotting a birth, with no death yet, use this:
Snapshot(const BirthOnThread& birth_on_thread, int count);
-
const ThreadData* birth_thread() const { return birth_->birth_thread(); }
const Location location() const { return birth_->location(); }
const BirthOnThread& birth() const { return *birth_; }
@@ -269,9 +280,16 @@
const std::string DeathThreadName() const;
int count() const { return death_data_.count(); }
- base::TimeDelta life_duration() const { return death_data_.life_duration(); }
- int64 square_duration() const { return death_data_.square_duration(); }
- int AverageMsDuration() const { return death_data_.AverageMsDuration(); }
+ base::TimeDelta run_duration() const { return death_data_.run_duration(); }
+ int AverageMsRunDuration() const {
+ return death_data_.AverageMsRunDuration();
+ }
+ base::TimeDelta queue_duration() const {
+ return death_data_.queue_duration();
+ }
+ int AverageMsQueueDuration() const {
+ return death_data_.AverageMsQueueDuration();
+ }
void Write(std::string* output) const;
@@ -282,10 +300,10 @@
const ThreadData* death_thread_;
DeathData death_data_;
};
+
//------------------------------------------------------------------------------
// DataCollector is a container class for Snapshot and BirthOnThread count
-// items. It protects the gathering under locks, so that it could be called via
-// Posttask on any threads, or passed to all the target threads in parallel.
+// items.
class BASE_EXPORT DataCollector {
public:
@@ -312,12 +330,6 @@
private:
typedef std::map<const BirthOnThread*, int> BirthCount;
- // This instance may be provided to several threads to contribute data. The
- // following counter tracks how many more threads will contribute. When it is
- // zero, then all asynchronous contributions are complete, and locked access
- // is no longer needed.
- int count_of_contributing_threads_;
-
// The array that we collect data into.
Collection collection_;
@@ -325,8 +337,6 @@
// seen a death count.
BirthCount global_birth_count_;
- base::Lock accumulation_lock_; // Protects access during accumulation phase.
-
DISALLOW_COPY_AND_ASSIGN(DataCollector);
};
@@ -381,8 +391,10 @@
BIRTH_FUNCTION = 8,
BIRTH_LINE = 16,
COUNT = 32,
- AVERAGE_DURATION = 64,
- TOTAL_DURATION = 128,
+ AVERAGE_RUN_DURATION = 64,
+ TOTAL_RUN_DURATION = 128,
+ AVERAGE_QUEUE_DURATION = 256,
+ TOTAL_QUEUE_DURATION = 512,
// Imediate action keywords.
RESET_ALL_DATA = -1,
@@ -418,7 +430,7 @@
// printed line.
bool IsGroupedBy(Selector selector) const;
- // Using the tiebreakers as set above, we mostly get an ordering, which
+ // Using the tiebreakers as set above, we mostly get an ordering, with some
// equivalent groups. If those groups are displayed (rather than just being
// aggregated, then the following is used to order them (within the group).
void SetSubgroupTiebreaker(Selector selector);
@@ -462,7 +474,6 @@
bool use_tiebreaker_for_sort_only_;
};
-
//------------------------------------------------------------------------------
// For each thread, we have a ThreadData that stores all tracking info generated
// on this thread. This prevents the need for locking as data accumulates.
@@ -472,15 +483,18 @@
typedef std::map<Location, Births*> BirthMap;
typedef std::map<const Births*, DeathData> DeathMap;
- ThreadData();
- ~ThreadData();
+ // Initialize the current thread context with a new instance of ThreadData.
+ // This is used by all threads that have names, and can be explicitly
+ // set *before* any births are threads have taken place. It is generally
+ // only used by the message loop, which has a well defined name.
+ static void InitializeThreadContext(const std::string& suggested_name);
// Using Thread Local Store, find the current instance for collecting data.
// If an instance does not exist, construct one (and remember it for use on
// this thread.
// If shutdown has already started, and we don't yet have an instance, then
// return null.
- static ThreadData* current();
+ static ThreadData* Get();
// For a given (unescaped) about:tracking query, develop resulting HTML, and
// append to output.
@@ -496,22 +510,33 @@
Births* TallyABirth(const Location& location);
// Find a place to record a death on this thread.
- void TallyADeath(const Births& lifetimes, const base::TimeDelta& duration);
+ void TallyADeath(const Births& the_birth,
+ const base::TimeDelta& queue_duration,
+ const base::TimeDelta& duration);
// Helper methods to only tally if the current thread has tracking active.
//
// TallyABirthIfActive will returns NULL if the birth cannot be tallied.
static Births* TallyABirthIfActive(const Location& location);
- static void TallyADeathIfActive(const Births* lifetimes,
- const base::TimeDelta& duration);
+ // Record the end of a timed run of an object. The |the_birth| is the record
+ // for the instance, the |time_posted| and |start_of_run| are times of posting
+ // into a message loop queue, and of starting to perform the run of the task.
+ // Implied is that the run just (Now()) ended. The current_message_loop is
+ // optional, and only used in DEBUG mode (when supplied) to verify that the
+ // ThreadData has a thread name that does indeed match the given loop's
+ // associated thread name (in RELEASE mode, its use is compiled away).
+ static void TallyADeathIfActive(const Births* the_birth,
+ const base::TimeTicks& time_posted,
+ const base::TimeTicks& delayed_start_time,
+ const base::TimeTicks& start_of_run);
+
// (Thread safe) Get start of list of instances.
static ThreadData* first();
// Iterate through the null terminated list of instances.
ThreadData* next() const { return next_; }
- MessageLoop* message_loop() const { return message_loop_; }
- const std::string ThreadName() const;
+ const std::string thread_name() const { return thread_name_; }
// Using our lock, make a copy of the specified maps. These calls may arrive
// from non-local threads, and are used to quickly scan data from all threads
@@ -528,31 +553,17 @@
// Using our lock to protect the iteration, Clear all birth and death data.
void Reset();
- // Using the "known list of threads" gathered during births and deaths, the
- // following attempts to run the given function once all all such threads.
- // Note that the function can only be run on threads which have a message
- // loop!
- static void RunOnAllThreads(void (*Func)());
-
// Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN,
// based on argument being true or false respectively.
// IF tracking is not compiled in, this function will return false.
static bool StartTracking(bool status);
static bool IsActive();
-#ifdef OS_WIN
- // WARNING: ONLY call this function when all MessageLoops are still intact for
- // all registered threads. IF you call it later, you will crash.
- // Note: You don't need to call it at all, and you can wait till you are
- // single threaded (again) to do the cleanup via
- // ShutdownSingleThreadedCleanup().
- // Start the teardown (shutdown) process in a multi-thread mode by disabling
- // further additions to thread database on all threads. First it makes a
- // local (locked) change to prevent any more threads from registering. Then
- // it Posts a Task to all registered threads to be sure they are aware that no
- // more accumulation can take place.
- static void ShutdownMultiThreadTracking();
-#endif
+ // Provide a time function that does nothing (runs fast) when we don't have
+ // the profiler enabled. It will generally be optimized away when it is
+ // ifdef'ed to be small enough (allowing the profiler to be "compiled out" of
+ // the code).
+ static base::TimeTicks Now();
// WARNING: ONLY call this function when you are running single threaded
// (again) and all message loops and threads have terminated. Until that
@@ -562,6 +573,18 @@
static void ShutdownSingleThreadedCleanup();
private:
+ // Worker thread construction creates a name.
+ ThreadData();
+ // Message loop based construction should provide a name.
+ explicit ThreadData(const std::string& suggested_name);
+
+ ~ThreadData();
+
+ // Enter a new instance into Thread Local Store.
+ // Return the instance, or null if we can't register it (because we're
+ // shutting down).
+ static ThreadData* RegisterCurrentContext(ThreadData* unregistered);
+
// Current allowable states of the tracking system. The states always
// proceed towards SHUTDOWN, and never go backwards.
enum Status {
@@ -570,17 +593,6 @@
SHUTDOWN,
};
-#if defined(OS_WIN)
- class ThreadSafeDownCounter;
- class RunTheStatic;
-#endif
-
- // Each registered thread is called to set status_ to SHUTDOWN.
- // This is done redundantly on every registered thread because it is not
- // protected by a mutex. Running on all threads guarantees we get the
- // notification into the memory cache of all possible threads.
- static void ShutdownDisablingFurtherTracking();
-
// We use thread local store to identify which ThreadData to interact with.
static base::ThreadLocalStorage::Slot tls_index_;
@@ -589,10 +601,7 @@
// Protection for access to first_.
static base::Lock list_lock_;
- // We set status_ to SHUTDOWN when we shut down the tracking service. This
- // setting is redundantly established by all participating threads so that we
- // are *guaranteed* (without locking) that all threads can "see" the status
- // and avoid additional calls into the service.
+ // We set status_ to SHUTDOWN when we shut down the tracking service.
static Status status_;
// Link to next instance (null terminated list). Used to globally track all
@@ -600,10 +609,9 @@
// data).
ThreadData* next_;
- // The message loop where tasks needing to access this instance's private data
- // should be directed. Since some threads have no message loop, some
- // instances have data that can't be (safely) modified externally.
- MessageLoop* message_loop_;
+ // The name of the thread that is being recorded. If this thread has no
+ // message_loop, then this is a worker thread, with a sequence number postfix.
+ std::string thread_name_;
// A map used on each thread to keep track of Births on this thread.
// This map should only be accessed on the thread it was constructed on.
@@ -625,10 +633,13 @@
// writing is only done from this thread.
mutable base::Lock lock_;
+ // The next available thread number. This should only be accessed when the
+ // list_lock_ is held.
+ static int thread_number_counter;
+
DISALLOW_COPY_AND_ASSIGN(ThreadData);
};
-
//------------------------------------------------------------------------------
// Provide simple way to to start global tracking, and to tear down tracking
// when done. Note that construction and destruction of this object must be
« no previous file with comments | « base/threading/worker_pool_win.cc ('k') | base/tracked_objects.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698