Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / netwerk / base / src / nsStreamTransportService.cpp
blobe8e31ef0746bed955bf7e2fbde40bbbd903757ba
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is Mozilla.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Darin Fisher <darin@netscape.com>
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.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsStreamTransportService.h"
39 #include "nsXPCOMCIDInternal.h"
40 #include "nsNetSegmentUtils.h"
41 #include "nsAutoLock.h"
42 #include "nsInt64.h"
43 #include "nsTransportUtils.h"
44 #include "nsStreamUtils.h"
45 #include "nsNetError.h"
46 #include "nsNetCID.h"
48 #include "nsIServiceManager.h"
49 #include "nsIAsyncInputStream.h"
50 #include "nsIAsyncOutputStream.h"
51 #include "nsISeekableStream.h"
52 #include "nsIPipe.h"
53 #include "nsITransport.h"
54 #include "nsIRunnable.h"
55 #include "nsIObserverService.h"
56 #include "mozilla/Services.h"
58 //-----------------------------------------------------------------------------
59 // nsInputStreamTransport
61 // Implements nsIInputStream as a wrapper around the real input stream. This
62 // allows the transport to support seeking, range-limiting, progress reporting,
63 // and close-when-done semantics while utilizing NS_AsyncCopy.
64 //-----------------------------------------------------------------------------
66 class nsInputStreamTransport : public nsITransport
67 , public nsIInputStream
69 public:
70 NS_DECL_ISUPPORTS
71 NS_DECL_NSITRANSPORT
72 NS_DECL_NSIINPUTSTREAM
74 nsInputStreamTransport(nsIInputStream *source,
75 PRUint64 offset,
76 PRUint64 limit,
77 PRBool closeWhenDone)
78 : mSource(source)
79 , mOffset(offset)
80 , mLimit(limit)
81 , mCloseWhenDone(closeWhenDone)
82 , mFirstTime(PR_TRUE)
83 , mInProgress(PR_FALSE)
87 virtual ~nsInputStreamTransport()
91 private:
92 nsCOMPtr<nsIAsyncInputStream> mPipeIn;
94 // while the copy is active, these members may only be accessed from the
95 // nsIInputStream implementation.
96 nsCOMPtr<nsITransportEventSink> mEventSink;
97 nsCOMPtr<nsIInputStream> mSource;
98 PRUint64 mOffset;
99 PRUint64 mLimit;
100 PRPackedBool mCloseWhenDone;
101 PRPackedBool mFirstTime;
103 // this variable serves as a lock to prevent the state of the transport
104 // from being modified once the copy is in progress.
105 PRPackedBool mInProgress;
108 NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTransport,
109 nsITransport,
110 nsIInputStream)
112 /** nsITransport **/
114 NS_IMETHODIMP
115 nsInputStreamTransport::OpenInputStream(PRUint32 flags,
116 PRUint32 segsize,
117 PRUint32 segcount,
118 nsIInputStream **result)
120 NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
122 nsresult rv;
123 nsCOMPtr<nsIEventTarget> target =
124 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
125 if (NS_FAILED(rv)) return rv;
127 // XXX if the caller requests an unbuffered stream, then perhaps
128 // we'd want to simply return mSource; however, then we would
129 // not be reading mSource on a background thread. is this ok?
131 PRBool nonblocking = !(flags & OPEN_BLOCKING);
133 net_ResolveSegmentParams(segsize, segcount);
134 nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
136 nsCOMPtr<nsIAsyncOutputStream> pipeOut;
137 rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
138 getter_AddRefs(pipeOut),
139 nonblocking, PR_TRUE,
140 segsize, segcount, segalloc);
141 if (NS_FAILED(rv)) return rv;
143 mInProgress = PR_TRUE;
145 // startup async copy process...
146 rv = NS_AsyncCopy(this, pipeOut, target,
147 NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
148 if (NS_SUCCEEDED(rv))
149 NS_ADDREF(*result = mPipeIn);
151 return rv;
154 NS_IMETHODIMP
155 nsInputStreamTransport::OpenOutputStream(PRUint32 flags,
156 PRUint32 segsize,
157 PRUint32 segcount,
158 nsIOutputStream **result)
160 // this transport only supports reading!
161 NS_NOTREACHED("nsInputStreamTransport::OpenOutputStream");
162 return NS_ERROR_UNEXPECTED;
165 NS_IMETHODIMP
166 nsInputStreamTransport::Close(nsresult reason)
168 if (NS_SUCCEEDED(reason))
169 reason = NS_BASE_STREAM_CLOSED;
171 return mPipeIn->CloseWithStatus(reason);
174 NS_IMETHODIMP
175 nsInputStreamTransport::SetEventSink(nsITransportEventSink *sink,
176 nsIEventTarget *target)
178 NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
180 if (target)
181 return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
182 sink, target);
184 mEventSink = sink;
185 return NS_OK;
188 /** nsIInputStream **/
190 NS_IMETHODIMP
191 nsInputStreamTransport::Close()
193 if (mCloseWhenDone)
194 mSource->Close();
196 // make additional reads return early...
197 mOffset = mLimit = 0;
198 return NS_OK;
201 NS_IMETHODIMP
202 nsInputStreamTransport::Available(PRUint32 *result)
204 return NS_ERROR_NOT_IMPLEMENTED;
207 NS_IMETHODIMP
208 nsInputStreamTransport::Read(char *buf, PRUint32 count, PRUint32 *result)
210 if (mFirstTime) {
211 mFirstTime = PR_FALSE;
212 if (mOffset != 0) {
213 // read from current position if offset equal to max
214 if (mOffset != LL_MAXUINT) {
215 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSource);
216 if (seekable)
217 seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
219 // reset offset to zero so we can use it to enforce limit
220 mOffset = 0;
224 // limit amount read
225 PRUint32 max = mLimit - mOffset;
226 if (max == 0) {
227 *result = 0;
228 return NS_OK;
231 if (count > max)
232 count = max;
234 nsresult rv = mSource->Read(buf, count, result);
236 if (NS_SUCCEEDED(rv)) {
237 mOffset += *result;
238 if (mEventSink)
239 mEventSink->OnTransportStatus(this, STATUS_READING, mOffset, mLimit);
241 return rv;
244 NS_IMETHODIMP
245 nsInputStreamTransport::ReadSegments(nsWriteSegmentFun writer, void *closure,
246 PRUint32 count, PRUint32 *result)
248 return NS_ERROR_NOT_IMPLEMENTED;
251 NS_IMETHODIMP
252 nsInputStreamTransport::IsNonBlocking(PRBool *result)
254 *result = PR_FALSE;
255 return NS_OK;
258 //-----------------------------------------------------------------------------
259 // nsOutputStreamTransport
261 // Implements nsIOutputStream as a wrapper around the real input stream. This
262 // allows the transport to support seeking, range-limiting, progress reporting,
263 // and close-when-done semantics while utilizing NS_AsyncCopy.
264 //-----------------------------------------------------------------------------
266 class nsOutputStreamTransport : public nsITransport
267 , public nsIOutputStream
269 public:
270 NS_DECL_ISUPPORTS
271 NS_DECL_NSITRANSPORT
272 NS_DECL_NSIOUTPUTSTREAM
274 nsOutputStreamTransport(nsIOutputStream *sink,
275 PRUint64 offset,
276 PRUint64 limit,
277 PRBool closeWhenDone)
278 : mSink(sink)
279 , mOffset(offset)
280 , mLimit(limit)
281 , mCloseWhenDone(closeWhenDone)
282 , mFirstTime(PR_TRUE)
283 , mInProgress(PR_FALSE)
287 virtual ~nsOutputStreamTransport()
291 private:
292 nsCOMPtr<nsIAsyncOutputStream> mPipeOut;
294 // while the copy is active, these members may only be accessed from the
295 // nsIOutputStream implementation.
296 nsCOMPtr<nsITransportEventSink> mEventSink;
297 nsCOMPtr<nsIOutputStream> mSink;
298 PRUint64 mOffset;
299 PRUint64 mLimit;
300 PRPackedBool mCloseWhenDone;
301 PRPackedBool mFirstTime;
303 // this variable serves as a lock to prevent the state of the transport
304 // from being modified once the copy is in progress.
305 PRPackedBool mInProgress;
308 NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamTransport,
309 nsITransport,
310 nsIOutputStream)
312 /** nsITransport **/
314 NS_IMETHODIMP
315 nsOutputStreamTransport::OpenInputStream(PRUint32 flags,
316 PRUint32 segsize,
317 PRUint32 segcount,
318 nsIInputStream **result)
320 // this transport only supports writing!
321 NS_NOTREACHED("nsOutputStreamTransport::OpenInputStream");
322 return NS_ERROR_UNEXPECTED;
325 NS_IMETHODIMP
326 nsOutputStreamTransport::OpenOutputStream(PRUint32 flags,
327 PRUint32 segsize,
328 PRUint32 segcount,
329 nsIOutputStream **result)
331 NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
333 nsresult rv;
334 nsCOMPtr<nsIEventTarget> target =
335 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
336 if (NS_FAILED(rv)) return rv;
338 // XXX if the caller requests an unbuffered stream, then perhaps
339 // we'd want to simply return mSink; however, then we would
340 // not be writing to mSink on a background thread. is this ok?
342 PRBool nonblocking = !(flags & OPEN_BLOCKING);
344 net_ResolveSegmentParams(segsize, segcount);
345 nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
347 nsCOMPtr<nsIAsyncInputStream> pipeIn;
348 rv = NS_NewPipe2(getter_AddRefs(pipeIn),
349 getter_AddRefs(mPipeOut),
350 PR_TRUE, nonblocking,
351 segsize, segcount, segalloc);
352 if (NS_FAILED(rv)) return rv;
354 mInProgress = PR_TRUE;
356 // startup async copy process...
357 rv = NS_AsyncCopy(pipeIn, this, target,
358 NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
359 if (NS_SUCCEEDED(rv))
360 NS_ADDREF(*result = mPipeOut);
362 return rv;
365 NS_IMETHODIMP
366 nsOutputStreamTransport::Close(nsresult reason)
368 if (NS_SUCCEEDED(reason))
369 reason = NS_BASE_STREAM_CLOSED;
371 return mPipeOut->CloseWithStatus(reason);
374 NS_IMETHODIMP
375 nsOutputStreamTransport::SetEventSink(nsITransportEventSink *sink,
376 nsIEventTarget *target)
378 NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
380 if (target)
381 return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
382 sink, target);
384 mEventSink = sink;
385 return NS_OK;
388 /** nsIOutputStream **/
390 NS_IMETHODIMP
391 nsOutputStreamTransport::Close()
393 if (mCloseWhenDone)
394 mSink->Close();
396 // make additional writes return early...
397 mOffset = mLimit = 0;
398 return NS_OK;
401 NS_IMETHODIMP
402 nsOutputStreamTransport::Flush()
404 return NS_OK;
407 NS_IMETHODIMP
408 nsOutputStreamTransport::Write(const char *buf, PRUint32 count, PRUint32 *result)
410 if (mFirstTime) {
411 mFirstTime = PR_FALSE;
412 if (mOffset != 0) {
413 // write to current position if offset equal to max
414 if (mOffset != LL_MAXUINT) {
415 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSink);
416 if (seekable)
417 seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
419 // reset offset to zero so we can use it to enforce limit
420 mOffset = 0;
424 // limit amount written
425 PRUint32 max = mLimit - mOffset;
426 if (max == 0) {
427 *result = 0;
428 return NS_OK;
431 if (count > max)
432 count = max;
434 nsresult rv = mSink->Write(buf, count, result);
436 if (NS_SUCCEEDED(rv)) {
437 mOffset += *result;
438 if (mEventSink)
439 mEventSink->OnTransportStatus(this, STATUS_WRITING, mOffset, mLimit);
441 return rv;
444 NS_IMETHODIMP
445 nsOutputStreamTransport::WriteSegments(nsReadSegmentFun reader, void *closure,
446 PRUint32 count, PRUint32 *result)
448 return NS_ERROR_NOT_IMPLEMENTED;
451 NS_IMETHODIMP
452 nsOutputStreamTransport::WriteFrom(nsIInputStream *in, PRUint32 count, PRUint32 *result)
454 return NS_ERROR_NOT_IMPLEMENTED;
457 NS_IMETHODIMP
458 nsOutputStreamTransport::IsNonBlocking(PRBool *result)
460 *result = PR_FALSE;
461 return NS_OK;
464 //-----------------------------------------------------------------------------
465 // nsStreamTransportService
466 //-----------------------------------------------------------------------------
468 nsStreamTransportService::~nsStreamTransportService()
470 NS_ASSERTION(!mPool, "thread pool wasn't shutdown");
473 nsresult
474 nsStreamTransportService::Init()
476 mPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
477 NS_ENSURE_STATE(mPool);
479 // Configure the pool
480 mPool->SetThreadLimit(4);
481 mPool->SetIdleThreadLimit(1);
482 mPool->SetIdleThreadTimeout(PR_SecondsToInterval(60));
484 nsCOMPtr<nsIObserverService> obsSvc =
485 mozilla::services::GetObserverService();
486 if (obsSvc)
487 obsSvc->AddObserver(this, "xpcom-shutdown-threads", PR_FALSE);
488 return NS_OK;
491 NS_IMPL_THREADSAFE_ISUPPORTS3(nsStreamTransportService,
492 nsIStreamTransportService,
493 nsIEventTarget,
494 nsIObserver)
496 NS_IMETHODIMP
497 nsStreamTransportService::Dispatch(nsIRunnable *task, PRUint32 flags)
499 NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED);
500 return mPool->Dispatch(task, flags);
503 NS_IMETHODIMP
504 nsStreamTransportService::IsOnCurrentThread(PRBool *result)
506 NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED);
507 return mPool->IsOnCurrentThread(result);
510 NS_IMETHODIMP
511 nsStreamTransportService::CreateInputTransport(nsIInputStream *stream,
512 PRInt64 offset,
513 PRInt64 limit,
514 PRBool closeWhenDone,
515 nsITransport **result)
517 nsInputStreamTransport *trans =
518 new nsInputStreamTransport(stream, offset, limit, closeWhenDone);
519 if (!trans)
520 return NS_ERROR_OUT_OF_MEMORY;
521 NS_ADDREF(*result = trans);
522 return NS_OK;
525 NS_IMETHODIMP
526 nsStreamTransportService::CreateOutputTransport(nsIOutputStream *stream,
527 PRInt64 offset,
528 PRInt64 limit,
529 PRBool closeWhenDone,
530 nsITransport **result)
532 nsOutputStreamTransport *trans =
533 new nsOutputStreamTransport(stream, offset, limit, closeWhenDone);
534 if (!trans)
535 return NS_ERROR_OUT_OF_MEMORY;
536 NS_ADDREF(*result = trans);
537 return NS_OK;
540 NS_IMETHODIMP
541 nsStreamTransportService::Observe(nsISupports *subject, const char *topic,
542 const PRUnichar *data)
544 NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
546 if (mPool) {
547 mPool->Shutdown();
548 mPool = nsnull;
550 return NS_OK;