LCOV - code coverage report
Current view: directory - xpcom/io - nsInputStreamTee.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 141 113 80.1 %
Date: 2012-06-02 Functions: 29 23 79.3 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       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
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Darin Fisher <darin@netscape.com> (original author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      27                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include <stdlib.h>
      40                 : #include "prlog.h"
      41                 : 
      42                 : #include "mozilla/Mutex.h"
      43                 : #include "nsIInputStreamTee.h"
      44                 : #include "nsIInputStream.h"
      45                 : #include "nsIOutputStream.h"
      46                 : #include "nsCOMPtr.h"
      47                 : #include "nsAutoPtr.h"
      48                 : #include "nsIEventTarget.h"
      49                 : #include "nsThreadUtils.h"
      50                 : 
      51                 : using namespace mozilla;
      52                 : 
      53                 : #ifdef PR_LOGGING
      54            1464 : static PRLogModuleInfo* gInputStreamTeeLog = PR_NewLogModule("nsInputStreamTee");
      55                 : #define LOG(args) PR_LOG(gInputStreamTeeLog, PR_LOG_DEBUG, args)
      56                 : #else
      57                 : #define LOG(args)
      58                 : #endif
      59                 : 
      60                 : class nsInputStreamTee : public nsIInputStreamTee
      61                 : {
      62                 : public:
      63                 :     NS_DECL_ISUPPORTS
      64                 :     NS_DECL_NSIINPUTSTREAM
      65                 :     NS_DECL_NSIINPUTSTREAMTEE
      66                 : 
      67                 :     nsInputStreamTee();
      68                 :     bool SinkIsValid();
      69                 :     void InvalidateSink();
      70                 : 
      71                 : private:
      72            1301 :     ~nsInputStreamTee() {}
      73                 : 
      74                 :     nsresult TeeSegment(const char *buf, PRUint32 count);
      75                 :     
      76                 :     static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *,
      77                 :                                      PRUint32, PRUint32, PRUint32 *);
      78                 : 
      79                 : private:
      80                 :     nsCOMPtr<nsIInputStream>  mSource;
      81                 :     nsCOMPtr<nsIOutputStream> mSink;
      82                 :     nsCOMPtr<nsIEventTarget>  mEventTarget;
      83                 :     nsWriteSegmentFun         mWriter;  // for implementing ReadSegments
      84                 :     void                      *mClosure; // for implementing ReadSegments
      85                 :     nsAutoPtr<Mutex>          mLock; // synchronize access to mSinkIsValid
      86                 :     bool                      mSinkIsValid; // False if TeeWriteEvent fails 
      87                 : };
      88                 : 
      89                 : class nsInputStreamTeeWriteEvent : public nsRunnable {
      90                 : public:
      91                 :     // aTee's lock is held across construction of this object
      92            1382 :     nsInputStreamTeeWriteEvent(const char *aBuf, PRUint32 aCount,
      93                 :                                nsIOutputStream  *aSink, 
      94                 :                                nsInputStreamTee *aTee)
      95            1382 :     {
      96                 :         // copy the buffer - will be free'd by dtor
      97            1382 :         mBuf = (char *)malloc(aCount);
      98            1382 :         if (mBuf) memcpy(mBuf, (char *)aBuf, aCount);
      99            1382 :         mCount = aCount;
     100            1382 :         mSink = aSink;
     101                 :         bool isNonBlocking;
     102            1382 :         mSink->IsNonBlocking(&isNonBlocking);
     103            1382 :         NS_ASSERTION(isNonBlocking == false, "mSink is nonblocking");
     104            1382 :         mTee = aTee;
     105            1382 :     }
     106                 : 
     107            1382 :     NS_IMETHOD Run()
     108                 :     {
     109            1382 :         if (!mBuf) {
     110                 :             NS_WARNING("nsInputStreamTeeWriteEvent::Run() "
     111               0 :                        "memory not allocated\n");
     112               0 :             return NS_OK;
     113                 :         }
     114            1382 :         NS_ABORT_IF_FALSE(mSink, "mSink is null!");
     115                 : 
     116                 :         //  The output stream could have been invalidated between when 
     117                 :         //  this event was dispatched and now, so check before writing.
     118            1382 :         if (!mTee->SinkIsValid()) {
     119               0 :             return NS_OK; 
     120                 :         }
     121                 : 
     122            1382 :         LOG(("nsInputStreamTeeWriteEvent::Run() [%p]"
     123                 :              "will write %u bytes to %p\n",
     124                 :               this, mCount, mSink.get()));
     125                 : 
     126            1382 :         PRUint32 totalBytesWritten = 0;
     127            4088 :         while (mCount) {
     128                 :             nsresult rv;
     129            1382 :             PRUint32 bytesWritten = 0;
     130            1382 :             rv = mSink->Write(mBuf + totalBytesWritten, mCount, &bytesWritten);
     131            1382 :             if (NS_FAILED(rv)) {
     132              58 :                 LOG(("nsInputStreamTeeWriteEvent::Run[%p] error %x in writing",
     133                 :                      this,rv));
     134              58 :                 mTee->InvalidateSink();
     135              58 :                 break;
     136                 :             }
     137            1324 :             totalBytesWritten += bytesWritten;
     138            1324 :             NS_ASSERTION(bytesWritten <= mCount, "wrote too much");
     139            1324 :             mCount -= bytesWritten;
     140                 :         }
     141            1382 :         return NS_OK;
     142                 :     }
     143                 : 
     144                 : protected:
     145            2764 :     virtual ~nsInputStreamTeeWriteEvent()
     146            2764 :     {
     147            1382 :         if (mBuf) free(mBuf);
     148            1382 :         mBuf = nsnull;
     149            5528 :     }
     150                 :     
     151                 : private:
     152                 :     char *mBuf;
     153                 :     PRUint32 mCount;
     154                 :     nsCOMPtr<nsIOutputStream> mSink;
     155                 :     // back pointer to the tee that created this runnable
     156                 :     nsRefPtr<nsInputStreamTee> mTee;
     157                 : };
     158                 : 
     159            1301 : nsInputStreamTee::nsInputStreamTee(): mLock(nsnull)
     160            1301 :                                     , mSinkIsValid(true)
     161                 : {
     162            1301 : }
     163                 : 
     164                 : bool
     165            2772 : nsInputStreamTee::SinkIsValid()
     166                 : {
     167            5544 :     MutexAutoLock lock(*mLock); 
     168            2772 :     return mSinkIsValid; 
     169                 : }
     170                 : 
     171                 : void
     172              58 : nsInputStreamTee::InvalidateSink()
     173                 : {
     174             116 :     MutexAutoLock lock(*mLock);
     175              58 :     mSinkIsValid = false;
     176              58 : }
     177                 : 
     178                 : nsresult
     179            1663 : nsInputStreamTee::TeeSegment(const char *buf, PRUint32 count)
     180                 : {
     181            1663 :     if (!mSink) return NS_OK; // nothing to do
     182            1663 :     if (mLock) { // asynchronous case
     183            1390 :         NS_ASSERTION(mEventTarget, "mEventTarget is null, mLock is not null.");
     184            1390 :         if (!SinkIsValid()) {
     185               8 :             return NS_OK; // nothing to do
     186                 :         }
     187                 :         nsRefPtr<nsIRunnable> event =
     188            4146 :             new nsInputStreamTeeWriteEvent(buf, count, mSink, this);
     189            1382 :         NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
     190            1382 :         LOG(("nsInputStreamTee::TeeSegment [%p] dispatching write %u bytes\n",
     191                 :               this, count));
     192            1382 :         return mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
     193                 :     } else { // synchronous case
     194             273 :         NS_ASSERTION(!mEventTarget, "mEventTarget is not null, mLock is null.");
     195                 :         nsresult rv;
     196             273 :         PRUint32 totalBytesWritten = 0;
     197             819 :         while (count) {
     198             273 :             PRUint32 bytesWritten = 0;
     199             273 :             rv = mSink->Write(buf + totalBytesWritten, count, &bytesWritten);
     200             273 :             if (NS_FAILED(rv)) {
     201                 :                 // ok, this is not a fatal error... just drop our reference to mSink
     202                 :                 // and continue on as if nothing happened.
     203               0 :                 NS_WARNING("Write failed (non-fatal)");
     204                 :                 // catch possible misuse of the input stream tee
     205               0 :                 NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "sink must be a blocking stream");
     206               0 :                 mSink = 0;
     207               0 :                 break;
     208                 :             }
     209             273 :             totalBytesWritten += bytesWritten;
     210             273 :             NS_ASSERTION(bytesWritten <= count, "wrote too much");
     211             273 :             count -= bytesWritten;
     212                 :         }
     213             273 :         return NS_OK;
     214                 :     }
     215                 : }
     216                 : 
     217                 : NS_METHOD
     218             602 : nsInputStreamTee::WriteSegmentFun(nsIInputStream *in, void *closure, const char *fromSegment,
     219                 :                                   PRUint32 offset, PRUint32 count, PRUint32 *writeCount)
     220                 : {
     221             602 :     nsInputStreamTee *tee = reinterpret_cast<nsInputStreamTee *>(closure);
     222                 : 
     223             602 :     nsresult rv = tee->mWriter(in, tee->mClosure, fromSegment, offset, count, writeCount);
     224             602 :     if (NS_FAILED(rv) || (*writeCount == 0)) {
     225               6 :         NS_ASSERTION((NS_FAILED(rv) ? (*writeCount == 0) : true),
     226                 :                 "writer returned an error with non-zero writeCount");
     227               6 :         return rv;
     228                 :     }
     229                 : 
     230             596 :     return tee->TeeSegment(fromSegment, *writeCount);
     231                 : }
     232                 : 
     233           38064 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTee,
     234                 :                               nsIInputStreamTee,
     235                 :                               nsIInputStream)
     236                 : NS_IMETHODIMP
     237               0 : nsInputStreamTee::Close()
     238                 : {
     239               0 :     NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
     240               0 :     nsresult rv = mSource->Close();
     241               0 :     mSource = 0;
     242               0 :     mSink = 0;
     243               0 :     return rv;
     244                 : }
     245                 : 
     246                 : NS_IMETHODIMP
     247             360 : nsInputStreamTee::Available(PRUint32 *avail)
     248                 : {
     249             360 :     NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
     250             360 :     return mSource->Available(avail);
     251                 : }
     252                 : 
     253                 : NS_IMETHODIMP
     254            1067 : nsInputStreamTee::Read(char *buf, PRUint32 count, PRUint32 *bytesRead)
     255                 : {
     256            1067 :     NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
     257                 : 
     258            1067 :     nsresult rv = mSource->Read(buf, count, bytesRead);
     259            1067 :     if (NS_FAILED(rv) || (*bytesRead == 0))
     260               0 :         return rv;
     261                 : 
     262            1067 :     return TeeSegment(buf, *bytesRead);
     263                 : }
     264                 : 
     265                 : NS_IMETHODIMP
     266             602 : nsInputStreamTee::ReadSegments(nsWriteSegmentFun writer, 
     267                 :                                void *closure,
     268                 :                                PRUint32 count,
     269                 :                                PRUint32 *bytesRead)
     270                 : {
     271             602 :     NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
     272                 : 
     273             602 :     mWriter = writer;
     274             602 :     mClosure = closure;
     275                 : 
     276             602 :     return mSource->ReadSegments(WriteSegmentFun, this, count, bytesRead);
     277                 : }
     278                 : 
     279                 : NS_IMETHODIMP
     280               0 : nsInputStreamTee::IsNonBlocking(bool *result)
     281                 : {
     282               0 :     NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
     283               0 :     return mSource->IsNonBlocking(result);
     284                 : }
     285                 : 
     286                 : NS_IMETHODIMP
     287            1648 : nsInputStreamTee::SetSource(nsIInputStream *source)
     288                 : {
     289            1648 :     mSource = source;
     290            1648 :     return NS_OK;
     291                 : }
     292                 : 
     293                 : NS_IMETHODIMP
     294               0 : nsInputStreamTee::GetSource(nsIInputStream **source)
     295                 : {
     296               0 :     NS_IF_ADDREF(*source = mSource);
     297               0 :     return NS_OK;
     298                 : }
     299                 : 
     300                 : NS_IMETHODIMP
     301            2602 : nsInputStreamTee::SetSink(nsIOutputStream *sink)
     302                 : {
     303                 : #ifdef DEBUG
     304            2602 :     if (sink) {
     305                 :         bool nonBlocking;
     306            1301 :         nsresult rv = sink->IsNonBlocking(&nonBlocking);
     307            1301 :         if (NS_FAILED(rv) || nonBlocking)
     308               0 :             NS_ERROR("sink should be a blocking stream");
     309                 :     }
     310                 : #endif
     311            2602 :     mSink = sink;
     312            2602 :     return NS_OK;
     313                 : }
     314                 : 
     315                 : NS_IMETHODIMP
     316               0 : nsInputStreamTee::GetSink(nsIOutputStream **sink)
     317                 : {
     318               0 :     NS_IF_ADDREF(*sink = mSink);
     319               0 :     return NS_OK;
     320                 : }
     321                 : 
     322                 : NS_IMETHODIMP
     323            1301 : nsInputStreamTee::SetEventTarget(nsIEventTarget *anEventTarget)
     324                 : {
     325            1301 :     mEventTarget = anEventTarget;
     326            1301 :     if (mEventTarget) {
     327                 :         // Only need synchronization if this is an async tee
     328            1028 :         mLock = new Mutex("nsInputStreamTee.mLock");
     329                 :     }
     330            1301 :     return NS_OK;
     331                 : }
     332                 : 
     333                 : NS_IMETHODIMP
     334               0 : nsInputStreamTee::GetEventTarget(nsIEventTarget **anEventTarget)
     335                 : {
     336               0 :     NS_IF_ADDREF(*anEventTarget = mEventTarget);
     337               0 :     return NS_OK;
     338                 : }
     339                 : 
     340                 : 
     341                 : nsresult
     342            1301 : NS_NewInputStreamTeeAsync(nsIInputStream **result,
     343                 :                           nsIInputStream *source,
     344                 :                           nsIOutputStream *sink,
     345                 :                           nsIEventTarget *anEventTarget)
     346                 : {
     347                 :     nsresult rv;
     348                 :     
     349            2602 :     nsCOMPtr<nsIInputStreamTee> tee = new nsInputStreamTee();
     350            1301 :     if (!tee)
     351               0 :         return NS_ERROR_OUT_OF_MEMORY;
     352                 : 
     353            1301 :     rv = tee->SetSource(source);
     354            1301 :     if (NS_FAILED(rv)) return rv;
     355                 : 
     356            1301 :     rv = tee->SetSink(sink);
     357            1301 :     if (NS_FAILED(rv)) return rv;
     358                 : 
     359            1301 :     rv = tee->SetEventTarget(anEventTarget);
     360            1301 :     if (NS_FAILED(rv)) return rv;
     361                 : 
     362            1301 :     NS_ADDREF(*result = tee);
     363            1301 :     return rv;
     364                 : }
     365                 : 
     366                 : nsresult
     367             273 : NS_NewInputStreamTee(nsIInputStream **result,
     368                 :                      nsIInputStream *source,
     369                 :                      nsIOutputStream *sink)
     370                 : {
     371             273 :     return NS_NewInputStreamTeeAsync(result, source, sink, nsnull);
     372            4392 : }

Generated by: LCOV version 1.7