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
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.
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"
43 #include "nsTransportUtils.h"
44 #include "nsStreamUtils.h"
45 #include "nsNetError.h"
48 #include "nsIServiceManager.h"
49 #include "nsIAsyncInputStream.h"
50 #include "nsIAsyncOutputStream.h"
51 #include "nsISeekableStream.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
72 NS_DECL_NSIINPUTSTREAM
74 nsInputStreamTransport(nsIInputStream
*source
,
81 , mCloseWhenDone(closeWhenDone
)
83 , mInProgress(PR_FALSE
)
87 virtual ~nsInputStreamTransport()
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
;
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
,
115 nsInputStreamTransport::OpenInputStream(PRUint32 flags
,
118 nsIInputStream
**result
)
120 NS_ENSURE_TRUE(!mInProgress
, NS_ERROR_IN_PROGRESS
);
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
);
155 nsInputStreamTransport::OpenOutputStream(PRUint32 flags
,
158 nsIOutputStream
**result
)
160 // this transport only supports reading!
161 NS_NOTREACHED("nsInputStreamTransport::OpenOutputStream");
162 return NS_ERROR_UNEXPECTED
;
166 nsInputStreamTransport::Close(nsresult reason
)
168 if (NS_SUCCEEDED(reason
))
169 reason
= NS_BASE_STREAM_CLOSED
;
171 return mPipeIn
->CloseWithStatus(reason
);
175 nsInputStreamTransport::SetEventSink(nsITransportEventSink
*sink
,
176 nsIEventTarget
*target
)
178 NS_ENSURE_TRUE(!mInProgress
, NS_ERROR_IN_PROGRESS
);
181 return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink
),
188 /** nsIInputStream **/
191 nsInputStreamTransport::Close()
196 // make additional reads return early...
197 mOffset
= mLimit
= 0;
202 nsInputStreamTransport::Available(PRUint32
*result
)
204 return NS_ERROR_NOT_IMPLEMENTED
;
208 nsInputStreamTransport::Read(char *buf
, PRUint32 count
, PRUint32
*result
)
211 mFirstTime
= PR_FALSE
;
213 // read from current position if offset equal to max
214 if (mOffset
!= LL_MAXUINT
) {
215 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mSource
);
217 seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, mOffset
);
219 // reset offset to zero so we can use it to enforce limit
225 PRUint32 max
= mLimit
- mOffset
;
234 nsresult rv
= mSource
->Read(buf
, count
, result
);
236 if (NS_SUCCEEDED(rv
)) {
239 mEventSink
->OnTransportStatus(this, STATUS_READING
, mOffset
, mLimit
);
245 nsInputStreamTransport::ReadSegments(nsWriteSegmentFun writer
, void *closure
,
246 PRUint32 count
, PRUint32
*result
)
248 return NS_ERROR_NOT_IMPLEMENTED
;
252 nsInputStreamTransport::IsNonBlocking(PRBool
*result
)
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
272 NS_DECL_NSIOUTPUTSTREAM
274 nsOutputStreamTransport(nsIOutputStream
*sink
,
277 PRBool closeWhenDone
)
281 , mCloseWhenDone(closeWhenDone
)
282 , mFirstTime(PR_TRUE
)
283 , mInProgress(PR_FALSE
)
287 virtual ~nsOutputStreamTransport()
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
;
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
,
315 nsOutputStreamTransport::OpenInputStream(PRUint32 flags
,
318 nsIInputStream
**result
)
320 // this transport only supports writing!
321 NS_NOTREACHED("nsOutputStreamTransport::OpenInputStream");
322 return NS_ERROR_UNEXPECTED
;
326 nsOutputStreamTransport::OpenOutputStream(PRUint32 flags
,
329 nsIOutputStream
**result
)
331 NS_ENSURE_TRUE(!mInProgress
, NS_ERROR_IN_PROGRESS
);
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
);
366 nsOutputStreamTransport::Close(nsresult reason
)
368 if (NS_SUCCEEDED(reason
))
369 reason
= NS_BASE_STREAM_CLOSED
;
371 return mPipeOut
->CloseWithStatus(reason
);
375 nsOutputStreamTransport::SetEventSink(nsITransportEventSink
*sink
,
376 nsIEventTarget
*target
)
378 NS_ENSURE_TRUE(!mInProgress
, NS_ERROR_IN_PROGRESS
);
381 return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink
),
388 /** nsIOutputStream **/
391 nsOutputStreamTransport::Close()
396 // make additional writes return early...
397 mOffset
= mLimit
= 0;
402 nsOutputStreamTransport::Flush()
408 nsOutputStreamTransport::Write(const char *buf
, PRUint32 count
, PRUint32
*result
)
411 mFirstTime
= PR_FALSE
;
413 // write to current position if offset equal to max
414 if (mOffset
!= LL_MAXUINT
) {
415 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mSink
);
417 seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, mOffset
);
419 // reset offset to zero so we can use it to enforce limit
424 // limit amount written
425 PRUint32 max
= mLimit
- mOffset
;
434 nsresult rv
= mSink
->Write(buf
, count
, result
);
436 if (NS_SUCCEEDED(rv
)) {
439 mEventSink
->OnTransportStatus(this, STATUS_WRITING
, mOffset
, mLimit
);
445 nsOutputStreamTransport::WriteSegments(nsReadSegmentFun reader
, void *closure
,
446 PRUint32 count
, PRUint32
*result
)
448 return NS_ERROR_NOT_IMPLEMENTED
;
452 nsOutputStreamTransport::WriteFrom(nsIInputStream
*in
, PRUint32 count
, PRUint32
*result
)
454 return NS_ERROR_NOT_IMPLEMENTED
;
458 nsOutputStreamTransport::IsNonBlocking(PRBool
*result
)
464 //-----------------------------------------------------------------------------
465 // nsStreamTransportService
466 //-----------------------------------------------------------------------------
468 nsStreamTransportService::~nsStreamTransportService()
470 NS_ASSERTION(!mPool
, "thread pool wasn't shutdown");
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();
487 obsSvc
->AddObserver(this, "xpcom-shutdown-threads", PR_FALSE
);
491 NS_IMPL_THREADSAFE_ISUPPORTS3(nsStreamTransportService
,
492 nsIStreamTransportService
,
497 nsStreamTransportService::Dispatch(nsIRunnable
*task
, PRUint32 flags
)
499 NS_ENSURE_TRUE(mPool
, NS_ERROR_NOT_INITIALIZED
);
500 return mPool
->Dispatch(task
, flags
);
504 nsStreamTransportService::IsOnCurrentThread(PRBool
*result
)
506 NS_ENSURE_TRUE(mPool
, NS_ERROR_NOT_INITIALIZED
);
507 return mPool
->IsOnCurrentThread(result
);
511 nsStreamTransportService::CreateInputTransport(nsIInputStream
*stream
,
514 PRBool closeWhenDone
,
515 nsITransport
**result
)
517 nsInputStreamTransport
*trans
=
518 new nsInputStreamTransport(stream
, offset
, limit
, closeWhenDone
);
520 return NS_ERROR_OUT_OF_MEMORY
;
521 NS_ADDREF(*result
= trans
);
526 nsStreamTransportService::CreateOutputTransport(nsIOutputStream
*stream
,
529 PRBool closeWhenDone
,
530 nsITransport
**result
)
532 nsOutputStreamTransport
*trans
=
533 new nsOutputStreamTransport(stream
, offset
, limit
, closeWhenDone
);
535 return NS_ERROR_OUT_OF_MEMORY
;
536 NS_ADDREF(*result
= trans
);
541 nsStreamTransportService::Observe(nsISupports
*subject
, const char *topic
,
542 const PRUnichar
*data
)
544 NS_ASSERTION(strcmp(topic
, "xpcom-shutdown-threads") == 0, "oops");