PageRenderTime 30ms CodeModel.GetById 12ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/image/src/DiscardTracker.cpp

http://github.com/zpao/v8monkey
C++ | 293 lines | 149 code | 45 blank | 99 comment | 26 complexity | e4ba92d674999750f621f9cfbf8859f4 MD5 | raw file
  1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2   ***** BEGIN LICENSE BLOCK *****
  3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4 *
  5 * The contents of this file are subject to the Mozilla Public License Version
  6 * 1.1 (the "License"); you may not use this file except in compliance with
  7 * the License. You may obtain a copy of the License at
  8 * http://www.mozilla.org/MPL/
  9 *
 10 * Software distributed under the License is distributed on an "AS IS" basis,
 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 12 * for the specific language governing rights and limitations under the
 13 * License.
 14 *
 15 * The Original Code is mozilla.org code.
 16 *
 17 * The Initial Developer of the Original Code is Mozilla Foundation.
 18 * Portions created by the Initial Developer are Copyright (C) 2010
 19 * the Initial Developer. All Rights Reserved.
 20 *
 21 * Contributor(s):
 22 *   Bobby Holley <bobbyholley@gmail.com>
 23 *
 24 * Alternatively, the contents of this file may be used under the terms of
 25 * either the GNU General Public License Version 2 or later (the "GPL"), or
 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 27 * in which case the provisions of the GPL or the LGPL are applicable instead
 28 * of those above. If you wish to allow use of your version of this file only
 29 * under the terms of either the GPL or the LGPL, and not to allow others to
 30 * use your version of this file under the terms of the MPL, indicate your
 31 * decision by deleting the provisions above and replace them with the notice
 32 * and other provisions required by the GPL or the LGPL. If you do not delete
 33 * the provisions above, a recipient may use your version of this file under
 34 * the terms of any one of the MPL, the GPL or the LGPL.
 35 *
 36 * ***** END LICENSE BLOCK ***** */
 37
 38#include "nsComponentManagerUtils.h"
 39#include "nsITimer.h"
 40#include "RasterImage.h"
 41#include "DiscardTracker.h"
 42#include "mozilla/Preferences.h"
 43
 44namespace mozilla {
 45namespace image {
 46
 47static bool sInitialized = false;
 48static bool sTimerOn = false;
 49static PRUint32 sMinDiscardTimeoutMs = 10000; /* Default if pref unreadable. */
 50static nsITimer *sTimer = nsnull;
 51static struct DiscardTrackerNode sHead, sSentinel, sTail;
 52
 53/*
 54 * Puts an image in the back of the tracker queue. If the image is already
 55 * in the tracker, this removes it first.
 56 */
 57nsresult
 58DiscardTracker::Reset(DiscardTrackerNode *node)
 59{
 60  nsresult rv;
 61#ifdef DEBUG
 62  bool isSentinel = (node == &sSentinel);
 63
 64  // Sanity check the node.
 65  NS_ABORT_IF_FALSE(isSentinel || node->curr, "Node doesn't point to anything!");
 66
 67  // We should not call this function if we can't discard
 68  NS_ABORT_IF_FALSE(isSentinel || node->curr->CanDiscard(),
 69                    "trying to reset discarding but can't discard!");
 70
 71  // As soon as an image becomes animated it is set non-discardable
 72  NS_ABORT_IF_FALSE(isSentinel || !node->curr->mAnim,
 73                    "Trying to reset discarding on animated image!");
 74#endif
 75
 76  // Initialize the first time through
 77  if (NS_UNLIKELY(!sInitialized)) {
 78    rv = Initialize();
 79    NS_ENSURE_SUCCESS(rv, rv);
 80  }
 81
 82  // Remove the node if it's in the list.
 83  Remove(node);
 84
 85  // Append it to the list.
 86  node->prev = sTail.prev;
 87  node->next = &sTail;
 88  node->prev->next = sTail.prev = node;
 89
 90  // Make sure the timer is running
 91  rv = TimerOn();
 92  NS_ENSURE_SUCCESS(rv,rv);
 93
 94  return NS_OK;
 95}
 96
 97/*
 98 * Removes a node from the tracker. No-op if the node is currently untracked.
 99 */
100void
101DiscardTracker::Remove(DiscardTrackerNode *node)
102{
103  NS_ABORT_IF_FALSE(node != nsnull, "Can't pass null node");
104
105  // If we're not in a list, we have nothing to do.
106  if ((node->prev == nsnull) || (node->next == nsnull)) {
107    NS_ABORT_IF_FALSE(node->prev == node->next,
108                      "Node is half in a list!");
109    return;
110  }
111
112  // Connect around ourselves
113  node->prev->next = node->next;
114  node->next->prev = node->prev;
115
116  // Clean up the node we removed.
117  node->prev = node->next = nsnull;
118}
119
120/*
121 * Discard all the images we're tracking.
122 */
123void
124DiscardTracker::DiscardAll()
125{
126  if (!sInitialized)
127    return;
128
129  // Remove the sentinel from the list so that the only elements in the list
130  // which don't track an image are the head and tail.
131  Remove(&sSentinel);
132
133  // Discard all tracked images.
134  for (DiscardTrackerNode *node = sHead.next;
135       node != &sTail; node = sHead.next) {
136    NS_ABORT_IF_FALSE(node->curr, "empty node!");
137    Remove(node);
138    node->curr->Discard();
139  }
140
141  // Add the sentinel back to the (now empty) list.
142  Reset(&sSentinel);
143
144  // Because the sentinel is the only element in the list, the next timer event
145  // would be a no-op.  Disable the timer as an optimization.
146  TimerOff();
147}
148
149static int
150DiscardTimeoutChangedCallback(const char* aPref, void *aClosure)
151{
152  DiscardTracker::ReloadTimeout();
153  return 0;
154}
155
156/**
157 * Initialize the tracker.
158 */
159nsresult
160DiscardTracker::Initialize()
161{
162  nsresult rv;
163
164  // Set up the list. Head<->Sentinel<->Tail
165  sHead.curr = sTail.curr = sSentinel.curr = nsnull;
166  sHead.prev = sTail.next = nsnull;
167  sHead.next = sTail.prev = &sSentinel;
168  sSentinel.prev = &sHead;
169  sSentinel.next = &sTail;
170
171  // Watch the timeout pref for changes.
172  Preferences::RegisterCallback(DiscardTimeoutChangedCallback,
173                                DISCARD_TIMEOUT_PREF);
174
175  ReloadTimeout();
176
177  // Create and start the timer
178  nsCOMPtr<nsITimer> t = do_CreateInstance("@mozilla.org/timer;1");
179  NS_ENSURE_TRUE(t, NS_ERROR_OUT_OF_MEMORY);
180  t.forget(&sTimer);
181  rv = TimerOn();
182  NS_ENSURE_SUCCESS(rv, rv);
183
184  // Mark us as initialized
185  sInitialized = true;
186
187  return NS_OK;
188}
189
190/**
191 * Shut down the tracker, deallocating the timer.
192 */
193void
194DiscardTracker::Shutdown()
195{
196  if (sTimer) {
197    sTimer->Cancel();
198    NS_RELEASE(sTimer);
199    sTimer = nsnull;
200  }
201}
202
203/**
204 * Read the discard timeout from about:config.
205 */
206void
207DiscardTracker::ReloadTimeout()
208{
209  nsresult rv;
210
211  // read the timeout pref
212  PRInt32 discardTimeout;
213  rv = Preferences::GetInt(DISCARD_TIMEOUT_PREF, &discardTimeout);
214
215  // If we got something bogus, return
216  if (!NS_SUCCEEDED(rv) || discardTimeout <= 0)
217    return;
218
219  // If the value didn't change, return
220  if ((PRUint32) discardTimeout == sMinDiscardTimeoutMs)
221    return;
222
223  // Update the value
224  sMinDiscardTimeoutMs = (PRUint32) discardTimeout;
225
226  // If the timer's on, restart the clock to make changes take effect
227  if (sTimerOn) {
228    TimerOff();
229    TimerOn();
230  }
231}
232
233/**
234 * Enables the timer. No-op if the timer is already running.
235 */
236nsresult
237DiscardTracker::TimerOn()
238{
239  // Nothing to do if the timer's already on.
240  if (sTimerOn)
241    return NS_OK;
242  sTimerOn = true;
243
244  // Activate
245  return sTimer->InitWithFuncCallback(TimerCallback,
246                                      nsnull,
247                                      sMinDiscardTimeoutMs,
248                                      nsITimer::TYPE_REPEATING_SLACK);
249}
250
251/*
252 * Disables the timer. No-op if the timer isn't running.
253 */
254void
255DiscardTracker::TimerOff()
256{
257  // Nothing to do if the timer's already off.
258  if (!sTimerOn)
259    return;
260  sTimerOn = false;
261
262  // Deactivate
263  sTimer->Cancel();
264}
265
266/**
267 * Routine activated when the timer fires. This discards everything
268 * in front of sentinel, and resets the sentinel to the back of the
269 * list.
270 */
271void
272DiscardTracker::TimerCallback(nsITimer *aTimer, void *aClosure)
273{
274  DiscardTrackerNode *node;
275
276  // Remove and discard everything before the sentinel
277  for (node = sSentinel.prev; node != &sHead; node = sSentinel.prev) {
278    NS_ABORT_IF_FALSE(node->curr, "empty node!");
279    Remove(node);
280    node->curr->Discard();
281  }
282
283  // Append the sentinel to the back of the list
284  Reset(&sSentinel);
285
286  // If there's nothing in front of the sentinel, the next callback
287  // is guaranteed to be a no-op. Disable the timer as an optimization.
288  if (sSentinel.prev == &sHead)
289    TimerOff();
290}
291
292} // namespace image
293} // namespace mozilla