sbBackgroundThreadMetadataProcessor.cpp
Go to the documentation of this file.
1 /* vim: set sw=2 :miv */
2 /*
3 //
4 // BEGIN SONGBIRD GPL
5 //
6 // This file is part of the Songbird web player.
7 //
8 // Copyright(c) 2005-2008 POTI, Inc.
9 // http://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 */
27 
33 // INCLUDES ===================================================================
34 #include <nspr.h>
35 #include <nscore.h>
36 #include <nsThreadUtils.h>
37 #include <nsComponentManagerUtils.h>
38 
40 #include "sbFileMetadataService.h"
41 #include "sbMetadataJobItem.h"
42 
43 #include "prlog.h"
44 
45 // DEFINES ====================================================================
46 
47 #ifdef PR_LOGGING
48 extern PRLogModuleInfo* gMetadataLog;
49 #define TRACE(args) PR_LOG(gMetadataLog, PR_LOG_DEBUG, args)
50 #define LOG(args) PR_LOG(gMetadataLog, PR_LOG_WARN, args)
51 #else
52 #define TRACE(args) /* nothing */
53 #define LOG(args) /* nothing */
54 #endif
55 
56 // GLOBALS ====================================================================
57 // CLASSES ====================================================================
58 
60 
62  sbFileMetadataService* aManager) :
63  mJobManager(aManager),
64  mThread(nsnull),
65  mShouldShutdown(PR_FALSE),
66  mMonitor(nsnull)
67 {
69  TRACE(("sbBackgroundThreadMetadataProcessor[0x%.8x] - ctor", this));
70  NS_ASSERTION(NS_IsMainThread(),
71  "sbBackgroundThreadMetadataProcessor called off the main thread");
72 }
73 
75 {
77  TRACE(("sbBackgroundThreadMetadataProcessor[0x%.8x] - dtor", this));
78  Stop();
79  mThread = nsnull;
80  mJobManager = nsnull;
81  if (mMonitor) {
82  nsAutoMonitor::DestroyMonitor(mMonitor);
83  }
84 }
85 
87 {
88  NS_ENSURE_STATE(mJobManager);
89  NS_ASSERTION(NS_IsMainThread(),
90  "sbBackgroundThreadMetadataProcessor called off the main thread!");
91  TRACE(("sbBackgroundThreadMetadataProcessor[0x%.8x] - Start", this));
92  nsresult rv;
93 
94  if (!mMonitor) {
95  mMonitor = nsAutoMonitor::NewMonitor(
96  "sbBackgroundThreadMetadataProcessor::mMonitor");
97  NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
98  }
99 
100  nsAutoMonitor monitor(mMonitor);
101 
102  if (!mThread) {
103  mShouldShutdown = PR_FALSE;
104  rv = NS_NewThread(getter_AddRefs(mThread), this);
105  NS_ENSURE_SUCCESS(rv, rv);
106  }
107 
108  rv = monitor.Notify();
109  NS_ASSERTION(NS_SUCCEEDED(rv),
110  "sbBackgroundThreadMetadataProcessor::Start monitor notify failed");
111 
112  return NS_OK;
113 }
114 
116 {
117  NS_ASSERTION(NS_IsMainThread(),
118  "sbBackgroundThreadMetadataProcessor called off the main thread");
119  TRACE(("sbBackgroundThreadMetadataProcessor[0x%.8x] - Stop", this));
120  nsresult rv;
121 
122  {
123  nsAutoMonitor monitor(mMonitor);
124  // Tell the thread to stop working
125  mShouldShutdown = PR_TRUE;
126 
127  rv = monitor.Notify();
128  NS_ASSERTION(NS_SUCCEEDED(rv),
129  "sbBackgroundThreadMetadataProcessor::Start monitor notify failed");
130  }
131 
132  // Wait for the thread to shutdown and release all resources.
133  if (mThread) {
134  mThread->Shutdown();
135  mThread = nsnull;
136  }
137 
138  return NS_OK;
139 }
140 
141 
145 NS_IMETHODIMP sbBackgroundThreadMetadataProcessor::Run()
146 {
147  TRACE(("sbBackgroundThreadMetadataProcessor[0x%.8x] - Thread Starting",
148  this));
149  nsresult rv;
150 
151  while (!mShouldShutdown) {
152 
153  // Get the next background job item
154  nsRefPtr<sbMetadataJobItem> item;
155 
156  // Lock to make sure we dont go to sleep right after
157  // a Start() call
158  {
159  nsAutoMonitor monitor(mMonitor);
160 
161  rv = mJobManager->GetQueuedJobItem(PR_FALSE, getter_AddRefs(item));
162 
163  // On error, skip the item and try again
164  if (NS_FAILED(rv)) {
165  // If there are no more job items available just go to sleep.
166  if (rv == NS_ERROR_NOT_AVAILABLE) {
167  TRACE(("sbBackgroundThreadMetadataProcessor[0x%.8x] - Thread waiting",
168  this));
169  rv = monitor.Wait();
170  NS_ASSERTION(NS_SUCCEEDED(rv),
171  "sbBackgroundThreadMetadataProcessor::Run monitor wait failed");
172 
173  } else {
174  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run encountered "
175  " an error while getting a job item.");
176  }
177 
178  continue;
179  }
180  }
181  NS_ENSURE_SUCCESS(rv, rv);
182 
183  // Get the job owning the job item.
184  nsRefPtr<sbMetadataJob> job;
185  rv = item->GetOwningJob(getter_AddRefs(job));
186  NS_ENSURE_SUCCESS(rv, rv);
187 
188  // Start the metadata handler for this job item.
189  nsCOMPtr<sbIMetadataHandler> handler;
190  rv = item->GetHandler(getter_AddRefs(handler));
191  // On error just wait, rather than killing the thread
192  if (!NS_SUCCEEDED(rv)) {
193  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run unable "
194  " to get an sbIMetadataHandler.");
195  continue;
196  }
197 
198  sbMetadataJob::JobType jobType;
199  rv = item->GetJobType(&jobType);
200  if (!NS_SUCCEEDED(rv)) {
201  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run unable "
202  " to determine job type.");
203  continue;
204  }
205 
206  // If the job item is blocked, wait until it is no longer blocked.
207  PRBool skipItem = PR_FALSE;
208  while (1) {
209  // Check if job item is blocked.
210  PRBool jobItemIsBlocked;
211  rv = mJobManager->GetJobItemIsBlocked(item, &jobItemIsBlocked);
212  if (NS_FAILED(rv)) {
213  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run unable "
214  " to determine if job item is blocked.");
215  skipItem = PR_TRUE;
216  break;
217  }
218 
219  // Stop waiting if job item is not blocked.
220  if (!jobItemIsBlocked)
221  break;
222 
223  // Set the job as blocked.
224  rv = job->SetBlocked(PR_TRUE);
225  if (NS_FAILED(rv)) {
226  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run unable "
227  " to set job blocked.");
228  skipItem = PR_TRUE;
229  break;
230  }
231 
232  // Wait a bit and check again.
233  PR_Sleep(PR_MillisecondsToInterval(20));
234  }
235  if (skipItem)
236  continue;
237 
238  // Set the job as not blocked.
239  rv = job->SetBlocked(PR_FALSE);
240  if (NS_FAILED(rv)) {
241  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run unable "
242  " to set job not blocked.");
243  continue;
244  }
245 
246  PRBool async = PR_FALSE;
247  PRInt32 operationRetVal;
248  if (jobType == sbMetadataJob::TYPE_WRITE) {
249  rv = handler->Write(&operationRetVal);
250  } else {
251  rv = handler->Read(&operationRetVal);
252  }
253 
254  // According to |sbIMetadataHandler| |write()| or |read()| will return
255  // -1 if the operation is async, or the number of metadata values written.
256  // (0 if failure).
257  if (operationRetVal == -1) {
258  async = PR_TRUE;
259  }
260 
261  // If we were able to start the handler,
262  // make sure it completes.
263  if (NS_SUCCEEDED(rv)) {
264  rv = item->SetProcessingStarted(PR_TRUE);
265  if (NS_FAILED(rv)) {
266  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run unable "
267  " to set item processing started.");
268  }
269 
270  PRBool handlerCompleted = PR_FALSE;
271  rv = handler->GetCompleted(&handlerCompleted);
272  if (!NS_SUCCEEDED(rv)) {
273  NS_ERROR("sbBackgroundThreadMetadataProcessor::Run unable "
274  " to determine check handler completed.");
275  }
276 
277  // All handler implementations should complete
278  // syncronously at the moment when running
279  // on the background thread.
280 
281  // However, the stupid sbIHandlerInterface
282  // allows async operations, so we should make
283  // sure it is possible.
284  if (async && !handlerCompleted) {
285  NS_WARNING("Attempting to run an async sbIMetadataHandler on " \
286  "a background thread! This may not work correctly!");
287  int counter = 0;
288  // Spend max 500ms pumping the event queue and waiting
289  // for the handler to complete. If it doesn't finish,
290  // give up. This code was ported from the original
291  // MetadataJob implementation.
292  for (handler->GetCompleted(&handlerCompleted);
293  !handlerCompleted && !(mShouldShutdown) && counter < 25;
294  handler->GetCompleted(&handlerCompleted), counter++) {
295 
296  // Run at most 10 messages.
297  PRBool event = PR_FALSE;
298  int eventCount = 0;
299  for (mThread->ProcessNextEvent(PR_FALSE, &event);
300  event && eventCount < 10;
301  mThread->ProcessNextEvent(PR_FALSE, &event), eventCount++) {
302  PR_Sleep(PR_MillisecondsToInterval(0));
303  }
304  // Sleep at least 20ms
305  PR_Sleep(PR_MillisecondsToInterval(20));
306  }
307  }
308 
309  // Indicate that we processed this item
310  item->SetProcessed(PR_TRUE);
311 
312  TRACE(("sbBackgroundThreadMetadataProcessor - item processed"));
313  }
314 
315  // And finally give back the item
316  mJobManager->PutProcessedJobItem(item);
317  }
318 
319  TRACE(("sbBackgroundThreadMetadataProcessor[0x%.8x] - Thread Finished", this));
320  return NS_OK;
321 }
322 
Runs sbIMetadataHandlers on a background thread.
return NS_OK
var event
Manages reading and writing metadata to and from sbIMediaItem objects and media files.
NS_IMPL_THREADSAFE_ISUPPORTS1(sbBackgroundThreadMetadataProcessor, nsIRunnable)
NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE sbBackgroundThreadMetadataProcessor(sbFileMetadataService *aManager)
A metadata job task.
GstMessage gpointer data sbGStreamerMessageHandler * handler
Coordinates reading and writing of media file metadata.