Back to index

lightning-sunbird  0.9+nobinonly
TestPipes.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsIThread.h"
00040 #include "nsIRunnable.h"
00041 #include "nsIInputStream.h"
00042 #include "nsIOutputStream.h"
00043 #include "nsIServiceManager.h"
00044 #include "prprf.h"
00045 #include "prinrval.h"
00046 #include "plstr.h"
00047 #include "nsCRT.h"
00048 #include "nsCOMPtr.h"
00049 #include <stdio.h>
00050 #include "nsIPipe.h"    // new implementation
00051 #include "nsAutoLock.h"
00052 #include <stdlib.h>     // for rand
00053 
00054 #define KEY             0xa7
00055 #define ITERATIONS      33333
00056 char kTestPattern[] = "My hovercraft is full of eels.\n";
00057 
00058 PRBool gTrace = PR_FALSE;
00059 
00060 static nsresult
00061 WriteAll(nsIOutputStream *os, const char *buf, PRUint32 bufLen, PRUint32 *lenWritten)
00062 {
00063     const char *p = buf;
00064     *lenWritten = 0;
00065     while (bufLen) {
00066         PRUint32 n;
00067         nsresult rv = os->Write(p, bufLen, &n);
00068         if (NS_FAILED(rv)) return rv;
00069         p += n;
00070         bufLen -= n;
00071         *lenWritten += n;
00072     }
00073     return NS_OK;
00074 }
00075 
00076 class nsReceiver : public nsIRunnable {
00077 public:
00078     NS_DECL_ISUPPORTS
00079 
00080     NS_IMETHOD Run() {
00081         nsresult rv;
00082         char buf[101];
00083         PRUint32 count;
00084         PRIntervalTime start = PR_IntervalNow();
00085         while (PR_TRUE) {
00086             rv = mIn->Read(buf, 100, &count);
00087             if (NS_FAILED(rv)) {
00088                 printf("read failed\n");
00089                 break;
00090             }
00091             if (count == 0) {
00092 //                printf("EOF count = %d\n", mCount);
00093                 break;
00094             }
00095 
00096             if (gTrace) {
00097                 printf("read:  ");
00098                 buf[count] = '\0';
00099                 printf(buf);
00100                 printf("\n");
00101             }
00102             mCount += count;
00103         }
00104         PRIntervalTime end = PR_IntervalNow();
00105         printf("read  %d bytes, time = %dms\n", mCount,
00106                PR_IntervalToMilliseconds(end - start));
00107         return rv;
00108     }
00109 
00110     nsReceiver(nsIInputStream* in) : mIn(in), mCount(0) {
00111         NS_ADDREF(in);
00112     }
00113 
00114     PRUint32 GetBytesRead() { return mCount; }
00115 
00116 private:
00117     ~nsReceiver() {
00118         NS_RELEASE(mIn);
00119     }
00120 
00121 protected:
00122     nsIInputStream*     mIn;
00123     PRUint32            mCount;
00124 };
00125 
00126 NS_IMPL_THREADSAFE_ISUPPORTS1(nsReceiver, nsIRunnable)
00127 
00128 nsresult
00129 TestPipe(nsIInputStream* in, nsIOutputStream* out)
00130 {
00131     nsresult rv;
00132     nsIThread* thread;
00133     nsReceiver* receiver = new nsReceiver(in);
00134     if (receiver == nsnull) return NS_ERROR_OUT_OF_MEMORY;
00135     NS_ADDREF(receiver);
00136 
00137     rv = NS_NewThread(&thread, receiver, 0, PR_JOINABLE_THREAD);
00138     if (NS_FAILED(rv)) return rv;
00139 
00140     PRUint32 total = 0;
00141     PRIntervalTime start = PR_IntervalNow();
00142     for (PRUint32 i = 0; i < ITERATIONS; i++) {
00143         PRUint32 writeCount;
00144         char *buf = PR_smprintf("%d %s", i, kTestPattern);
00145         PRUint32 len = strlen(buf);
00146         rv = WriteAll(out, buf, len, &writeCount);
00147         if (gTrace) {
00148             printf("wrote: ");
00149             for (PRUint32 j = 0; j < writeCount; j++) {
00150                 putc(buf[j], stdout);
00151             }
00152             printf("\n");
00153         }
00154         PR_smprintf_free(buf);
00155         if (NS_FAILED(rv)) return rv;
00156         total += writeCount;
00157     }
00158     rv = out->Close();
00159     if (NS_FAILED(rv)) return rv;
00160 
00161     PRIntervalTime end = PR_IntervalNow();
00162 
00163     thread->Join();
00164 
00165     printf("wrote %d bytes, time = %dms\n", total,
00166            PR_IntervalToMilliseconds(end - start));
00167     NS_ASSERTION(receiver->GetBytesRead() == total, "didn't read everything");
00168 
00169     NS_RELEASE(thread);
00170     NS_RELEASE(receiver);
00171 
00172     return NS_OK;
00173 }
00174 
00176 
00177 class nsShortReader : public nsIRunnable {
00178 public:
00179     NS_DECL_ISUPPORTS
00180 
00181     NS_IMETHOD Run() {
00182         nsresult rv;
00183         char buf[101];
00184         PRUint32 count;
00185         PRUint32 total = 0;
00186         while (PR_TRUE) {
00187             //if (gTrace)
00188             //    printf("calling Read\n");
00189             rv = mIn->Read(buf, 100, &count);
00190             if (NS_FAILED(rv)) {
00191                 printf("read failed\n");
00192                 break;
00193             }
00194             if (count == 0) {
00195                 break;
00196             }
00197             buf[count] = '\0';
00198             if (gTrace)
00199                 printf("read %d bytes: %s\n", count, buf);
00200             Received(count);
00201             total += count;
00202         }
00203         printf("read  %d bytes\n", total);
00204         return rv;
00205     }
00206 
00207     nsShortReader(nsIInputStream* in) : mIn(in), mReceived(0) {
00208         NS_ADDREF(in);
00209     }
00210 
00211     void Received(PRUint32 count) {
00212         nsAutoCMonitor mon(this);
00213         mReceived += count;
00214         mon.Notify();
00215     }
00216 
00217     PRUint32 WaitForReceipt() {
00218         nsAutoCMonitor mon(this);
00219         PRUint32 result = mReceived;
00220         while (result == 0) {
00221             mon.Wait();
00222             NS_ASSERTION(mReceived >= 0, "failed to receive");
00223             result = mReceived;
00224         }
00225         mReceived = 0;
00226         return result;
00227     }
00228 
00229 private:
00230     ~nsShortReader() {
00231         NS_RELEASE(mIn);
00232     }
00233 
00234 protected:
00235     nsIInputStream*     mIn;
00236     PRUint32            mReceived;
00237 };
00238 
00239 NS_IMPL_THREADSAFE_ISUPPORTS1(nsShortReader, nsIRunnable)
00240 
00241 nsresult
00242 TestShortWrites(nsIInputStream* in, nsIOutputStream* out)
00243 {
00244     nsresult rv;
00245     nsIThread* thread;
00246     nsShortReader* receiver = new nsShortReader(in);
00247     if (receiver == nsnull) return NS_ERROR_OUT_OF_MEMORY;
00248     NS_ADDREF(receiver);
00249 
00250     rv = NS_NewThread(&thread, receiver, 0, PR_JOINABLE_THREAD);
00251     if (NS_FAILED(rv)) return rv;
00252 
00253     PRUint32 total = 0;
00254     for (PRUint32 i = 0; i < ITERATIONS; i++) {
00255         PRUint32 writeCount;
00256         char* buf = PR_smprintf("%d %s", i, kTestPattern);
00257         PRUint32 len = strlen(buf);
00258         len = len * rand() / RAND_MAX;
00259         len = PR_MAX(1, len);
00260         rv = WriteAll(out, buf, len, &writeCount);
00261         if (NS_FAILED(rv)) return rv;
00262         NS_ASSERTION(writeCount == len, "didn't write enough");
00263         total += writeCount;
00264 
00265         if (gTrace)
00266             printf("wrote %d bytes: %s\n", writeCount, buf);
00267         PR_smprintf_free(buf);
00268         //printf("calling Flush\n");
00269         out->Flush();
00270         //printf("calling WaitForReceipt\n");
00271         PRUint32 received = receiver->WaitForReceipt();
00272         NS_ASSERTION(received == writeCount, "received wrong amount");
00273     }
00274     rv = out->Close();
00275     if (NS_FAILED(rv)) return rv;
00276 
00277     thread->Join();
00278     printf("wrote %d bytes\n", total);
00279 
00280     NS_RELEASE(thread);
00281     NS_RELEASE(receiver);
00282 
00283     return NS_OK;
00284 }
00285 
00287 
00288 #if 0
00289 
00290 class nsPipeObserver : public nsIInputStreamObserver, 
00291                        public nsIOutputStreamObserver
00292 {
00293 public:
00294     NS_DECL_ISUPPORTS
00295 
00296     NS_IMETHOD OnFull(nsIOutputStream *outStr) {
00297         printf("OnFull outStr=%p\n", outStr);
00298         return NS_OK;
00299     }
00300 
00301     NS_IMETHOD OnWrite(nsIOutputStream *outStr, PRUint32 amount) {
00302         printf("OnWrite outStr=%p amount=%d\n", outStr, amount);
00303         return NS_OK;
00304     }
00305 
00306     NS_IMETHOD OnEmpty(nsIInputStream* inStr) {
00307         printf("OnEmpty inStr=%p\n", inStr);
00308         return NS_OK;
00309     }
00310 
00311     NS_IMETHOD OnClose(nsIInputStream* inStr) {
00312         printf("OnClose inStr=%p\n", inStr);
00313         return NS_OK;
00314     }
00315 
00316     nsPipeObserver() { }
00317 
00318 private:
00319     ~nsPipeObserver() {}
00320 };
00321 
00322 NS_IMPL_ISUPPORTS2(nsPipeObserver, nsIInputStreamObserver, nsIOutputStreamObserver)
00323 
00324 nsresult
00325 TestPipeObserver()
00326 {
00327     nsresult rv;
00328     nsPipeObserver* obs = new nsPipeObserver();
00329     if (obs == nsnull) return NS_ERROR_OUT_OF_MEMORY;
00330     NS_ADDREF(obs);
00331 
00332     printf("TestPipeObserver: OnWrite and OnFull should be called once, OnEmpty should be called twice.\n");
00333     nsIInputStream* in;
00334     nsIOutputStream* out;
00335     rv = NS_NewPipe(&in, &out, 18, 36, PR_TRUE, PR_TRUE);
00336     if (NS_FAILED(rv)) return rv;
00337 
00338     nsCOMPtr<nsIObservableInputStream> observableIn(do_QueryInterface(in, &rv));
00339     if (NS_FAILED(rv)) return rv;
00340     nsCOMPtr<nsIObservableInputStream> observableOut(do_QueryInterface(out, &rv));
00341     if (NS_FAILED(rv)) return rv;
00342 
00343     rv = observableIn->SetObserver(obs);
00344     if (NS_FAILED(rv)) return rv;
00345     rv = observableOut->SetObserver(obs);
00346     if (NS_FAILED(rv)) return rv;
00347 
00348     char buf[] = "puirt a beul: a style of Gaelic vocal music intended for dancing.";
00349     PRUint32 cnt;
00350     printf("> should see OnWrite message:\n");
00351     rv = out->Write(buf, 20, &cnt);
00352     if (NS_FAILED(rv)) return rv;
00353     NS_ASSERTION(cnt == 20, "Write failed");
00354 
00355     printf("> should see OnWrite message followed by OnFull message:\n");
00356     rv = out->Write(buf + 20, 20, &cnt);
00357     if (NS_FAILED(rv)) return rv;
00358     NS_ASSERTION(cnt == 16, "Write failed");
00359 
00360     rv = in->Available(&cnt);
00361     if (NS_FAILED(rv)) return rv;
00362     printf("available = %u\n", cnt);
00363     NS_ASSERTION(cnt == 36, "Available failed");
00364 
00365     char buf2[40];
00366     printf("> should see OnEmpty message:\n");
00367     rv = in->Read(buf2, 40, &cnt);
00368     if (NS_FAILED(rv)) return rv;
00369     printf("cnt = %u\n", cnt);
00370     NS_ASSERTION(cnt == 36, "Read failed");
00371     NS_ASSERTION(nsCRT::strncmp(buf, buf2, 36) == 0, "Read wrong stuff");
00372 
00373     rv = in->Available(&cnt);
00374     if (NS_FAILED(rv)) return rv;
00375     printf("available = %u\n", cnt);
00376     NS_ASSERTION(cnt == 0, "Available failed");
00377 
00378     printf("> should see OnEmpty message:\n");
00379     rv = in->Read(buf2, 2, &cnt);
00380     if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) return rv;
00381     NS_ASSERTION(cnt == 0 && rv == NS_BASE_STREAM_WOULD_BLOCK, "Read failed");
00382 
00383     printf("> should see OnWrite message:\n");
00384     rv = out->Write(buf, 20, &cnt);
00385     if (NS_FAILED(rv)) return rv;
00386     NS_ASSERTION(cnt == 20, "Write failed");
00387 
00388     rv = in->Available(&cnt);
00389     if (NS_FAILED(rv)) return rv;
00390     printf("available = %u\n", cnt);
00391     NS_ASSERTION(cnt == 20, "Available failed");
00392 
00393     printf("> should see OnEmpty message:\n");
00394     rv = in->Read(buf2, 20, &cnt);
00395     if (NS_FAILED(rv)) return rv;
00396     NS_ASSERTION(cnt == 20, "Read failed");
00397 
00398     printf("> should see OnClose message:\n");
00399     NS_RELEASE(obs);
00400     NS_RELEASE(in);
00401     NS_RELEASE(out);
00402     return NS_OK;
00403 }
00404 #endif
00405 
00407 
00408 class nsPump : public nsIRunnable
00409 {
00410 public:
00411     NS_DECL_ISUPPORTS
00412 
00413     NS_IMETHOD Run() {
00414         nsresult rv;
00415         PRUint32 count;
00416         while (PR_TRUE) {
00417             nsAutoCMonitor mon(this);
00418             rv = mOut->WriteFrom(mIn, ~0U, &count);
00419             if (NS_FAILED(rv)) {
00420                 printf("Write failed\n");
00421                 break;
00422             }
00423             if (count == 0) {
00424                 printf("EOF count = %d\n", mCount);
00425                 break;
00426             }
00427 
00428             if (gTrace) {
00429                 printf("Wrote: %d\n", count);
00430             }
00431             mCount += count;
00432         }
00433         mOut->Close();
00434         return rv;
00435     }
00436 
00437     nsPump(nsIInputStream* in,
00438            nsIOutputStream* out)
00439         : mIn(in), mOut(out), mCount(0) {
00440     }
00441 
00442 private:
00443     ~nsPump() {}
00444 
00445 protected:
00446     nsCOMPtr<nsIInputStream>      mIn;
00447     nsCOMPtr<nsIOutputStream>     mOut;
00448     PRUint32                            mCount;
00449 };
00450 
00451 NS_IMPL_THREADSAFE_ISUPPORTS1(nsPump,
00452                               nsIRunnable)
00453 
00454 nsresult
00455 TestChainedPipes()
00456 {
00457     nsresult rv;
00458     printf("TestChainedPipes\n");
00459 
00460     nsIInputStream* in1;
00461     nsIOutputStream* out1;
00462     rv = NS_NewPipe(&in1, &out1, 20, 1999);
00463     if (NS_FAILED(rv)) return rv;
00464 
00465     nsIInputStream* in2;
00466     nsIOutputStream* out2;
00467     rv = NS_NewPipe(&in2, &out2, 200, 401);
00468     if (NS_FAILED(rv)) return rv;
00469 
00470     nsIThread* thread;
00471     nsPump* pump = new nsPump(in1, out2);
00472     if (pump == nsnull) return NS_ERROR_OUT_OF_MEMORY;
00473     NS_ADDREF(pump);
00474 
00475     rv = NS_NewThread(&thread, pump, 0, PR_JOINABLE_THREAD);
00476     if (NS_FAILED(rv)) return rv;
00477 
00478     nsIThread* receiverThread;
00479     nsReceiver* receiver = new nsReceiver(in2);
00480     if (receiver == nsnull) return NS_ERROR_OUT_OF_MEMORY;
00481     NS_ADDREF(receiver);
00482 
00483     rv = NS_NewThread(&receiverThread, receiver, 0, PR_JOINABLE_THREAD);
00484     if (NS_FAILED(rv)) return rv;
00485 
00486     PRUint32 total = 0;
00487     for (PRUint32 i = 0; i < ITERATIONS; i++) {
00488         PRUint32 writeCount;
00489         char* buf = PR_smprintf("%d %s", i, kTestPattern);
00490         PRUint32 len = strlen(buf);
00491         len = len * rand() / RAND_MAX;
00492         len = PR_MAX(1, len);
00493         rv = WriteAll(out1, buf, len, &writeCount);
00494         if (NS_FAILED(rv)) return rv;
00495         NS_ASSERTION(writeCount == len, "didn't write enough");
00496         total += writeCount;
00497 
00498         if (gTrace)
00499             printf("wrote %d bytes: %s\n", writeCount, buf);
00500 
00501         PR_smprintf_free(buf);
00502     }
00503     printf("wrote total of %d bytes\n", total);
00504     rv = out1->Close();
00505     if (NS_FAILED(rv)) return rv;
00506 
00507     thread->Join();
00508     receiverThread->Join();
00509 
00510     NS_RELEASE(thread);
00511     NS_RELEASE(pump);
00512     NS_RELEASE(receiverThread);
00513     NS_RELEASE(receiver);
00514     return NS_OK;
00515 }
00516 
00518 
00519 void
00520 RunTests(PRUint32 segSize, PRUint32 segCount)
00521 {
00522     nsresult rv;
00523     nsIInputStream* in;
00524     nsIOutputStream* out;
00525     PRUint32 bufSize;
00526 
00527     bufSize = segSize * segCount;
00528     printf("Testing New Pipes: segment size %d buffer size %d\n", segSize, bufSize);
00529 
00530     printf("Testing long writes...\n");
00531     rv = NS_NewPipe(&in, &out, segSize, bufSize);
00532     NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewPipe failed");
00533     rv = TestPipe(in, out);
00534     NS_RELEASE(in);
00535     NS_RELEASE(out);
00536     NS_ASSERTION(NS_SUCCEEDED(rv), "TestPipe failed");
00537 
00538     printf("Testing short writes...\n");
00539     rv = NS_NewPipe(&in, &out, segSize, bufSize);
00540     NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewPipe failed");
00541     rv = TestShortWrites(in, out);
00542     NS_RELEASE(in);
00543     NS_RELEASE(out);
00544     NS_ASSERTION(NS_SUCCEEDED(rv), "TestPipe failed");
00545 }
00546 
00548 #if 0
00549 void
00550 TestSearch(const char* delim, PRUint32 segSize)
00551 {
00552     nsresult rv;
00553     // need at least 2 segments to test boundary conditions:
00554     PRUint32 bufDataSize = segSize * 2;
00555     PRUint32 bufSize = segSize * 2;
00556     nsIInputStream* in;
00557     nsIOutputStream* out;
00558     rv = NS_NewPipe(&in, &out, segSize, bufSize);
00559     NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewPipe failed");
00560     out->SetNonBlocking(PR_TRUE);
00561 
00562     PRUint32 i, j, amt;
00563     PRUint32 delimLen = nsCRT::strlen(delim);
00564     for (i = 0; i < bufDataSize; i++) {
00565         // first fill the buffer
00566         for (j = 0; j < i; j++) {
00567             rv = out->Write("-", 1, &amt);
00568             NS_ASSERTION(NS_SUCCEEDED(rv) && amt == 1, "Write failed");
00569         }
00570         rv = out->Write(delim, delimLen, &amt);
00571         NS_ASSERTION(NS_SUCCEEDED(rv), "Write failed");
00572         if (i + amt < bufDataSize) {
00573             for (j = i + amt; j < bufDataSize; j++) {
00574                 rv = out->Write("+", 1, &amt);
00575                 NS_ASSERTION(NS_SUCCEEDED(rv) && amt == 1, "Write failed");
00576             }
00577         }
00578 
00579         // now search for the delimiter
00580         PRBool found;
00581         PRUint32 offset;
00582         rv = in->Search(delim, PR_FALSE, &found, &offset);
00583         NS_ASSERTION(NS_SUCCEEDED(rv), "Search failed");
00584 
00585         // print the results
00586         char* bufferContents = new char[bufDataSize + 1];
00587         rv = in->Read(bufferContents, bufDataSize, &amt);
00588         NS_ASSERTION(NS_SUCCEEDED(rv) && amt == bufDataSize, "Read failed");
00589         bufferContents[bufDataSize] = '\0';
00590         printf("Buffer: %s\nDelim: %s %s offset: %d\n", bufferContents,
00591                delim, (found ? "found" : "not found"), offset);
00592     }
00593     NS_RELEASE(in);
00594     NS_RELEASE(out);
00595 }
00596 #endif
00597 
00598 
00599 #ifdef DEBUG
00600 extern NS_COM void
00601 TestSegmentedBuffer();
00602 #endif
00603 
00604 int
00605 main(int argc, char* argv[])
00606 {
00607     nsresult rv;
00608     nsIServiceManager* servMgr;
00609 
00610     rv = NS_InitXPCOM2(&servMgr, NULL, NULL);
00611     if (NS_FAILED(rv)) return rv;
00612 
00613     if (argc > 1 && nsCRT::strcmp(argv[1], "-trace") == 0)
00614         gTrace = PR_TRUE;
00615 
00616 #ifdef DEBUG
00617     TestSegmentedBuffer();
00618 #endif
00619 
00620 #if 0   // obsolete old implementation
00621     rv = NS_NewPipe(&in, &out, 4096 * 4);
00622     if (NS_FAILED(rv)) {
00623         printf("NewPipe failed\n");
00624         return -1;
00625     }
00626 
00627     rv = TestPipe(in, out);
00628     NS_RELEASE(in);
00629     NS_RELEASE(out);
00630     if (NS_FAILED(rv)) {
00631         printf("TestPipe failed\n");
00632         return -1;
00633     }
00634 #endif
00635 #if 0
00636     TestSearch("foo", 8);
00637     TestSearch("bar", 6);
00638     TestSearch("baz", 2);
00639 #endif
00640 
00641     rv = TestChainedPipes();
00642     NS_ASSERTION(NS_SUCCEEDED(rv), "TestChainedPipes failed");
00643     RunTests(16, 1);
00644     RunTests(4096, 16);
00645     NS_RELEASE(servMgr);
00646     rv = NS_ShutdownXPCOM( NULL );
00647     NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
00648     return 0;
00649 }
00650