Back to index

lightning-sunbird  0.9+nobinonly
mozStorageAsyncIO.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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 Storage
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Google Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2006
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Brett Wilson <brettw@gmail.com>
00024  *   Ben Turner <mozilla@songbirdnest.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00233 #include "mozStorageService.h"
00234 #include "nsAutoLock.h"
00235 #include "nsEventQueueUtils.h"
00236 #include "nsIConsoleService.h"
00237 #include "nsIPrompt.h"
00238 #include "nsIRunnable.h"
00239 #include "nsIStringBundle.h"
00240 #include "nsIThread.h"
00241 #include "nsMemory.h"
00242 #include "nsNetCID.h"
00243 #include "nsProxyRelease.h"
00244 #include "nsString.h"
00245 #include "plstr.h"
00246 #include "prlock.h"
00247 #include "prcvar.h"
00248 #include "prtypes.h"
00249 
00250 #include "sqlite3.h"
00251 #include "sqlite3file.h"
00252 
00253 // See below for some discussion on this. This will let us use a reader
00254 // filehandle and a writer filehandle so that read operations are not blocked
00255 // by asychronous writes.
00256 #define SQLITE_ASYNC_TWO_FILEHANDLES 1
00257 
00258 //#define SINGLE_THREADED
00259 
00260 // define this to wait this many ms after every IO operation. Good for
00261 // emulating slow disks and for verification: Set this to some value like
00262 // 200-1000ms and do a bunch of stuff. This will make writes fall behind reads
00263 // and will test that everything stays in sync. Also try doing stuff and then
00264 // exiting to be sure that everything gets flushed on exit.
00265 //
00266 // Undefine for not waiting.
00267 //#define IO_DELAY_INTERVAL_MS 30
00268 
00269 // AsyncOsFile
00270 //
00271 //    This is a wrapper around the sqlite interal OsFile.
00272 //
00273 //    ======
00274 //    DANGER
00275 //    ======
00276 //
00277 //    This function is allocated on my Alloc(), and NOT by new. This means that
00278 //    any C++ objects in here will not get their constructor called.
00279 
00280 struct AsyncOsFile : public OsFile
00281 {
00282   // This is the filename of the file when it was opened.
00283   nsCString* mFilename;
00284 
00285   // This keeps track of the current file offset. Seek operations change this
00286   // offset instead of actually changing the file because we will do stuff to
00287   // the file in the background. We store this offset for each operation such
00288   // as reading and writing so that when it occurs later we know where it
00289   // actually was.
00290   sqlite_int64 mOffset;
00291 
00292   // Set to true normally, false when the file is closed. This way we know not
00293   // to accept any more operations for a closed file (even if the close is
00294   // pending).
00295   PRBool mOpen;
00296 
00297   OsFile* mBaseRead;
00298   OsFile* mBaseWrite;
00299 };
00300 
00301 // AsyncMessage
00302 //
00303 //    Entries on the write-op queue are instances of the AsyncMessage
00304 //    structure, defined here.
00305 //
00306 //    The interpretation of the iOffset and mBytes variables varies depending 
00307 //    on the value of AsyncMessage.mOp:
00308 //
00309 //    ASYNC_WRITE:
00310 //        mOffset -> Offset in file to write to.
00311 //        mBytes  -> Number of bytes of data to write (pointed to by zBuf).
00312 //
00313 //    ASYNC_SYNC:
00314 //        mOffset -> Unused.
00315 //        mBytes  -> Value of "fullsync" flag to pass to sqlite3OsSync().
00316 //
00317 //    ASYNC_TRUNCATE:
00318 //        mOffset -> Size to truncate file to.
00319 //        mBytes  -> Unused.
00320 //
00321 //    ASYNC_CLOSE:
00322 //        mOffset -> Unused.
00323 //        mBytes  -> Unused.
00324 //
00325 //    ASYNC_OPENDIRECTORY:
00326 //        mOffset -> Unused.
00327 //        mBytes  -> Number of bytes of zBuf points to (directory name).
00328 //
00329 //    ASYNC_SETFULLSYNC:
00330 //        mOffset -> Unused.
00331 //        mBytes  -> New value for the full-sync flag.
00332 //
00333 //    ASYNC_DELETE:
00334 //        mOffset -> Unused.
00335 //        mBytes  -> Number of bytes of zBuf points to (file name).
00336 //
00337 //    ASYNC_OPENEXCLUSIVE:
00338 //        mOffset -> Value of "delflag".
00339 //        mBytes  -> Number of bytes of zBuf points to (file name).
00340 //
00341 //    For an ASYNC_WRITE operation, zBuf points to the data to write to the file. 
00342 //    This space is sqliteMalloc()d along with the AsyncMessage structure in a
00343 //    single blob, so is deleted when sqliteFree() is called on the parent 
00344 //    structure.
00345 
00346 struct AsyncMessage
00347 {
00348   // File to write data or to sync
00349   AsyncOsFile* mFile;
00350 
00351   // One of ASYNC_xxx etc.
00352   PRUint32 mOp;            // (was op)
00353   sqlite_int64 mOffset;    // See above (was iOffset)
00354   PRInt32 mBytes;          // See above (was nByte)
00355 
00356   // Data to write to file (or NULL if op != ASYNC_WRITE)
00357   // YOU DO NOT NEED TO FREE THIS SEPARATELY. AppendNewAsyncMessage allocates
00358   // a single buffer with mBuf pointing to the memory immediately following
00359   // this structure. Freeing this structure will also free mBuf.
00360   char *mBuf;
00361 
00362   // Next write operation (to any file) in the linked list
00363   AsyncMessage* mNext;
00364 };
00365 
00366 struct AsyncMessageBarrierData
00367 {
00368   PRLock *mLock;
00369   PRCondVar *mCondVar;
00370 };
00371 
00372 // Possible values of AsyncMessage.mOp
00373 #define ASYNC_WRITE         1
00374 #define ASYNC_SYNC          2
00375 #define ASYNC_TRUNCATE      3
00376 #define ASYNC_CLOSE         4
00377 #define ASYNC_OPENDIRECTORY 5
00378 #define ASYNC_SETFULLSYNC   6
00379 #define ASYNC_DELETE        7
00380 #define ASYNC_OPENEXCLUSIVE 8
00381 #define ASYNC_SYNCDIRECTORY 9
00382 #define ASYNC_BARRIER       10
00383 
00384 // replacements for the sqlite OS routines
00385 static int AsyncOpenReadWrite(const char *aName, OsFile **aFile, int *aReadOnly);
00386 static int AsyncOpenExclusive(const char *aName, OsFile **aFile, int aDelFlag);
00387 static int AsyncOpenReadOnly(const char *aName, OsFile **aFile);
00388 static int AsyncDelete(const char* aName);
00389 static int AsyncSyncDirectory(const char* aName);
00390 static int AsyncFileExists(const char *aName);
00391 static int AsyncClose(OsFile** aFile);
00392 static int AsyncWrite(OsFile* aFile, const void* aBuf, int aCount);
00393 static int AsyncTruncate(OsFile* aFile, sqlite_int64 aNumBytes);
00394 static int AsyncOpenDirectory(OsFile* aFile, const char* aName);
00395 static int AsyncSync(OsFile* aFile, int aFullsync);
00396 static void AsyncSetFullSync(OsFile* aFile, int aValue);
00397 static int AsyncRead(OsFile* aFile, void *aBuffer, int aCount);
00398 static int AsyncSeek(OsFile* aFile, sqlite_int64 aOffset);
00399 static int AsyncFileSize(OsFile* aFile, sqlite_int64* aSize);
00400 static int AsyncFileHandle(OsFile* aFile);
00401 static int AsyncLock(OsFile* aFile, int aLockType);
00402 static int AsyncUnlock(OsFile* aFile, int aLockType);
00403 static int AsyncCheckReservedLock(OsFile* aFile);
00404 static int AsyncLockState(OsFile* aFile);
00405 
00406 static int AsyncBarrier(PRLock* aLock, PRCondVar* aCondVar);
00407 
00408 // backend for all the open functions
00409 static int AsyncOpenFile(const char *aName, AsyncOsFile **aFile,
00410                      OsFile *aBaseRead, PRBool aOpenForWriting);
00411 
00412 // message queue
00413 static AsyncMessage* AsyncQueueFirst = nsnull;
00414 static AsyncMessage* AsyncQueueLast = nsnull;
00415 #ifdef SINGLE_THREADED
00416   // this causes the processing function to return as soon as the messages
00417   // have been processed
00418   static PRBool AsyncWriterHaltWhenIdle = PR_TRUE;
00419 #else
00420   static PRBool AsyncWriterHaltWhenIdle = PR_FALSE;
00421 #endif
00422 static void ProcessAsyncMessages();
00423 static int ProcessOneMessage(AsyncMessage* aMessage);
00424 static void AppendAsyncMessage(AsyncMessage* aMessage);
00425 static int AppendNewAsyncMessage(AsyncOsFile* aFile, PRUint32 aOp,
00426                                  sqlite_int64 aOffset, PRInt32 aDataSize,
00427                                  const char *aData);
00428 static int AsyncWriteError = SQLITE_OK; // set on write error
00429 static void DisplayAsyncWriteError();
00430 
00431 // threading
00432 // serializes access to the queue, AsyncWriteThreadInstance = nsnull means
00433 // single-threaded mode
00434 static nsIThread* AsyncWriteThreadInstance = nsnull;
00435 static PRLock* AsyncQueueLock = nsnull;
00436 static PRCondVar* AsyncQueueCondition = nsnull; // set when queue has something in it
00437 
00438 // pointers to the original sqlite file I/O routines
00439 static int (*sqliteOrigOpenReadWrite)(const char*, OsFile**, int*) = nsnull;
00440 static int (*sqliteOrigOpenExclusive)(const char*, OsFile**, int) = nsnull;
00441 static int (*sqliteOrigOpenReadOnly)(const char*, OsFile**) = nsnull;
00442 static int (*sqliteOrigDelete)(const char*) = nsnull;
00443 static int (*sqliteOrigFileExists)(const char*) = nsnull;
00444 static int (*sqliteOrigSyncDirectory)(const char*) = nsnull;
00445 
00446 // pointers to the original file I/O routines associated with an open file
00447 // these are populated the first time we open a file
00448 static int (*sqliteOrigClose)(OsFile**) = nsnull;
00449 static int (*sqliteOrigRead)(OsFile*, void*, int amt) = nsnull;
00450 static int (*sqliteOrigWrite)(OsFile*, const void*, int amt) = nsnull;
00451 static int (*sqliteOrigFileSize)(OsFile*, sqlite_int64 *pSize) = nsnull;
00452 static int (*sqliteOrigSeek)(OsFile*, sqlite_int64 offset) = nsnull;
00453 static int (*sqliteOrigSync)(OsFile*, int) = nsnull;
00454 static int (*sqliteOrigTruncate)(OsFile*, sqlite_int64 size) = nsnull;
00455 static int (*sqliteOrigOpenDirectory)(OsFile*, const char*);
00456 static void (*sqliteOrigSetFullSync)(OsFile*, int setting);
00457 
00458 
00459 #ifndef SINGLE_THREADED
00460 class AsyncWriteThread : public nsIRunnable
00461 {
00462 public:
00463   AsyncWriteThread(mozIStorageService* aStorageService) :
00464     mStorageService(aStorageService) {}
00465 
00466   NS_DECL_ISUPPORTS
00467 
00468   NS_IMETHOD Run()
00469   {
00470     NS_ASSERTION(! AsyncWriterHaltWhenIdle, "You don't want halt on idle when starting up!");
00471     ProcessAsyncMessages();
00472 
00473     // this will delay processing the release of the storage service until we
00474     // get to the main thread.
00475     nsCOMPtr<nsIEventQueue> eventQueue;
00476     nsresult rv = NS_GetMainEventQ(getter_AddRefs(eventQueue));
00477     if (NS_SUCCEEDED(rv)) {
00478       mozIStorageService* service = nsnull;
00479       mStorageService.swap(service);
00480       NS_ProxyRelease(eventQueue, service);
00481     } else {
00482       NS_NOTREACHED("No event queue");
00483     }
00484     return NS_OK;
00485   }
00486 
00487 protected:
00488   // The thread must keep a reference to the storage service to make sure the
00489   // thread is destroyed before the storage service is. When the storage service
00490   // is done, it will release all the locks that this thread is using. This
00491   // makes sure our locks don't get deleted until we're done using them.
00492   nsCOMPtr<mozIStorageService> mStorageService;
00493 };
00494 NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncWriteThread, nsIRunnable)
00495 #endif // ! SINGLE_THREADED
00496 
00497 
00498 // mozStorageService::InitStorageAsyncIO
00499 //
00500 //    This function must be called before any data base connections have been
00501 //    opened.
00502 
00503 nsresult
00504 mozStorageService::InitStorageAsyncIO()
00505 {
00506   sqlite3OsVtbl* vtable = sqlite3_os_switch();
00507 
00508   sqliteOrigOpenReadWrite = vtable->xOpenReadWrite;
00509   sqliteOrigOpenReadOnly = vtable->xOpenReadOnly;
00510   sqliteOrigOpenExclusive = vtable->xOpenExclusive;
00511   sqliteOrigDelete = vtable->xDelete;
00512   sqliteOrigFileExists = vtable->xFileExists;
00513   sqliteOrigSyncDirectory = vtable->xSyncDirectory;
00514 
00515   vtable->xOpenReadWrite = AsyncOpenReadWrite;
00516   vtable->xOpenReadOnly = AsyncOpenReadOnly;
00517   vtable->xOpenExclusive = AsyncOpenExclusive;
00518   vtable->xDelete = AsyncDelete;
00519   vtable->xFileExists = AsyncFileExists;
00520   vtable->xSyncDirectory = AsyncSyncDirectory;
00521 
00522   // AsyncQueueLock
00523   AsyncQueueLock = PR_NewLock();
00524   if (! AsyncQueueLock) {
00525     return NS_ERROR_OUT_OF_MEMORY;
00526   }
00527 
00528   // AsyncQueueCondition
00529   AsyncQueueCondition = PR_NewCondVar(AsyncQueueLock);
00530   if (! AsyncQueueCondition)
00531     return NS_ERROR_OUT_OF_MEMORY;
00532 
00533 #ifndef SINGLE_THREADED
00534   // start the writer thread
00535   nsCOMPtr<nsIRunnable> thread = new AsyncWriteThread(this);
00536   if (! thread)
00537     return NS_ERROR_OUT_OF_MEMORY;
00538 
00539   nsresult rv = NS_NewThread(&AsyncWriteThreadInstance,
00540                              thread,
00541                              0,
00542                              PR_JOINABLE_THREAD);
00543   if (NS_FAILED(rv)) {
00544     AsyncWriteThreadInstance = nsnull;
00545     return rv;
00546   }
00547 #endif
00548 
00549   return NS_OK;
00550 }
00551 
00552 // mozstorageService::FlushAsyncIO
00553 //
00554 //    This function will grab the async lock and process all
00555 //    remaining async operations that are in the queue on the current
00556 //    thread.  Call this when you need to make sure that an operation
00557 //    has taken place, e.g. that a file has been closed.
00558 
00559 nsresult
00560 mozStorageService::FlushAsyncIO()
00561 {
00562   AsyncMessage *message = 0;
00563   int rc;
00564 
00565   // single threaded? nothing to do.
00566   if (!AsyncWriteThreadInstance)
00567     return NS_OK;
00568 
00569   PRLock *flushLock = PR_NewLock();
00570   if (!flushLock)
00571     return NS_ERROR_OUT_OF_MEMORY;
00572 
00573   PRCondVar *flushCond = PR_NewCondVar(flushLock);
00574   if (!flushCond) {
00575     PR_DestroyLock(flushLock);
00576     return NS_ERROR_OUT_OF_MEMORY;
00577   }
00578 
00579   PR_Lock(flushLock);
00580 
00581   rc = AsyncBarrier(flushLock, flushCond);
00582   if (rc == SQLITE_OK) {
00583     // the async thread will notify us once it reaches
00584     // the ASYNC_BARRIER operation; only wait if
00585     // adding the barrier worked, otherwise just unlock
00586     // and return the error
00587     PR_WaitCondVar(flushCond, PR_INTERVAL_NO_TIMEOUT);
00588   }
00589 
00590   PR_Unlock(flushLock);
00591 
00592   PR_DestroyCondVar(flushCond);
00593   PR_DestroyLock(flushLock);
00594 
00595   if (rc == SQLITE_NOMEM)
00596     return NS_ERROR_OUT_OF_MEMORY;
00597   else if (rc != SQLITE_OK)
00598     return NS_ERROR_FAILURE;
00599   return NS_OK;
00600 }
00601 
00602 
00603 // mozStorageService::FinishAsyncIO
00604 //
00605 //    Call this function on shutdown to ensure that all buffered writes have
00606 //    been comitted to disk. This then puts us into sychronous write mode. Any
00607 //    subsequent database operations will be blocking. This way, we don't care
00608 //    about the shutdown order of components. Other components can still
00609 //    continue to use the database as we shut down, it just won't be buffered.
00610 //    (Which is usually fine since we have to wait for these to be flushed
00611 //    before we can exit anyway.)
00612 
00613 nsresult
00614 mozStorageService::FinishAsyncIO()
00615 {
00616   {
00617     nsAutoLock lock(AsyncQueueLock);
00618 
00619     if (!AsyncWriteThreadInstance)
00620       return NS_OK; // single-threaded mode, nothing to do
00621 
00622     // this will tell the writer to exit when the message queue is empty.
00623     AsyncWriterHaltWhenIdle = PR_TRUE;
00624 
00625     // this will wake up the writer thread when we release the lock
00626     PR_NotifyAllCondVar(AsyncQueueCondition);
00627   }
00628 
00629   // now we join with the writer thread
00630   AsyncWriteThreadInstance->Join();
00631 
00632   // release the thread and enter single-threaded mode
00633   NS_RELEASE(AsyncWriteThreadInstance);
00634   AsyncWriteThreadInstance = nsnull;
00635 
00636   return NS_OK;
00637 }
00638 
00639 
00640 // mozStorageService::FreeLocks
00641 //
00642 //    The locks must be associated with the service so that the destruction
00643 //    is cleanly handled ay service shutdown without the tread trying to
00644 //    unlock a destroyed lock.
00645 
00646 void
00647 mozStorageService::FreeLocks()
00648 {
00649   // Destroy the condition variables
00650   if (AsyncQueueCondition) {
00651     PR_DestroyCondVar(AsyncQueueCondition);
00652     AsyncQueueCondition = nsnull;
00653   }
00654 
00655   if (AsyncQueueLock) {
00656     PR_DestroyLock(AsyncQueueLock);
00657     AsyncQueueLock = nsnull;
00658   }
00659 }
00660 
00661 
00662 // AsyncOpenFile
00663 //
00664 //    This routine does most of the work of opening a file and building the
00665 //    OsFile structure. On error, it will close the input file aBaseRead.
00666 //
00667 //    @param aName            The name of the file to be opened
00668 //    @paran aFile            Put the OsFile structure here
00669 //    @param aBaseRead        The real OsFile from the real I/O routine
00670 //    @param aOpenForWriting  Open a second file handle for writing if true
00671 
00672 int
00673 AsyncOpenFile(const char* aName, AsyncOsFile** aFile,
00674                                  OsFile* aBaseRead, PRBool aOpenForWriting)
00675 {
00676   int rc;
00677   OsFile *baseWrite = nsnull;
00678 
00679   if (! sqliteOrigClose) {
00680     sqliteOrigClose = aBaseRead->pMethod->xClose;
00681     sqliteOrigRead = aBaseRead->pMethod->xRead;
00682     sqliteOrigWrite = aBaseRead->pMethod->xWrite;
00683     sqliteOrigFileSize = aBaseRead->pMethod->xFileSize;
00684     sqliteOrigSeek = aBaseRead->pMethod->xSeek;
00685     sqliteOrigSync = aBaseRead->pMethod->xSync;
00686     sqliteOrigTruncate = aBaseRead->pMethod->xTruncate;
00687     sqliteOrigOpenDirectory = aBaseRead->pMethod->xOpenDirectory;
00688     sqliteOrigSetFullSync = aBaseRead->pMethod->xSetFullSync;
00689   }
00690 
00691   static IoMethod iomethod = {
00692     AsyncClose,
00693     AsyncOpenDirectory,
00694     AsyncRead,
00695     AsyncWrite,
00696     AsyncSeek,
00697     AsyncTruncate,
00698     AsyncSync,
00699     AsyncSetFullSync,
00700     AsyncFileHandle,
00701     AsyncFileSize,
00702     AsyncLock,
00703     AsyncUnlock,
00704     AsyncLockState,
00705     AsyncCheckReservedLock
00706   };
00707 
00708   if (aOpenForWriting && SQLITE_ASYNC_TWO_FILEHANDLES) {
00709     int dummy;
00710     rc = sqliteOrigOpenReadWrite(aName, &baseWrite, &dummy);
00711     if (rc != SQLITE_OK)
00712       goto error_out;
00713   }
00714 
00715   *aFile = NS_STATIC_CAST(AsyncOsFile*, nsMemory::Alloc(sizeof(AsyncOsFile)));
00716   if (! *aFile) {
00717     rc = SQLITE_NOMEM;
00718     goto error_out;
00719   }
00720   memset(*aFile, 0, sizeof(AsyncOsFile));
00721 
00722   (*aFile)->mFilename = new nsCString(aName);
00723   (*aFile)->pMethod = &iomethod;
00724   (*aFile)->mOpen = PR_TRUE;
00725   (*aFile)->mBaseRead = aBaseRead;
00726   (*aFile)->mBaseWrite = baseWrite;
00727 
00728   return SQLITE_OK;
00729 
00730 error_out:
00731   NS_ASSERTION(!*aFile, "File not cleared on error");
00732   sqliteOrigClose(&aBaseRead);
00733   sqliteOrigClose(&baseWrite);
00734   return rc;
00735 }
00736 
00737 
00738 // AppendAsyncMessage
00739 //
00740 //    Add an entry to the end of the global write-op list. pWrite should point
00741 //    to an AsyncMessage structure allocated using nsMemory::Alloc().  The
00742 //    writer thread will call nsMemory::Free() to free the structure after the
00743 //    specified operation has been completed.
00744 //
00745 //    Once an AsyncMessage structure has been added to the list, it becomes the
00746 //    property of the writer thread and must not be read or modified by the
00747 //    caller.
00748 
00749 void
00750 AppendAsyncMessage(AsyncMessage* aMessage)
00751 {
00752   // We must hold the queue mutex in order to modify the queue pointers
00753   PR_Lock(AsyncQueueLock);
00754 
00755   // Add the record to the end of the write-op queue
00756   NS_ASSERTION(! aMessage->mNext, "New messages should not have next pointers");
00757   if (AsyncQueueLast) {
00758     NS_ASSERTION(AsyncQueueFirst, "If we have a last item, we need to have a first one");
00759     AsyncQueueLast->mNext = aMessage;
00760   } else {
00761     AsyncQueueFirst = aMessage;
00762   }
00763   AsyncQueueLast = aMessage;
00764 
00765   // The writer thread might have been idle because there was nothing on the
00766   // write-op queue for it to do. So wake it up.
00767   if (AsyncWriteThreadInstance) {
00768     PR_NotifyCondVar(AsyncQueueCondition);
00769     PR_Unlock(AsyncQueueLock);
00770   } else {
00771     // single threaded mode: call the writer to process this message
00772     NS_ASSERTION(AsyncWriterHaltWhenIdle, "In single-threaded mode, the writer thread should always halt when idle");
00773     PR_Unlock(AsyncQueueLock);
00774     ProcessAsyncMessages();
00775   }
00776 }
00777 
00778 
00779 // AppendNewAsyncMessage
00780 //
00781 //    This is a utility function to allocate and populate a new AsyncWrite
00782 //    structure and insert it (via addAsyncWrite() ) into the global list.
00783 //
00784 //    Note that for some messages data size has a different meaning, and
00785 //    the data pointer is NULL, so we always have to check 'aData' for NULL.
00786 
00787 int // static
00788 AppendNewAsyncMessage(AsyncOsFile* aFile, PRUint32 aOp,
00789                                          sqlite_int64 aOffset, PRInt32 aDataSize,
00790                                          const char *aData)
00791 {
00792   // allocate one buffer, we will put the buffer immediately after our struct
00793   AsyncMessage* p = NS_STATIC_CAST(AsyncMessage*,
00794       nsMemory::Alloc(sizeof(AsyncMessage) + (aData ? aDataSize : 0)));
00795   if (! p)
00796     return SQLITE_NOMEM;
00797 
00798   p->mOp = aOp;
00799   p->mOffset = aOffset;
00800   p->mBytes = aDataSize;
00801   p->mFile = aFile;
00802   p->mNext = nsnull;
00803   if (aData) {
00804     // this gets the address of the data immediately following our structure
00805     p->mBuf = (char*)&p[1];
00806     memcpy(p->mBuf, aData, aDataSize);
00807   } else {
00808     p->mBuf = nsnull;
00809   }
00810   AppendAsyncMessage(p);
00811   return SQLITE_OK;
00812 }
00813 
00814 
00815 // AsyncOpenExclusive
00816 //
00817 //    The async-IO backends implementation of the three functions used to open
00818 //    a file (mOpenExclusive, mOpenReadWrite and mOpenReadOnly). Most of the
00819 //    work is done in function AsyncOpenFile() - see above.
00820 //
00821 //    An OpenExclusive is only valid when the file does not exist.
00822 //
00823 //    OpenExclusive creates a new file structure with no reader and no writer.
00824 //    It posts a message onto the thread for exclusive opening. When this
00825 //    message is processed by the thread, it will create a mBaseReader opened
00826 //    exclusively. Writing will still be OK because the thread will try to
00827 //    use the reader structure for writing if no writer exists.
00828 //
00829 //    Until the file is actually opened, you can actually write to it because
00830 //    the writes will be added to the queue. Reads will work because it will
00831 //    skip reading from the file (no reader structure has been created) and
00832 //    read from the write queue. Because OpenExclusive is not valid for
00833 //    previously-existing files, we know anything in the file is in our
00834 //    write queue until it is actually opened.
00835 
00836 int // static
00837 AsyncOpenExclusive(const char* aName, OsFile** aFile,
00838                                       int aDelFlag)
00839 {
00840   if (AsyncWriteError != SQLITE_OK)
00841     return AsyncWriteError;
00842   // Create a new async file with no base reader that is not writable. Nothing
00843   // will be able to be done with this until the message is processed.
00844   AsyncOsFile* osfile;
00845   int rc = AsyncOpenFile(aName, &osfile, nsnull, PR_FALSE);
00846   if (rc != SQLITE_OK)
00847     return rc;
00848 
00849   rc = AppendNewAsyncMessage(osfile, ASYNC_OPENEXCLUSIVE, aDelFlag,
00850                              PL_strlen(aName) + 1, aName);
00851   if (rc != SQLITE_OK) {
00852     nsMemory::Free(osfile);
00853     osfile = nsnull;
00854   }
00855   *aFile = osfile;
00856   return rc;
00857 }
00858 
00859 
00860 // AsyncOpenReadOnly
00861 
00862 int // static
00863 AsyncOpenReadOnly(const char* aName, OsFile** aFile)
00864 {
00865   if (AsyncWriteError != SQLITE_OK)
00866     return AsyncWriteError;
00867   OsFile* base = nsnull;
00868   int rc = sqliteOrigOpenReadOnly(aName, &base);
00869   if (rc == SQLITE_OK) {
00870     AsyncOsFile* asyncfile;
00871     rc = AsyncOpenFile(aName, &asyncfile, base, PR_FALSE);
00872     if (rc == SQLITE_OK)
00873       *aFile = asyncfile;
00874     else
00875       *aFile = nsnull;
00876   }
00877   return rc;
00878 }
00879 
00880 
00881 // AsyncOpenReadWrite
00882 
00883 int // static
00884 AsyncOpenReadWrite(const char *aName, OsFile** aFile,
00885                                       int* aReadOnly)
00886 {
00887   if (AsyncWriteError != SQLITE_OK)
00888     return AsyncWriteError;
00889   OsFile* base = nsnull;
00890   int rc = sqliteOrigOpenReadWrite(aName, &base, aReadOnly);
00891   if (rc == SQLITE_OK) {
00892     AsyncOsFile* asyncfile;
00893     rc = AsyncOpenFile(aName, &asyncfile, base, !(*aReadOnly));
00894     if (rc == SQLITE_OK)
00895       *aFile = asyncfile;
00896     else
00897       *aFile = nsnull;
00898   }
00899   return rc;
00900 }
00901 
00902 
00903 // AsyncDelete
00904 //
00905 //    Implementation of sqlite3OsDelete. Add an entry to the end of the
00906 //    write-op queue to perform the delete.
00907 
00908 int // static
00909 AsyncDelete(const char* aName)
00910 {
00911   if (AsyncWriteError != SQLITE_OK)
00912     return AsyncWriteError;
00913   return AppendNewAsyncMessage(0, ASYNC_DELETE, 0, PL_strlen(aName) + 1, aName);
00914 }
00915 
00916 
00917 // AsyncSyncDirectory
00918 //
00919 //    Implementation of sqlite3OsSyncDirectory. Add an entry to the end of the
00920 //    write-op queue to perform the directory sync.
00921 
00922 int // static
00923 AsyncSyncDirectory(const char* aName)
00924 {
00925   if (AsyncWriteError != SQLITE_OK)
00926     return AsyncWriteError;
00927   return AppendNewAsyncMessage(0, ASYNC_SYNCDIRECTORY, 0, strlen(aName) + 1, aName);
00928 }
00929 
00930 
00931 // AsyncFileExists
00932 //
00933 //    Implementation of sqlite3OsFileExists. Return true if the file exists in
00934 //    the file system.
00935 //
00936 //    This method is more complicated because the file may have been requested
00937 //    to be deleted, then created, etc. This has to calculate the status of
00938 //    the file at the end of the queue.
00939 //
00940 //    This method holds the mutex from start to finish because it has to check
00941 //    the whole queue to see if the file has been created or deleted.
00942 
00943 int // static
00944 AsyncFileExists(const char *aName)
00945 {
00946   if (AsyncWriteError != SQLITE_OK)
00947     return AsyncWriteError;
00948   nsAutoLock lock(AsyncQueueLock);
00949 
00950   // See if the real file system contains the specified file.
00951   int ret = sqliteOrigFileExists(aName);
00952 
00953   for (AsyncMessage* p = AsyncQueueFirst; p != nsnull; p = p->mNext) {
00954     if (p->mOp == ASYNC_DELETE && 0 == strcmp(p->mBuf, aName)) {
00955       ret = 0;
00956     } else if (p->mOp == ASYNC_OPENEXCLUSIVE && 0 == strcmp(p->mBuf, aName)) {
00957       ret = 1;
00958     }
00959   }
00960   return ret;
00961 }
00962 
00963 
00964 // AsyncClose
00965 //
00966 //    Close the file. This just adds an entry to the write-op list, the file is
00967 //    not actually closed. We also note that the file has been closed by NULLing
00968 //    out the file pointers on the file structure. Other functions will check
00969 //    these to verify that the file hasn't been closed before they accept new
00970 //    operations.
00971 
00972 int // static
00973 AsyncClose(OsFile** aFile)
00974 {
00975   if (AsyncWriteError != SQLITE_OK)
00976     return AsyncWriteError;
00977   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, *aFile);
00978   if (! asyncfile->mOpen) {
00979     NS_NOTREACHED("Attempting to write to a file with a close pending!");
00980     return SQLITE_INTERNAL;
00981   }
00982   asyncfile->mOpen = PR_FALSE;
00983   return AppendNewAsyncMessage(asyncfile, ASYNC_CLOSE, 0, 0, 0);
00984 }
00985 
00986 
00987 // AsyncWrite
00988 //
00989 //    Implementation of sqlite3OsWrite() for asynchronous files. Instead of
00990 //    writing to the underlying file, this function adds an entry to the end of
00991 //    the global AsyncWrite list. Either SQLITE_OK or SQLITE_NOMEM may be
00992 //    returned.
00993 
00994 int // static
00995 AsyncWrite(OsFile* aFile, const void* aBuf, int aCount)
00996 {
00997   if (AsyncWriteError != SQLITE_OK)
00998     return AsyncWriteError;
00999   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01000   if (! asyncfile->mOpen) {
01001     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01002     return SQLITE_INTERNAL;
01003   }
01004   int rc = AppendNewAsyncMessage(asyncfile, ASYNC_WRITE, asyncfile->mOffset,
01005                                  aCount, NS_STATIC_CAST(const char*, aBuf));
01006   asyncfile->mOffset += aCount;
01007   return rc;
01008 }
01009 
01010 
01011 // AsyncTruncate
01012 //
01013 //    Truncate the file to nByte bytes in length. This just adds an entry to
01014 //    the write-op list, no IO actually takes place.
01015 
01016 int // static
01017 AsyncTruncate(OsFile* aFile, sqlite_int64 aNumBytes)
01018 {
01019   if (AsyncWriteError != SQLITE_OK)
01020     return AsyncWriteError;
01021   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01022   if (! asyncfile->mOpen) {
01023     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01024     return SQLITE_INTERNAL;
01025   }
01026   return AppendNewAsyncMessage(asyncfile, ASYNC_TRUNCATE, aNumBytes, 0, 0);
01027 }
01028 
01029 
01030 // AsyncOpenDirectory
01031 //
01032 //    Open the directory identified by zName and associate it with the
01033 //    specified file. This just adds an entry to the write-op list, the
01034 //    directory is opened later by sqlite3_async_flush().
01035 
01036 int // static
01037 AsyncOpenDirectory(OsFile* aFile, const char* aName)
01038 {
01039   if (AsyncWriteError != SQLITE_OK)
01040     return AsyncWriteError;
01041   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01042   if (! asyncfile->mOpen) {
01043     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01044     return SQLITE_INTERNAL;
01045   }
01046   return AppendNewAsyncMessage(asyncfile, ASYNC_OPENDIRECTORY, 0,
01047                           strlen(aName) + 1, aName);
01048 }
01049 
01050 
01051 // AsyncSync
01052 //
01053 //    Sync the file. This just adds an entry to the write-op list, the sync()
01054 //    is done later by sqlite3_async_flush().
01055 
01056 int // static
01057 AsyncSync(OsFile* aFile, int aFullsync)
01058 {
01059   if (AsyncWriteError != SQLITE_OK)
01060     return AsyncWriteError;
01061   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01062   if (! asyncfile->mOpen) {
01063     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01064     return SQLITE_INTERNAL;
01065   }
01066   return AppendNewAsyncMessage(asyncfile, ASYNC_SYNC, 0, aFullsync, 0);
01067 }
01068 
01069 
01070 // AsyncSetFullSync
01071 //
01072 //    Set (or clear) the full-sync flag on the underlying file. This operation
01073 //    is queued and performed later by sqlite3_async_flush().
01074 
01075 void // static
01076 AsyncSetFullSync(OsFile* aFile, int aValue)
01077 {
01078   if (AsyncWriteError != SQLITE_OK)
01079     return;
01080   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01081   if (! asyncfile->mOpen) {
01082     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01083     return;
01084   }
01085   AppendNewAsyncMessage(asyncfile, ASYNC_SETFULLSYNC, 0, aValue, 0);
01086 }
01087 
01088 
01089 // AsyncRead
01090 //
01091 //    Read data from the file. First we read from the filesystem, then adjust
01092 //    the contents of the buffer based on ASYNC_WRITE operations in the
01093 //    write-op queue.
01094 //
01095 //    This method holds the mutex from start to finish because it has to
01096 //    go through the whole queue and apply any changes to the file.
01097 
01098 int // static
01099 AsyncRead(OsFile* aFile, void *aBuffer, int aCount)
01100 {
01101   if (AsyncWriteError != SQLITE_OK)
01102     return AsyncWriteError;
01103   int rc = SQLITE_OK;
01104 
01105   // Grab the write queue mutex for the duration of the call. We don't want
01106   // the writer thread going and writing stuff to the file or processing
01107   // any messages while we do this. Open exclusive may also change mBaseRead.
01108   nsAutoLock lock(AsyncQueueLock);
01109 
01110   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01111   if (! asyncfile->mOpen) {
01112     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01113     return SQLITE_INTERNAL;
01114   }
01115 
01116   OsFile* pBase = asyncfile->mBaseRead;
01117   if (pBase) {
01118     // Only do any actual file reading if there is a reader structure. For
01119     // pending OpenExclusives, there will not be any so we don't want to do
01120     // file reading. Reading while an OpenExclusive is pending will read
01121     // entirely from the write queue. Since OpenExclusive can not work for
01122     // prevously existing files, we know anything in the file is in our write
01123     // queue.
01124     sqlite_int64 filesize;
01125     NS_ASSERTION(sqliteOrigFileSize, "Original file size pointer uninitialized!");
01126     rc = sqliteOrigFileSize(pBase, &filesize);
01127     if (rc != SQLITE_OK)
01128       goto asyncread_out;
01129 
01130     // This may seek beyond EOF if there is appended data waiting in the write
01131     // buffer. The OS should be OK with this. We will only try reading if there
01132     // is stuff for us to read there.
01133     NS_ASSERTION(sqliteOrigSeek, "Original seek pointer uninitialized!");
01134     rc = sqliteOrigSeek(pBase, asyncfile->mOffset);
01135     if (rc != SQLITE_OK)
01136       goto asyncread_out;
01137 
01138     // Here, we try to read as much data as we want up to EOF.
01139     int numread = PR_MIN(filesize - asyncfile->mOffset, aCount);
01140     if (numread > 0) {
01141       NS_ASSERTION(pBase, "Original read pointer uninitialized!");
01142       rc = sqliteOrigRead(pBase, aBuffer, numread);
01143     }
01144   }
01145 
01146   if (rc == SQLITE_OK) {
01147     sqlite_int64 blockOffset = asyncfile->mOffset; // Current seek offset
01148 
01149     // Now we need to bring our data up-do-date with any pending writes.
01150     for (AsyncMessage* p = AsyncQueueFirst; p != nsnull; p = p->mNext) {
01151       if (p->mFile == asyncfile && p->mOp == ASYNC_WRITE) {
01152 
01153         // What we're reading:
01154         //
01155         //      [==================================================]
01156         //      ^- aFile.mOffset = blockOffset
01157         //      <--------------------aCount------------------------>
01158         //
01159         // Possibly pending writes:
01160         //
01161         // [==============]
01162         // ^- p.mOffset
01163         //      <---------> copycount
01164         //
01165         //               [================]
01166         //               ^- p.mOffset
01167         //               <----------------> copycount
01168         //
01169         //                                              [=============]
01170         //                                              ^- p.mOffset
01171         //                                              <----------> copycount
01172         PRInt32 beginIn = PR_MAX(0, blockOffset - p->mOffset);
01173         PRInt32 beginOut = PR_MAX(0, p->mOffset - blockOffset);
01174         PRInt32 copycount = PR_MIN(p->mBytes - beginIn, aCount - beginOut);
01175 
01176         if (copycount > 0) {
01177           memcpy(&NS_STATIC_CAST(char*, aBuffer)[beginOut],
01178                  &p->mBuf[beginIn], copycount);
01179         }
01180       }
01181     }
01182 
01183     // successful read, update virtual current seek offset
01184     asyncfile->mOffset += aCount;
01185   }
01186 
01187 asyncread_out:
01188   return rc;
01189 }
01190 
01191 
01192 // AsyncSeek
01193 //
01194 //    Seek to the specified offset. This just adjusts the AsyncFile.iOffset
01195 //    variable - calling seek() on the underlying file is defered until the
01196 //    next read() or write() operation.
01197 
01198 int // static
01199 AsyncSeek(OsFile* aFile, sqlite_int64 aOffset)
01200 {
01201   if (AsyncWriteError != SQLITE_OK)
01202     return AsyncWriteError;
01203   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01204   if (! asyncfile->mOpen) {
01205     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01206     return SQLITE_INTERNAL;
01207   }
01208   asyncfile->mOffset = aOffset;
01209   return SQLITE_OK;
01210 }
01211 
01212 
01213 // AsyncFileSize
01214 //
01215 //    Read the size of the file. First we read the size of the file system
01216 //    entry, then adjust for any ASYNC_WRITE or ASYNC_TRUNCATE operations
01217 //    currently in the write-op list.
01218 //
01219 //    This method holds the mutex from start to finish because it has to
01220 //    grub through the whole queue.
01221 
01222 int // static
01223 AsyncFileSize(OsFile* aFile, sqlite_int64* aSize)
01224 {
01225   nsAutoLock lock(AsyncQueueLock);
01226 
01227   if (AsyncWriteError != SQLITE_OK)
01228     return AsyncWriteError;
01229 
01230   AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01231   if (! asyncfile->mOpen) {
01232     NS_NOTREACHED("Attempting to write to a file with a close pending!");
01233     return SQLITE_INTERNAL;
01234   }
01235   int rc = SQLITE_OK;
01236   sqlite_int64 size = 0;
01237 
01238   // Read the filesystem size from the base file. If pBaseRead is NULL, this
01239   // means the file hasn't been opened yet. In this case all relevant data must
01240   // be in the write-op queue anyway, so we can omit reading from the
01241   // file-system.
01242   OsFile* pBase = asyncfile->mBaseRead;
01243   if (pBase) {
01244     NS_ASSERTION(sqliteOrigFileSize, "Original file size pointer uninitialized!");
01245     rc = sqliteOrigFileSize(pBase, &size);
01246   }
01247 
01248   if (rc == SQLITE_OK) {
01249     for (AsyncMessage* p = AsyncQueueFirst; p != nsnull; p = p->mNext) {
01250       if (p->mFile == asyncfile) {
01251         switch (p->mOp) {
01252           case ASYNC_WRITE:
01253             size = PR_MAX(p->mOffset + p->mBytes, size);
01254             break;
01255           case ASYNC_TRUNCATE:
01256             size = PR_MIN(size, p->mOffset);
01257             break;
01258         }
01259       }
01260     }
01261     *aSize = size;
01262   }
01263   return rc;
01264 }
01265 
01266 
01267 // AsyncFileHandle
01268 //
01269 //    Return the operating system file handle. This is only used for debugging
01270 //    at the moment anyway. Using this filesystem handle outside of the async
01271 //    service will make bad things happen!
01272 
01273 int // static
01274 AsyncFileHandle(OsFile* aFile)
01275 {
01276   if (AsyncWriteError != SQLITE_OK)
01277     return AsyncWriteError;
01278   NS_NOTREACHED("Don't call FileHandle in async mode");
01279   return SQLITE_OK;
01280 
01281   // If you actually wanted the file handle you would do this:
01282   //AsyncOsFile* asyncfile = NS_STATIC_CAST(AsyncOsFile*, aFile);
01283   //return sqlite3OsFileHandle(asyncfile->mBaseRead);
01284 }
01285 
01286 
01287 // AsyncLock
01288 //
01289 //    No file locking occurs with this version of the asynchronous backend.
01290 //    So the locking routines are no-ops.
01291 
01292 int // static
01293 AsyncLock(OsFile* aFile, int aLockType)
01294 {
01295   if (AsyncWriteError != SQLITE_OK)
01296     return AsyncWriteError;
01297   return SQLITE_OK;
01298 }
01299 
01300 
01301 // AsnycUnlock
01302 
01303 int // static
01304 AsyncUnlock(OsFile* aFile, int aLockType)
01305 {
01306   if (AsyncWriteError != SQLITE_OK)
01307     return AsyncWriteError;
01308   return SQLITE_OK;
01309 }
01310 
01311 
01312 // AsyncCheckReservedLock
01313 //
01314 //    This function is called when the pager layer first opens a database file
01315 //    and is checking for a hot-journal.
01316 
01317 int // static
01318 AsyncCheckReservedLock(OsFile* aFile)
01319 {
01320   if (AsyncWriteError != SQLITE_OK)
01321     return AsyncWriteError;
01322   return SQLITE_OK;
01323 }
01324 
01325 
01326 // AsyncLockState
01327 //
01328 //    This is broken in this async wrapper. But sqlite3OsLockState() is only
01329 //    used for testing anyway.
01330 
01331 int // static
01332 AsyncLockState(OsFile* aFile)
01333 {
01334   if (AsyncWriteError != SQLITE_OK)
01335     return AsyncWriteError;
01336   NS_NOTREACHED("Don't call LockState in async mode");
01337   return SQLITE_OK;
01338 }
01339 
01340 // AsyncBarrier
01341 //
01342 //    This is used to notify a waiting thread that this point in the async
01343 //    queue has been reached.  Note that this is not a sqlite redirected IO
01344 //    function
01345 
01346 int // static
01347 AsyncBarrier(PRLock* aLock, PRCondVar* aCondVar)
01348 {
01349   AsyncMessageBarrierData bd;
01350 
01351   bd.mLock = aLock;
01352   bd.mCondVar = aCondVar;
01353 
01354   return AppendNewAsyncMessage(nsnull, ASYNC_BARRIER, 0,
01355                                sizeof(AsyncMessageBarrierData), (const char*) &bd);
01356 }
01357 
01358 // ProcessOneMessage
01359 //
01360 //    When called, this thread is holding the mutex on the write-op queue.  In
01361 //    the general case, we hold on to the mutex for the entire processing of
01362 //    the message.
01363 //
01364 //    However in the potentially slower cases enumerated below, we relinquish
01365 //    the mutex, perform the IO, and then re-request the mutex of the write-op
01366 //    queue. The idea is to increase concurrency with sqlite threads.
01367 //
01368 //     * An ASYNC_CLOSE operation.
01369 //     * An ASYNC_OPENEXCLUSIVE operation. For this one, we relinquish the
01370 //       mutex, call the underlying xOpenExclusive() function, then
01371 //       re-aquire the mutex before seting the AsyncFile.pBaseRead
01372 //       variable.
01373 //     * ASYNC_SYNC and ASYNC_WRITE operations, if
01374 //       SQLITE_ASYNC_TWO_FILEHANDLES was set at compile time and two
01375 //       file-handles are open for the particular file being "synced".
01376 
01377 int // static
01378 ProcessOneMessage(AsyncMessage* aMessage)
01379 {
01380   PRBool regainMutex = PR_FALSE;
01381   OsFile* pBase = nsnull;
01382 
01383   if (aMessage->mFile) {
01384     pBase = aMessage->mFile->mBaseWrite;
01385     if (aMessage->mOp == ASYNC_CLOSE || 
01386         aMessage->mOp == ASYNC_OPENEXCLUSIVE ||
01387         (pBase && (aMessage->mOp == ASYNC_SYNC ||
01388                    aMessage->mOp == ASYNC_WRITE))) {
01389       regainMutex = PR_TRUE;
01390       PR_Unlock(AsyncQueueLock);
01391     }
01392     if (! pBase)
01393       pBase = aMessage->mFile->mBaseRead;
01394   }
01395 
01396   int rc = SQLITE_OK;
01397   switch (aMessage->mOp) {
01398     case ASYNC_WRITE:
01399       NS_ASSERTION(pBase, "Must have base writer for writing");
01400       rc = sqliteOrigSeek(pBase, aMessage->mOffset);
01401       if (rc == SQLITE_OK)
01402         rc = sqliteOrigWrite(pBase, (const void *)(aMessage->mBuf), aMessage->mBytes);
01403       break;
01404 
01405     case ASYNC_SYNC:
01406       NS_ASSERTION(pBase, "Must have base writer for writing");
01407       rc = sqliteOrigSync(pBase, aMessage->mBytes);
01408       break;
01409 
01410     case ASYNC_TRUNCATE:
01411       NS_ASSERTION(pBase, "Must have base writer for writing");
01412       NS_ASSERTION(sqliteOrigTruncate, "No truncate pointer");
01413       rc = sqliteOrigTruncate(pBase, aMessage->mOffset);
01414       break;
01415 
01416     case ASYNC_CLOSE:
01417       // note that the sqlite close function accepts NULL pointers here and
01418       // will return success if given one (I think the order we close these
01419       // two handles matters here)
01420       sqliteOrigClose(&aMessage->mFile->mBaseWrite);
01421       sqliteOrigClose(&aMessage->mFile->mBaseRead);
01422       if (aMessage->mFile->mFilename)
01423         delete aMessage->mFile->mFilename;
01424       nsMemory::Free(aMessage->mFile);
01425       aMessage->mFile = nsnull;
01426       break;
01427 
01428     case ASYNC_OPENDIRECTORY:
01429       NS_ASSERTION(pBase, "Must have base writer for writing");
01430       NS_ASSERTION(sqliteOrigOpenDirectory, "No open directory pointer");
01431       sqliteOrigOpenDirectory(pBase, aMessage->mBuf);
01432       break;
01433 
01434     case ASYNC_SETFULLSYNC:
01435       NS_ASSERTION(pBase, "Must have base writer for writing");
01436       sqliteOrigSetFullSync(pBase, aMessage->mBytes);
01437       break;
01438 
01439     case ASYNC_DELETE:
01440       NS_ASSERTION(sqliteOrigDelete, "No delete pointer");
01441       rc = sqliteOrigDelete(aMessage->mBuf);
01442       break;
01443 
01444     case ASYNC_SYNCDIRECTORY:
01445       NS_ASSERTION(sqliteOrigSyncDirectory, "No sync directory pointer");
01446       rc = sqliteOrigSyncDirectory(aMessage->mBuf);
01447       break;
01448 
01449     case ASYNC_OPENEXCLUSIVE: {
01450       AsyncOsFile *pFile = aMessage->mFile;
01451       int delFlag = ((aMessage->mOffset) ? 1 : 0);
01452       OsFile* pBase = nsnull;
01453       NS_ASSERTION(! pFile->mBaseRead && ! pFile->mBaseWrite,
01454                    "OpenExclusive expects no file pointers");
01455       rc = sqliteOrigOpenExclusive(aMessage->mBuf, &pBase, delFlag);
01456 
01457       // exclusive opens actually go and write to the OsFile structure to set
01458       // the file object. We therefore need to be locked so the main thread
01459       // doesn't try to use it to do synchronous reading.
01460       PR_Lock(AsyncQueueLock);
01461       regainMutex = PR_FALSE;
01462       if (rc == SQLITE_OK)
01463         pFile->mBaseRead = pBase;
01464       break;
01465     }
01466 
01467     case ASYNC_BARRIER: {
01468       AsyncMessageBarrierData *bd = (AsyncMessageBarrierData*) aMessage->mBuf;
01469       PR_Lock(bd->mLock);
01470       PR_NotifyCondVar(bd->mCondVar);
01471       PR_Unlock(bd->mLock);
01472       break;
01473     }
01474 
01475     default:
01476       NS_NOTREACHED("Illegal value for AsyncMessage.mOp");
01477   }
01478 
01479   if (regainMutex) {
01480     PR_Lock(AsyncQueueLock);
01481   }
01482   return rc;
01483 }
01484 
01485 
01486 // ProcessAsyncMessages
01487 //
01488 //    This procedure runs in a separate thread, reading messages off of the
01489 //    write queue and processing them one by one.
01490 //
01491 //    If async.writerHaltNow is true, then this procedure exits after
01492 //    processing a single message.
01493 //
01494 //    If async.writerHaltWhenIdle is true, then this procedure exits when the
01495 //    write queue is empty.
01496 //
01497 //    If both of the above variables are false, this procedure runs
01498 //    indefinately, waiting for operations to be added to the write queue and
01499 //    processing them in the order in which they arrive.
01500 //
01501 //    An artifical delay of async.ioDelay milliseconds is inserted before each
01502 //    write operation in order to simulate the effect of a slow disk.
01503 //
01504 //    Only one instance of this procedure may be running at a time.
01505 
01506 void // static
01507 ProcessAsyncMessages()
01508 {
01509   AsyncMessage *message = 0;
01510   int rc = SQLITE_OK;
01511 
01512   while (PR_TRUE) {
01513     {
01514       // wait for a message to come in
01515       nsAutoLock lock(AsyncQueueLock);
01516       while ((message = AsyncQueueFirst) == 0) {
01517         if (AsyncWriterHaltWhenIdle) {
01518           // We've been asked to stop, so exit the thread
01519           return;
01520         } else {
01521           // This will unlock AsyncQueueLock and wait for the condition to
01522           // be true. This condition is set when somebody adds an item to our
01523           // queue.
01524           NS_ASSERTION(AsyncQueueLock, "We need to be in multi threaded mode if we're going to wait");
01525           PR_WaitCondVar(AsyncQueueCondition, PR_INTERVAL_NO_TIMEOUT);
01526         }
01527       }
01528 
01529       // this function may release the lock in the middle, but should always
01530       // put it back when it's done
01531       rc = ProcessOneMessage(message);
01532 
01533       // check for error
01534       if (rc != SQLITE_OK) {
01535         AsyncWriteError = rc;
01536         NS_NOTREACHED("FILE ERROR");
01537 
01538         // log error to console
01539         nsresult rv;
01540         nsCOMPtr<nsIConsoleService> consoleSvc =
01541             do_GetService("@mozilla.org/consoleservice;1", &rv);
01542         if (NS_FAILED(rv)) {
01543           NS_WARNING("Couldn't get the console service for logging file error");
01544         } else {
01545           nsAutoString logMessage;
01546           logMessage.AssignLiteral("mozStorage: error code ");
01547           logMessage.AppendInt(rc);
01548           logMessage.AppendLiteral(" for database ");
01549           if (message->mFile && message->mFile->mFilename)
01550             logMessage.Append(NS_ConvertUTF8toUTF16(*message->mFile->mFilename));
01551           rv = consoleSvc->LogStringMessage(logMessage.get());
01552           NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't log message on async error");
01553         }
01554 
01555         // tell user to restart
01556         DisplayAsyncWriteError();
01557         return;
01558       }
01559 
01560       // remove the message from the end of the message queue and release it
01561       if (message == AsyncQueueLast)
01562         AsyncQueueLast = nsnull;
01563       AsyncQueueFirst = message->mNext;
01564       nsMemory::Free(message);
01565 
01566       // free any out-of-memory flags in the library
01567       sqlite3ApiExit(nsnull, 0);
01568     }
01569     // Drop the queue mutex before continuing to the next write operation
01570     // in order to give other threads a chance to work with the write queue
01571     // (that should have been done by the autolock in exiting the scope that
01572     // just closed). We want writers to the queue to generally have priority.
01573     #ifdef IO_DELAY_INTERVAL_MS
01574       // this simulates slow disk
01575       PR_Sleep(PR_MillisecondsToInterval(IO_DELAY_INTERVAL_MS));
01576     #else
01577       // yield so the UI thread is more responsive
01578       PR_Sleep(PR_INTERVAL_NO_WAIT);
01579     #endif
01580   }
01581 }
01582 
01583 
01584 // nsAsyncWriteErrorDisplayer
01585 //
01586 //    This gets dispatched to the main thread so that we can do all the UI
01587 //    calls there. The prompt service must be called from the main thread.
01588 
01589 class nsAsyncWriteErrorDisplayer : public PLEvent
01590 {
01591 public:
01592   nsAsyncWriteErrorDisplayer();
01593   nsresult Post();
01594 };
01595 
01596 // HandleWriteErrorDisplayPLEvent
01597 //
01598 //    Actually displays the error message
01599 
01600 PR_STATIC_CALLBACK(void*)
01601 HandleWriteErrorDisplayPLEvent(PLEvent* aEvent)
01602 {
01603   nsresult rv;
01604   nsCOMPtr<nsIPrompt> prompt = do_CreateInstance(
01605       NS_DEFAULTPROMPT_CONTRACTID, &rv);
01606   if (NS_FAILED(rv)) {
01607     NS_NOTREACHED("Can't get prompt service");
01608     return nsnull;
01609   }
01610 
01611   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(
01612       "@mozilla.org/intl/stringbundle;1", &rv);
01613   if (NS_FAILED(rv)) {
01614     NS_NOTREACHED("Can't get string bundle");
01615     return nsnull;
01616   }
01617 
01618   nsCOMPtr<nsIStringBundle> bundle;
01619   rv = bundleService->CreateBundle(
01620       "chrome://global/locale/storage.properties", getter_AddRefs(bundle));
01621   if (NS_FAILED(rv)) {
01622     NS_NOTREACHED("Can't get storage properties");
01623     return nsnull;
01624   }
01625 
01626   nsXPIDLString message;
01627   rv = bundle->GetStringFromName(NS_LITERAL_STRING("storageWriteError").get(),
01628                                  getter_Copies(message));
01629   if (NS_FAILED(rv)) {
01630     NS_NOTREACHED("Can't get error string");
01631     return nsnull;
01632   }
01633 
01634   prompt->Alert(nsnull, message.get());
01635   return nsnull;
01636 }
01637 
01638 PR_STATIC_CALLBACK(void)
01639 DestroyWriteErrorDisplayerPLEvent(PLEvent* aEvent)
01640 {
01641   nsAsyncWriteErrorDisplayer* displayer = NS_REINTERPRET_CAST(
01642       nsAsyncWriteErrorDisplayer*, aEvent);
01643   delete displayer;
01644 }
01645 
01646 nsAsyncWriteErrorDisplayer::nsAsyncWriteErrorDisplayer()
01647 {
01648   PL_InitEvent(this, nsnull, HandleWriteErrorDisplayPLEvent,
01649                DestroyWriteErrorDisplayerPLEvent);
01650 }
01651 
01652 
01653 // nsAsyncWriteErrorDisplayer::Post
01654 //
01655 //    Post this event to the given UI thread event queue.
01656 
01657 nsresult
01658 nsAsyncWriteErrorDisplayer::Post()
01659 {
01660   nsresult rv;
01661 
01662   nsCOMPtr<nsIEventQueueService> eventQueueService =
01663     do_GetService("@mozilla.org/event-queue-service;1", &rv);
01664   NS_ENSURE_SUCCESS(rv, rv);
01665 
01666   nsCOMPtr<nsIEventQueue> eventQueue;
01667   eventQueueService->
01668     GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
01669                          getter_AddRefs(eventQueue));
01670   if (!eventQueue)
01671     return NS_ERROR_FAILURE;
01672 
01673   rv = eventQueue->PostEvent(this);
01674   if (NS_FAILED(rv)) {
01675     PL_DestroyEvent(this);
01676     return rv;
01677   }
01678 
01679   return NS_OK;
01680 }
01681 
01682 // DisplayAsyncWriteError
01683 //
01684 //    Displays a general message box informing the user of the I/O error. The
01685 //    problem is that this is called from the I/O thread, which can't display
01686 //    UI. Therefore, we proxy to the UI thread.
01687 
01688 void
01689 DisplayAsyncWriteError()
01690 {
01691   nsAsyncWriteErrorDisplayer* displayer = new nsAsyncWriteErrorDisplayer();
01692   if (! displayer) {
01693     NS_NOTREACHED("Can't create the event to display a write error");
01694     return;
01695   }
01696   if (NS_FAILED(displayer->Post())) {
01697     NS_NOTREACHED("Can't call main thread");
01698   }
01699 }