/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "TestHarness.h" #include "nsIThread.h" #include "nsIRunnable.h" #include "nsThreadUtils.h" #include "prprf.h" #include "prinrval.h" #include "nsCRT.h" #include "nsIPipe.h" // new implementation #include "mozilla/Monitor.h" using namespace mozilla; /** NS_NewPipe2 reimplemented, because it's not exported by XPCOM */ nsresult TP_NewPipe2(nsIAsyncInputStream** input, nsIAsyncOutputStream** output, PRBool nonBlockingInput, PRBool nonBlockingOutput, PRUint32 segmentSize, PRUint32 segmentCount, nsIMemory* segmentAlloc) { nsCOMPtr pipe = do_CreateInstance("@mozilla.org/pipe;1"); if (!pipe) return NS_ERROR_OUT_OF_MEMORY; nsresult rv = pipe->Init(nonBlockingInput, nonBlockingOutput, segmentSize, segmentCount, segmentAlloc); if (NS_FAILED(rv)) return rv; pipe->GetInputStream(input); pipe->GetOutputStream(output); return NS_OK; } /** NS_NewPipe reimplemented, because it's not exported by XPCOM */ #define TP_DEFAULT_SEGMENT_SIZE 4096 nsresult TP_NewPipe(nsIInputStream **pipeIn, nsIOutputStream **pipeOut, PRUint32 segmentSize = 0, PRUint32 maxSize = 0, PRBool nonBlockingInput = PR_FALSE, PRBool nonBlockingOutput = PR_FALSE, nsIMemory *segmentAlloc = nsnull); nsresult TP_NewPipe(nsIInputStream **pipeIn, nsIOutputStream **pipeOut, PRUint32 segmentSize, PRUint32 maxSize, PRBool nonBlockingInput, PRBool nonBlockingOutput, nsIMemory *segmentAlloc) { if (segmentSize == 0) segmentSize = TP_DEFAULT_SEGMENT_SIZE; // Handle maxSize of PR_UINT32_MAX as a special case PRUint32 segmentCount; if (maxSize == PR_UINT32_MAX) segmentCount = PR_UINT32_MAX; else segmentCount = maxSize / segmentSize; nsIAsyncInputStream *in; nsIAsyncOutputStream *out; nsresult rv = TP_NewPipe2(&in, &out, nonBlockingInput, nonBlockingOutput, segmentSize, segmentCount, segmentAlloc); if (NS_FAILED(rv)) return rv; *pipeIn = in; *pipeOut = out; return NS_OK; } #define KEY 0xa7 #define ITERATIONS 33333 char kTestPattern[] = "My hovercraft is full of eels.\n"; PRBool gTrace = PR_FALSE; static nsresult WriteAll(nsIOutputStream *os, const char *buf, PRUint32 bufLen, PRUint32 *lenWritten) { const char *p = buf; *lenWritten = 0; while (bufLen) { PRUint32 n; nsresult rv = os->Write(p, bufLen, &n); if (NS_FAILED(rv)) return rv; p += n; bufLen -= n; *lenWritten += n; } return NS_OK; } class nsReceiver : public nsIRunnable { public: NS_DECL_ISUPPORTS NS_IMETHOD Run() { nsresult rv; char buf[101]; PRUint32 count; PRIntervalTime start = PR_IntervalNow(); while (PR_TRUE) { rv = mIn->Read(buf, 100, &count); if (NS_FAILED(rv)) { printf("read failed\n"); break; } if (count == 0) { // printf("EOF count = %d\n", mCount); break; } if (gTrace) { buf[count] = '\0'; printf("read: %s\n", buf); } mCount += count; } PRIntervalTime end = PR_IntervalNow(); printf("read %d bytes, time = %dms\n", mCount, PR_IntervalToMilliseconds(end - start)); return rv; } nsReceiver(nsIInputStream* in) : mIn(in), mCount(0) { } PRUint32 GetBytesRead() { return mCount; } protected: nsCOMPtr mIn; PRUint32 mCount; }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsReceiver, nsIRunnable) nsresult TestPipe(nsIInputStream* in, nsIOutputStream* out) { nsCOMPtr receiver = new nsReceiver(in); if (!receiver) return NS_ERROR_OUT_OF_MEMORY; nsresult rv; nsCOMPtr thread; rv = NS_NewThread(getter_AddRefs(thread), receiver); if (NS_FAILED(rv)) return rv; PRUint32 total = 0; PRIntervalTime start = PR_IntervalNow(); for (PRUint32 i = 0; i < ITERATIONS; i++) { PRUint32 writeCount; char *buf = PR_smprintf("%d %s", i, kTestPattern); PRUint32 len = strlen(buf); rv = WriteAll(out, buf, len, &writeCount); if (gTrace) { printf("wrote: "); for (PRUint32 j = 0; j < writeCount; j++) { putc(buf[j], stdout); } printf("\n"); } PR_smprintf_free(buf); if (NS_FAILED(rv)) return rv; total += writeCount; } rv = out->Close(); if (NS_FAILED(rv)) return rv; PRIntervalTime end = PR_IntervalNow(); thread->Shutdown(); printf("wrote %d bytes, time = %dms\n", total, PR_IntervalToMilliseconds(end - start)); NS_ASSERTION(receiver->GetBytesRead() == total, "didn't read everything"); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// class nsShortReader : public nsIRunnable { public: NS_DECL_ISUPPORTS NS_IMETHOD Run() { nsresult rv; char buf[101]; PRUint32 count; PRUint32 total = 0; while (PR_TRUE) { //if (gTrace) // printf("calling Read\n"); rv = mIn->Read(buf, 100, &count); if (NS_FAILED(rv)) { printf("read failed\n"); break; } if (count == 0) { break; } if (gTrace) { // For next |printf()| call and possible others elsewhere. buf[count] = '\0'; printf("read %d bytes: %s\n", count, buf); } Received(count); total += count; } printf("read %d bytes\n", total); return rv; } nsShortReader(nsIInputStream* in) : mIn(in), mReceived(0) { mMon = new Monitor("nsShortReader"); } void Received(PRUint32 count) { MonitorAutoEnter mon(*mMon); mReceived += count; mon.Notify(); } PRUint32 WaitForReceipt(const PRUint32 aWriteCount) { MonitorAutoEnter mon(*mMon); PRUint32 result = mReceived; while (result < aWriteCount) { mon.Wait(); NS_ASSERTION(mReceived > result, "failed to receive"); result = mReceived; } mReceived = 0; return result; } protected: nsCOMPtr mIn; PRUint32 mReceived; Monitor* mMon; }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsShortReader, nsIRunnable) nsresult TestShortWrites(nsIInputStream* in, nsIOutputStream* out) { nsCOMPtr receiver = new nsShortReader(in); if (!receiver) return NS_ERROR_OUT_OF_MEMORY; nsresult rv; nsCOMPtr thread; rv = NS_NewThread(getter_AddRefs(thread), receiver); if (NS_FAILED(rv)) return rv; PRUint32 total = 0; for (PRUint32 i = 0; i < ITERATIONS; i++) { PRUint32 writeCount; char* buf = PR_smprintf("%d %s", i, kTestPattern); PRUint32 len = strlen(buf); len = len * rand() / RAND_MAX; len = NS_MAX(1, len); rv = WriteAll(out, buf, len, &writeCount); if (NS_FAILED(rv)) return rv; NS_ASSERTION(writeCount == len, "didn't write enough"); total += writeCount; if (gTrace) printf("wrote %d bytes: %s\n", writeCount, buf); PR_smprintf_free(buf); //printf("calling Flush\n"); out->Flush(); //printf("calling WaitForReceipt\n"); #ifdef DEBUG const PRUint32 received = #endif receiver->WaitForReceipt(writeCount); NS_ASSERTION(received == writeCount, "received wrong amount"); } rv = out->Close(); if (NS_FAILED(rv)) return rv; thread->Shutdown(); printf("wrote %d bytes\n", total); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// class nsPump : public nsIRunnable { public: NS_DECL_ISUPPORTS NS_IMETHOD Run() { nsresult rv; PRUint32 count; while (PR_TRUE) { rv = mOut->WriteFrom(mIn, ~0U, &count); if (NS_FAILED(rv)) { printf("Write failed\n"); break; } if (count == 0) { printf("EOF count = %d\n", mCount); break; } if (gTrace) { printf("Wrote: %d\n", count); } mCount += count; } mOut->Close(); return rv; } nsPump(nsIInputStream* in, nsIOutputStream* out) : mIn(in), mOut(out), mCount(0) { } protected: nsCOMPtr mIn; nsCOMPtr mOut; PRUint32 mCount; }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsPump, nsIRunnable) nsresult TestChainedPipes() { nsresult rv; printf("TestChainedPipes\n"); nsCOMPtr in1; nsCOMPtr out1; rv = TP_NewPipe(getter_AddRefs(in1), getter_AddRefs(out1), 20, 1999); if (NS_FAILED(rv)) return rv; nsCOMPtr in2; nsCOMPtr out2; rv = TP_NewPipe(getter_AddRefs(in2), getter_AddRefs(out2), 200, 401); if (NS_FAILED(rv)) return rv; nsCOMPtr pump = new nsPump(in1, out2); if (pump == nsnull) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr thread; rv = NS_NewThread(getter_AddRefs(thread), pump); if (NS_FAILED(rv)) return rv; nsCOMPtr receiver = new nsReceiver(in2); if (receiver == nsnull) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr receiverThread; rv = NS_NewThread(getter_AddRefs(receiverThread), receiver); if (NS_FAILED(rv)) return rv; PRUint32 total = 0; for (PRUint32 i = 0; i < ITERATIONS; i++) { PRUint32 writeCount; char* buf = PR_smprintf("%d %s", i, kTestPattern); PRUint32 len = strlen(buf); len = len * rand() / RAND_MAX; len = NS_MAX(1, len); rv = WriteAll(out1, buf, len, &writeCount); if (NS_FAILED(rv)) return rv; NS_ASSERTION(writeCount == len, "didn't write enough"); total += writeCount; if (gTrace) printf("wrote %d bytes: %s\n", writeCount, buf); PR_smprintf_free(buf); } printf("wrote total of %d bytes\n", total); rv = out1->Close(); if (NS_FAILED(rv)) return rv; thread->Shutdown(); receiverThread->Shutdown(); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// void RunTests(PRUint32 segSize, PRUint32 segCount) { nsresult rv; nsCOMPtr in; nsCOMPtr out; PRUint32 bufSize = segSize * segCount; printf("Testing New Pipes: segment size %d buffer size %d\n", segSize, bufSize); printf("Testing long writes...\n"); rv = TP_NewPipe(getter_AddRefs(in), getter_AddRefs(out), segSize, bufSize); NS_ASSERTION(NS_SUCCEEDED(rv), "TP_NewPipe failed"); rv = TestPipe(in, out); NS_ASSERTION(NS_SUCCEEDED(rv), "TestPipe failed"); printf("Testing short writes...\n"); rv = TP_NewPipe(getter_AddRefs(in), getter_AddRefs(out), segSize, bufSize); NS_ASSERTION(NS_SUCCEEDED(rv), "TP_NewPipe failed"); rv = TestShortWrites(in, out); NS_ASSERTION(NS_SUCCEEDED(rv), "TestPipe failed"); } //////////////////////////////////////////////////////////////////////////////// #if 0 extern void TestSegmentedBuffer(); #endif int main(int argc, char* argv[]) { nsresult rv; nsCOMPtr servMgr; rv = NS_InitXPCOM2(getter_AddRefs(servMgr), NULL, NULL); if (NS_FAILED(rv)) return rv; if (argc > 1 && nsCRT::strcmp(argv[1], "-trace") == 0) gTrace = PR_TRUE; rv = TestChainedPipes(); NS_ASSERTION(NS_SUCCEEDED(rv), "TestChainedPipes failed"); RunTests(16, 1); RunTests(4096, 16); servMgr = 0; rv = NS_ShutdownXPCOM( NULL ); NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed"); return 0; } ////////////////////////////////////////////////////////////////////////////////