sbDeviceRequestThreadQueue.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4  *=BEGIN SONGBIRD GPL
5  *
6  * This file is part of the Songbird web player.
7  *
8  * Copyright(c) 2005-2011 POTI, Inc.
9  * http://www.songbirdnest.com
10  *
11  * This file may be licensed under the terms of of the
12  * GNU General Public License Version 2 (the ``GPL'').
13  *
14  * Software distributed under the License is distributed
15  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
16  * express or implied. See the GPL for the specific language
17  * governing rights and limitations.
18  *
19  * You should have received a copy of the GPL along with this
20  * program. If not, go to http://www.gnu.org/licenses/gpl.html
21  * or write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  *
24  *=END SONGBIRD GPL
25  */
26 
28 
29 // Local includes
30 #include "sbBaseDevice.h"
31 #include "sbRequestItem.h"
32 
33 // Mozilla includes
34 #include <nsArrayUtils.h>
35 
36 // Songbird includes
37 #include <sbDebugUtils.h>
38 #include <sbPropertiesCID.h>
39 #include <sbVariantUtils.h>
40 
41 // Songbird interfaces
42 #include <sbIDeviceEvent.h>
43 
45 {
46  sbDeviceRequestThreadQueue * newObject;
47  NS_NEWXPCOM(newObject, sbDeviceRequestThreadQueue);
48  return newObject;
49 }
50 
52 {
53  TRACE_FUNCTION("");
54 
55  NS_ENSURE_ARG_POINTER(aBaseDevice);
56  sbIDevice * baseDevice = mBaseDevice;
57  NS_IF_RELEASE(baseDevice);
58  mBaseDevice = aBaseDevice;
59  NS_ADDREF(static_cast<sbIDevice *>(mBaseDevice));
60  nsresult rv = sbRequestThreadQueue::Start();
61  NS_ENSURE_SUCCESS(rv, rv);
62 
63  return NS_OK;
64 }
65 
66 sbDeviceRequestThreadQueue::sbDeviceRequestThreadQueue() :
67  mBaseDevice(nsnull)
68 {
70 }
71 
72 sbDeviceRequestThreadQueue::~sbDeviceRequestThreadQueue()
73 {
74  TRACE_FUNCTION("");
75  sbIDevice * device = mBaseDevice;
76  mBaseDevice = nsnull;
77  NS_IF_RELEASE(device);
78 }
79 
84 {
85 public:
87 
92  static bool CompareItems(sbIMediaItem * aLeft, sbIMediaItem * aRight)
93  {
94  PRBool same = PR_FALSE;
95  // They're the same if neither is specified or both are and they are "equal"
96  return (!aLeft && !aRight) ||
97  (aLeft && aRight && NS_SUCCEEDED(aLeft->Equals(aRight, &same)) &&
98  same);
99  }
100 
107  sbBaseDevice::TransferRequest * aRequest2)
108  {
109  NS_ENSURE_TRUE(aRequest1, false);
110  NS_ENSURE_TRUE(aRequest2, false);
111 
112  return CompareItems(aRequest1->item, aRequest2->item) &&
113  CompareItems(aRequest1->list, aRequest2->list);
114  }
115 
130  static bool DupeCheck(sbBaseDevice::TransferRequest * aQueueRequest,
131  sbBaseDevice::TransferRequest * aNewRequest,
132  bool & aIsDupe);
133 };
134 
136  sbBaseDevice::TransferRequest * aQueueRequest,
137  sbBaseDevice::TransferRequest * aNewRequest,
138  bool & aIsDupe)
139 {
140  PRUint32 queueType = aQueueRequest->GetType();
141  PRUint32 newType = aNewRequest->GetType();
142 
143  // Default to not a duplicate
144  aIsDupe = false;
145  // Check the type of the request being added
146  switch (newType) {
148  // is this a write to a playlist?
149  if (aNewRequest->IsPlaylist()) {
150  // And the request on queue is a playlist operation
151  if (aQueueRequest->IsPlaylist()) {
152  switch (queueType) {
153 
154  // If the queue request is a playlist operation for the same
155  // playlist dupe it
159  aIsDupe = CompareItems(aNewRequest->list, aQueueRequest->list);
160  return aIsDupe;
161  default:
162  return false;
163  }
164  }
165 
166  // queue request wasn't a playlist operation so check for creation,
167  // deletion, and updating of playlists
168  switch (queueType) {
169  // If we find a delete playlist, look no further, but it is not a dupe
171  aIsDupe = false;
172  return CompareItems(aNewRequest->list, aQueueRequest->item);
173 
174  // If we find a new playlist request for the same playlist dupe it
176 
177  // If we find an update playlist request for the same playlist
178  // dupe it
180  aIsDupe = CompareItems(aNewRequest->list, aQueueRequest->item);
181  return aIsDupe;
182 
183  default:
184  return false;
185  }
186  }
187 
188  // Not a playlist operation, if it's the same item and
189  // type then dupe it
190  aIsDupe = (queueType == TransferRequest::REQUEST_WRITE) &&
191  CompareRequestItems(aQueueRequest, aNewRequest);
192  return aIsDupe;
193  }
195  // If we're dealing with a remove item from playlist
196  if (aNewRequest->IsPlaylist()) {
197  // If the playlist on the queue is the same as this one
198  if (CompareItems(aNewRequest->list, aQueueRequest->list)) {
199  switch (queueType) {
204  aIsDupe = true;
205  return true;
206  }
207  default:
208  return false;
209  break;
210  }
211  }
212  return false;
213  }
214  // If the item we're deleting has a previous request in the queue
215  // remove it unless it's another delete then dupe it
216  if (CompareRequestItems(aQueueRequest, aNewRequest)) {
217  switch (queueType) {
221  // Stop, but not a dupe
222  return true;
224  aIsDupe = true;
225  return true;
226  default:
227  return false;
228  }
229  }
230  return false;
231  }
233  // If we have new playlist request for the same item dupe this one
234  if (queueType == newType) {
235  aIsDupe = CompareItems(aNewRequest->item, aQueueRequest->item);
236  return aIsDupe;
237  }
238  // If we find a delete request for this new playlist then we want to
239  // stop looking in the queue
240  return (queueType == TransferRequest::REQUEST_DELETE) &&
241  CompareItems(aNewRequest->item, aQueueRequest->item);
242  }
244  if (aNewRequest->IsPlaylist()) {
245  switch (queueType) {
246  // Move after creation is unnecessary, dupe it
248  aIsDupe = CompareItems(aNewRequest->list, aNewRequest->item);
249  return aIsDupe;
250  // Move after writing is unnecessary, dupe it
252  aIsDupe = aNewRequest->IsPlaylist() &&
253  CompareItems(aNewRequest->list, aQueueRequest->list);
254  return aIsDupe;
255  // Move after update is unnecessary, dupe it
257  aIsDupe = CompareItems(aNewRequest->list, aQueueRequest->item);
258  return aIsDupe;
259  }
260  default:
261  return false;
262  }
263  }
264  return false;
265  }
267  // If the queue item is a playlist operation then this request can
268  // be treated as a dupe
269  if (aQueueRequest->IsPlaylist()) {
270  aIsDupe = CompareItems(aNewRequest->item, aQueueRequest->list);
271  return aIsDupe;
272  }
273  switch (queueType) {
277  aIsDupe = CompareRequestItems(aQueueRequest, aNewRequest);
278  return aIsDupe;
279 
281  aIsDupe = CompareRequestItems(aQueueRequest, aNewRequest);
282  return aIsDupe;
283 
284  default:
285  return false;
286  }
287  }
295  default: {
296  aIsDupe = CompareRequestItems(aQueueRequest, aNewRequest) &&
297  newType == queueType;
298  return aIsDupe;
299  }
300  }
301  // We should never get here, but some compilers erroneously throw a warning
302  // if we don't have a return
303  return false;
304 }
305 
306 nsresult
307 sbDeviceRequestThreadQueue::IsDuplicateRequest(sbRequestItem * aQueueRequest,
308  sbRequestItem * aRequest,
309  bool & aIsDuplicate,
310  bool & aContinueChecking)
311 {
312  NS_ENSURE_ARG_POINTER(aQueueRequest);
313  NS_ENSURE_ARG_POINTER(aRequest);
314 
315  nsresult rv;
316 
317  sbBaseDevice::TransferRequest * queueRequest =
318  static_cast<sbBaseDevice::TransferRequest*>(aQueueRequest);
319 
321  static_cast<sbBaseDevice::TransferRequest*>(aRequest);
322 
323  PRUint32 type = request->GetType();
324 
325  // reverse search through the queue for a comparable request
326  bool isDuplicate = PR_FALSE;
327  PRBool continueChecking =
329  request,
330  isDuplicate);
331 
332 
333  // If the requests are item update requests, merge the updated properties.
334  if (isDuplicate) {
336  !request->IsPlaylist()) {
337  // Create a new, merged property array.
338  nsCOMPtr<sbIMutablePropertyArray>
339  mergedPropertyList =
340  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
341  NS_ENSURE_SUCCESS(rv, rv);
342 
343  // Add the duplicate in the queue properties.
344  nsCOMPtr<sbIPropertyArray> propertyList;
345  propertyList = do_QueryInterface(queueRequest->data, &rv);
346  // This may be a request that doesn't contain a property array
347  if (NS_SUCCEEDED(rv)) {
348  rv = mergedPropertyList->AppendProperties(propertyList, PR_TRUE);
349  NS_ENSURE_SUCCESS(rv, rv);
350  }
351  // Add the new request properties.
352  propertyList = do_QueryInterface(request->data, &rv);
353  NS_ENSURE_SUCCESS(rv, rv);
354  rv = mergedPropertyList->AppendProperties(propertyList, PR_TRUE);
355  NS_ENSURE_SUCCESS(rv, rv);
356 
357  // Change the duplicate in the queue properties to the merged set.
358  queueRequest->data = mergedPropertyList;
359  }
360  // If we found a duplicate playlist request, we may need to change it to
361  // update request. Currently all devices recreate playlists on any
362  // modification.
363  else if (request->IsPlaylist()) {
364  PRUint32 queueType = aQueueRequest->GetType();
365 
366  // If the previous request was a write or move then change it to an update
367  switch (queueType) {
372 
373  queueRequest->item = queueRequest->list;
374  nsCOMPtr<sbILibrary> library;
375  queueRequest->list->GetLibrary(getter_AddRefs(library));
376  queueRequest->list = library;
377  }
378  break;
380  // Just ignore the update request if we have a new playlist already
381  // in the queue
382  break;
383  }
384  }
385  }
386  aIsDuplicate = isDuplicate;
387  aContinueChecking = continueChecking;
388 
389  return NS_OK;
390 }
391 
392 void sbDeviceRequestThreadQueue::CompleteRequests() {
393 
395 
396  {
397  nsAutoLock lock(mLock);
399  nsresult rv = mBaseDevice->ChangeState(sbIDevice::STATE_IDLE);
400  if (NS_FAILED(rv)) {
401  NS_WARNING("Failed to clear cancel state of aborted device");
402  }
403  }
404  }
405 }
406 
407 nsresult sbDeviceRequestThreadQueue::CleanupBatch(Batch & aBatch)
408 {
409  TRACE_FUNCTION("");
410 
411  nsresult rv;
412  nsInterfaceHashtable<nsISupportsHashKey, nsIMutableArray> groupedItems;
413  groupedItems.Init();
414 
415  // Accumulate all the items that have not been processed so we can remove
416  // their corresponding items from the library.
417  const Batch::const_iterator end = aBatch.end();
418  for (Batch::const_iterator iter = aBatch.begin();
419  iter != end;
420  ++iter) {
422  static_cast<sbBaseDevice::TransferRequest *>(*iter);
423 
424  // If the request was processed nothing to cleanup
425  bool processed = request->GetIsProcessed();
426  if (processed) {
427  continue;
428  }
429 
430  PRUint32 type = request->GetType();
431 
432  // If this is a request that adds an item to the device we need to remove
433  // it from the device since it never was copied. We store these via
434  // media list so that we can use the batch logic to efficiently remove
435  // them.
436  switch (type) {
439  if (request->item) {
440  nsCOMPtr<nsIMutableArray> items;
441  groupedItems.Get(request->list, getter_AddRefs(items));
442  if (!items) {
443  items = do_CreateInstance(
444  "@songbirdnest.com/moz/xpcom/threadsafe-array;1",
445  &rv);
446  NS_ENSURE_TRUE(groupedItems.Put(request->list, items),
447  NS_ERROR_OUT_OF_MEMORY);
448  }
449  rv = items->AppendElement(request->item, PR_FALSE);
450  NS_ENSURE_SUCCESS(rv, rv);
451  }
452  }
453  continue;
454  }
455  }
456  // If there are no items, don't bother do anything.
457  if (groupedItems.Count() > 0 && mBaseDevice->mLibraryListener) {
458  sbBaseDevice::AutoListenerIgnore ignore(mBaseDevice);
459  groupedItems.Enumerate(RemoveLibraryEnumerator, mBaseDevice);
460  }
461 
462  return NS_OK;
463 }
464 
465 nsresult sbDeviceRequestThreadQueue::OnThreadStop()
466 {
467  TRACE_FUNCTION("");
468 
469  nsresult rv;
470 
471  if (mBaseDevice) {
472  rv = mBaseDevice->DeviceSpecificDisconnect();
473  NS_ENSURE_SUCCESS(rv, rv);
474 
475  // Now notify everyone else that we have been removed.
478  sbNewVariant(static_cast<sbIDevice*>(mBaseDevice)));
479 
480  sbIDevice * device = mBaseDevice;
481  mBaseDevice = nsnull;
482  NS_IF_RELEASE(device);
483  }
484  else {
485  NS_WARNING("mBaseDevice is null in "
486  "sbDeviceRequestThreadActions::OnThreadShutdown");
487  }
488  return NS_OK;
489 }
490 
491 PLDHashOperator
492 sbDeviceRequestThreadQueue::RemoveLibraryEnumerator(
493  nsISupports * aList,
494  nsCOMPtr<nsIMutableArray> & aItems,
495  void * aUserArg)
496 {
497  NS_ENSURE_TRUE(aList, PL_DHASH_NEXT);
498  NS_ENSURE_TRUE(aItems, PL_DHASH_NEXT);
499 
500  sbBaseDevice * const device =
501  static_cast<sbBaseDevice*>(aUserArg);
502 
503  NS_ASSERTION(device->mLibraryListener,
504  "sbDeviceRequestThreadQueue::RemoveLibraryEnumerator called "
505  "with a device that does not have mLibraryListener set");
506  sbBaseDevice::AutoListenerIgnore ignore(device);
507 
508  nsCOMPtr<nsISimpleEnumerator> enumerator;
509  nsresult rv = aItems->Enumerate(getter_AddRefs(enumerator));
510  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
511 
512  nsCOMPtr<sbIMediaList> list = do_QueryInterface(aList);
513  if (list) {
514  rv = list->RemoveSome(enumerator);
515  NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
516  }
517 
518  return PL_DHASH_NEXT;
519 }
520 
521 nsresult
522 sbDeviceRequestThreadQueue::ProcessBatch(Batch & aBatch)
523 {
524  TRACE_FUNCTION("");
525 
526  NS_ENSURE_STATE(mBaseDevice);
527 
528  nsresult rv;
529 
530  rv = mBaseDevice->ProcessBatch(aBatch);
531  NS_ENSURE_SUCCESS(rv, rv);
532 
533  return NS_OK;
534 }
#define SB_PRLOG_SETUP(x)
Definition: sbDebugUtils.h:115
return NS_OK
bool GetIsProcessed() const
virtual nsresult ChangeState(PRUint32 aState)
PRUint32 GetType() const
Definition: sbRequestItem.h:65
virtual void CompleteRequests()
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
nsRefPtr< sbBaseDeviceLibraryListener > mLibraryListener
Definition: sbBaseDevice.h:693
Songbird Variant Utility Definitions.
static bool DupeCheck(sbBaseDevice::TransferRequest *aQueueRequest, sbBaseDevice::TransferRequest *aNewRequest, bool &aIsDupe)
static sbDeviceRequestThreadQueue * New()
static bool CompareItems(sbIMediaItem *aLeft, sbIMediaItem *aRight)
restoreDimensions aLeft
sbBaseDevice::TransferRequest TransferRequest
RequestItems::const_iterator const_iterator
const unsigned long STATE_IDLE
Definition: sbIDevice.idl:220
#define TRACE_FUNCTION(...)
Definition: sbDebugUtils.h:118
nsresult CreateAndDispatchDeviceManagerEvent(PRUint32 aType, nsIVariant *aData, PRBool aAsync=PR_TRUE)
const unsigned long EVENT_DEVICE_REMOVED
nsCOMPtr< sbIMediaItem > item
Definition: sbBaseDevice.h:155
Interface that defines a single item of media in the system.
void SetType(PRUint32 aType)
Definition: sbRequestItem.h:73
virtual nsresult ProcessBatch(Batch &aBatch)=0
nsCOMPtr< sbIMediaList > list
Definition: sbBaseDevice.h:156
static bool CompareRequestItems(sbBaseDevice::TransferRequest *aRequest1, sbBaseDevice::TransferRequest *aRequest2)
nsCOMPtr< nsISupports > data
Definition: sbBaseDevice.h:157