** rollback before accessing the database file.
*/
if( isOpen(pPager->jfd) ){
+#if 0
if( pagerIsServer(pPager) ){
assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
pPager->journalMode = PAGER_JOURNALMODE_DELETE;
** is deleted by the call to pagerUnlockAndRollback() below. */
if( pPager->eState==PAGER_OPEN ) pPager->eState = PAGER_READER;
}
+#endif
pager_error(pPager, pagerSyncHotJournal(pPager));
}
pagerUnlockAndRollback(pPager);
if( pagerIsServer(pPager) ){
sqlite3ServerDisconnect(pPager->pServer, pPager->fd);
pPager->pServer = 0;
- sqlite3_free(pPager->zJournal);
+ }else{
+ sqlite3OsClose(pPager->jfd);
}
#endif
sqlite3EndBenignMalloc();
enable_simulated_io_errors();
PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
- IOTRACE(("CLOSE %p\n", pPager))
- sqlite3OsClose(pPager->jfd);
+ IOTRACE(("CLOSE %p\n", pPager));
sqlite3OsClose(pPager->fd);
sqlite3PageFree(pTmp);
sqlite3PcacheClose(pPager->pPCache);
#endif
assert( !pPager->aSavepoint && !pPager->pInJournal );
- assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) );
+ assert( !isOpen(pPager->sjfd) );
sqlite3_free(pPager);
return SQLITE_OK;
static int pagerServerConnect(Pager *pPager){
int rc = SQLITE_OK;
if( pPager->tempFile==0 ){
- int iClient = 0;
pPager->noLock = 1;
pPager->journalMode = PAGER_JOURNALMODE_PERSIST;
- rc = sqlite3ServerConnect(pPager, &pPager->pServer, &iClient);
- if( rc==SQLITE_OK ){
- pPager->zJournal = sqlite3_mprintf(
- "%s-journal%d", pPager->zFilename, iClient
- );
- if( pPager->zJournal==0 ){
- rc = SQLITE_NOMEM_BKPT;
- }
- }
+ rc = sqlite3ServerConnect(pPager, &pPager->pServer);
}
return rc;
}
-int sqlite3PagerRollbackJournal(Pager *pPager, int iClient){
- int rc;
- char *zJrnl = sqlite3_mprintf("%s-journal%d", pPager->zFilename, iClient);
+int sqlite3PagerRollbackJournal(Pager *pPager, sqlite3_file *pJfd){
+ int rc; /* Return Code */
+ sqlite3_file *saved_jfd = pPager->jfd;
+ u8 saved_eState = pPager->eState;
+ u8 saved_eLock = pPager->eLock;
+ i64 saved_journalOff = pPager->journalOff;
+ i64 saved_journalHdr = pPager->journalHdr;
- if( zJrnl ){
- int bExists = 0;
- sqlite3_file *jfd = 0;
- sqlite3_vfs * const pVfs = pPager->pVfs;
+ assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
- rc = sqlite3OsAccess(pVfs, zJrnl, SQLITE_ACCESS_EXISTS, &bExists);
- if( rc==SQLITE_OK && bExists ){
- int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
- rc = sqlite3OsOpenMalloc(pVfs, zJrnl, &jfd, flags, &flags);
- }
- assert( rc==SQLITE_OK || jfd==0 );
- if( jfd ){
- sqlite3_file *saved_jfd = pPager->jfd;
- u8 saved_eState = pPager->eState;
- u8 saved_eLock = pPager->eLock;
- i64 saved_journalOff = pPager->journalOff;
- i64 saved_journalHdr = pPager->journalHdr;
- char *saved_zJournal = pPager->zJournal;
-
- pPager->eLock = EXCLUSIVE_LOCK;
- pPager->eState = PAGER_WRITER_DBMOD;
- pPager->jfd = jfd;
- rc = pagerSyncHotJournal(pPager);
- if( rc==SQLITE_OK ) rc = pager_playback(pPager, 1);
-
- pPager->jfd = saved_jfd;
- pPager->eState = saved_eState;
- pPager->eLock = saved_eLock;
- pPager->journalOff = saved_journalOff;
- pPager->journalHdr = saved_journalHdr;
- pPager->zJournal = saved_zJournal;
-
- sqlite3OsCloseFree(jfd);
- if( rc==SQLITE_OK ){
- rc = sqlite3OsDelete(pVfs, zJrnl, 0);
- }
- }
- sqlite3_free(zJrnl);
- }else{
- rc = SQLITE_NOMEM_BKPT;
- }
+ pPager->eLock = EXCLUSIVE_LOCK;
+ pPager->eState = PAGER_WRITER_DBMOD;
+ pPager->jfd = pJfd;
+ rc = pagerSyncHotJournal(pPager);
+ if( rc==SQLITE_OK ) rc = pager_playback(pPager, 1);
+ assert( isOpen(pPager->jfd) );
+ pPager->jfd = saved_jfd;
+ pPager->eState = saved_eState;
+ pPager->eLock = saved_eLock;
+ pPager->journalOff = saved_journalOff;
+ pPager->journalHdr = saved_journalHdr;
return rc;
}
+void sqlite3PagerServerJournal(
+ Pager *pPager,
+ sqlite3_file *jfd,
+ const char *zJournal
+){
+ pPager->zJournal = zJournal;
+ pPager->jfd = jfd;
+}
+
#else
# define pagerServerConnect(pPager) SQLITE_OK
#endif
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
assert( rc==SQLITE_OK );
+ assert( sqlite3PagerRefcount(pPager)==0 );
pager_reset(pPager);
rc = sqlite3ServerBegin(pPager->pServer);
+ if( rc==SQLITE_OK){
+ rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
+ }
}
#endif
if( rc==SQLITE_OK && pagerUseWal(pPager) ){
#include "sqliteInt.h"
/*
-** HMA file layout:
-**
-** 4 bytes - DMS slot. All connections read-lock this slot.
-**
-** 16*4 bytes - locking slots. Connections hold a read-lock on a locking slot
-** when they are connected, a write lock when they have an open
-** transaction.
-**
-** N*4 bytes - Page locking slots. N is HMA_PAGELOCK_SLOTS.
-**
** Page-locking slot format:
**
-** Each page-locking slot provides SHARED/RESERVED/EXCLUSIVE locks on a
-** single page. A RESERVED lock is similar to a RESERVED in SQLite's
-** rollback mode - existing SHARED locks may continue but new SHARED locks
-** may not be established. As in rollback mode, EXCLUSIVE and RESERVED
-** locks are mutually exclusive.
-**
-** Each 32-bit locking slot is divided into two sections - a bitmask for
-** read-locks and a single integer field for the write lock. The bitmask
-** occupies the least-significant 27 bits of the slot. The integer field
-** occupies the remaining 5 bits (so that it can store values from 0-31).
+** Assuming HMA_MAX_TRANSACTIONID is set to 16.
**
-** Each client has a unique integer client id. Currently these range from
-** 0-15 (maximum of 16 concurrent connections). The page-locking slot format
-** allows this to be increased to 0-26 (maximum of 26 connections). To
-** take a SHARED lock, the corresponding bit is set in the locking slot
-** bitmask:
+** The least-significant 16 bits are used for read locks. When a read
+** lock is taken, the client sets the bit associated with its
+** transaction-id.
**
-** slot = slot | (1 << iClient);
+** The next 8 bits are set to the number of transient-read locks
+** currently held on the page.
**
-** To take an EXCLUSIVE or RESERVED lock, the integer part of the locking
-** slot is set to the client-id of the locker plus one (a value of zero
-** indicates that no connection holds a RESERVED or EXCLUSIVE lock):
-**
-** slot = slot | ((iClient+1) << 27)
+** The next 5 bits are set to 0 if no client currently holds a write
+** lock. Or to (transaction-id + 1) if a write lock is held.
*/
#ifdef SQLITE_SERVER_EDITION
-#define HMA_CLIENT_SLOTS 16
+/* Number of page-locking slots */
#define HMA_PAGELOCK_SLOTS (256*1024)
-#define HMA_FILE_SIZE (4 + 4*HMA_CLIENT_SLOTS + 4*HMA_PAGELOCK_SLOTS)
+/* Maximum concurrent read/write transactions */
+#define HMA_MAX_TRANSACTIONID 16
+
+/*
+** The argument to this macro is the value of a locking slot. It returns
+** -1 if no client currently holds the write lock, or the transaction-id
+** of the locker otherwise.
+*/
+#define slotGetWriter(v) (((int)((v) >> HMA_MAX_TRANSACTIONID) & 0x1f) -1)
+
+#define slotReaderMask(v) ((v) & ((1 << HMA_MAX_TRANSACTIONID)-1))
#include "unistd.h"
#include "fcntl.h"
#include "sys/stat.h"
#include "errno.h"
-typedef struct ServerHMA ServerHMA;
+typedef struct ServerDb ServerDb;
+typedef struct ServerJournal ServerJournal;
struct ServerGlobal {
- ServerHMA *pHma; /* Linked list of all ServerHMA objects */
+ ServerDb *pDb; /* Linked list of all ServerHMA objects */
};
static struct ServerGlobal g_server;
+struct ServerJournal {
+ char *zJournal;
+ sqlite3_file *jfd;
+};
+
/*
** There is one instance of the following structure for each distinct
-** HMA file opened by clients within this process.
+** database file opened in server mode by this process.
*/
-struct ServerHMA {
- char *zName; /* hma file path */
- int fd; /* Fd open on hma file */
- int nClient; /* Current number of clients */
- Server *aClient[HMA_CLIENT_SLOTS]; /* Local (this process) clients */
- u32 *aMap; /* MMapped hma file */
- ServerHMA *pNext; /* Next HMA in this process */
-
- dev_t st_dev;
- ino_t st_ino;
+struct ServerDb {
+ sqlite3_mutex *mutex; /* Non-recursive mutex */
+ int nClient; /* Current number of clients */
+ int bInit; /* True once initialized */
+ u32 transmask; /* Bitmask of taken transaction ids */
+ u32 *aSlot; /* Array of page locking slots */
+ i64 aFileId[2]; /* Opaque VFS file-id */
+ ServerDb *pNext; /* Next db in this process */
+
+ sqlite3_vfs *pVfs;
+ ServerJournal aJrnl[HMA_MAX_TRANSACTIONID];
+ u8 *aJrnlFdSpace;
};
+/*
+** Once instance for each client connection open on a server mode database
+** in this process.
+*/
struct Server {
- ServerHMA *pHma; /* Hma file object */
- int iClient; /* Client id */
+ ServerDb *pDb; /* Database object */
Pager *pPager; /* Associated pager object */
- i64 nUsWrite; /* Cumulative us holding WRITER lock */
- i64 iUsWrite; /* Time WRITER lock was taken */
+ int iTransId; /* Current transaction id (or -1) */
int nAlloc; /* Allocated size of aLock[] array */
int nLock; /* Number of entries in aLock[] */
u32 *aLock; /* Mapped lock file */
assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1)) );
}
-static int posixLock(int fd, int iSlot, int eLock, int bBlock){
- int res;
- struct flock l;
- short aType[4] = {0, F_UNLCK, F_RDLCK, F_WRLCK};
- assert( eLock==SERVER_WRITE_LOCK
- || eLock==SERVER_READ_LOCK
- || eLock==SERVER_NO_LOCK
- );
- memset(&l, 0, sizeof(l));
- l.l_type = aType[eLock];
- l.l_whence = SEEK_SET;
- l.l_start = iSlot*sizeof(u32);
- l.l_len = 1;
-
- res = fcntl(fd, (bBlock ? F_SETLKW : F_SETLK), &l);
- if( res && bBlock && errno==EDEADLK ){
- return SQLITE_BUSY_DEADLOCK;
+static int serverFindDatabase(Server *pNew, i64 *aFileId){
+ ServerDb *p;
+ int rc = SQLITE_OK;
+ serverEnterMutex();
+ for(p=g_server.pDb; p; p=p->pNext){
+ if( p->aFileId[0]==aFileId[0] && p->aFileId[1]==aFileId[1] ){
+ break;
+ }
}
- return (res==0 ? SQLITE_OK : SQLITE_BUSY);
-}
+ if( p==0 ){
+ p = (ServerDb*)sqlite3MallocZero(sizeof(ServerDb));
+ if( p ){
+ p->aSlot = (u32*)sqlite3MallocZero(sizeof(u32)*HMA_PAGELOCK_SLOTS);
+ if( p->aSlot==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ p->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+#if SQLITE_THREADSAFE!=0
+ if( p->mutex==0 ) rc = SQLITE_NOMEM_BKPT;
+#endif
+ }
-static int serverMapFile(ServerHMA *p){
- assert( p->aMap==0 );
- p->aMap = mmap(0, HMA_FILE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
- if( p->aMap==0 ){
- return SQLITE_ERROR;
+ if( rc==SQLITE_NOMEM ){
+ sqlite3_free(p->aSlot);
+ sqlite3_free(p);
+ p = 0;
+ }else{
+ p->nClient = 1;
+ p->aFileId[0] = aFileId[0];
+ p->aFileId[1] = aFileId[1];
+ p->pNext = g_server.pDb;
+ g_server.pDb = p;
+ }
+ }else{
+ rc = SQLITE_NOMEM_BKPT;
+ }
+ }else{
+ p->nClient++;
}
- return SQLITE_OK;
+ pNew->pDb = p;
+ serverLeaveMutex();
+ return rc;
}
+/*
+** Free all resources allocated by serverInitDatabase() associated with the
+** object passed as the only argument.
+*/
+static void serverShutdownDatabase(ServerDb *pDb){
+ int i;
-static void serverDecrHmaRefcount(ServerHMA *pHma){
- if( pHma ){
- pHma->nClient--;
- if( pHma->nClient<=0 ){
- ServerHMA **pp;
- if( pHma->aMap ) munmap(pHma->aMap, HMA_FILE_SIZE);
- if( pHma->fd>=0 ) close(pHma->fd);
- for(pp=&g_server.pHma; *pp!=pHma; pp=&(*pp)->pNext);
- *pp = pHma->pNext;
- sqlite3_free(pHma);
+ for(i=0; i<HMA_MAX_TRANSACTIONID; i++){
+ ServerJournal *pJ = &pDb->aJrnl[i];
+ if( pJ->jfd ){
+ sqlite3OsClose(pJ->jfd);
+ sqlite3OsDelete(pDb->pVfs, pJ->zJournal, 0);
}
+ sqlite3_free(pJ->zJournal);
}
-}
+ memset(pDb->aJrnl, 0, sizeof(ServerJournal)*HMA_MAX_TRANSACTIONID);
+ if( pDb->aJrnlFdSpace ){
+ sqlite3_free(pDb->aJrnlFdSpace);
+ pDb->aJrnlFdSpace = 0;
+ }
-static int serverOpenHma(Pager *pPager, const char *zPath, ServerHMA **ppHma){
- struct stat sStat; /* Structure populated by stat() */
- int res; /* result of stat() */
- int rc = SQLITE_OK; /* Return code */
- ServerHMA *pHma = 0;
-
- serverAssertMutexHeld();
+ sqlite3_free(pDb->aSlot);
+ pDb->bInit = 0;
+}
- res = stat(zPath, &sStat);
- if( res!=0 ){
- sqlite3_log(SQLITE_CANTOPEN, "Failed to stat(%s)", zPath);
- rc = SQLITE_ERROR;
+/*
+** This function is called when the very first connection to a database
+** is established. It is responsible for rolling back any hot journal
+** files found in the file-system.
+*/
+static int serverInitDatabase(Server *pNew){
+ int nByte;
+ int rc = SQLITE_OK;
+ ServerDb *pDb = pNew->pDb;
+ sqlite3_vfs *pVfs;
+ const char *zFilename = sqlite3PagerFilename(pNew->pPager, 0);
+
+ assert( zFilename );
+ pVfs = pDb->pVfs = sqlite3PagerVfs(pNew->pPager);
+ nByte = ROUND8(pVfs->szOsFile) * HMA_MAX_TRANSACTIONID;
+ pDb->aJrnlFdSpace = (u8*)sqlite3MallocZero(nByte);
+ if( pDb->aJrnlFdSpace==0 ){
+ rc = SQLITE_NOMEM_BKPT;
}else{
- for(pHma=g_server.pHma; pHma; pHma=pHma->pNext){
- if( sStat.st_dev==pHma->st_dev && sStat.st_ino==pHma->st_ino ) break;
- }
- if( pHma==0 ){
- int nPath = strlen(zPath);
- int nByte = sizeof(ServerHMA) + nPath+1 + 4;
-
- pHma = (ServerHMA*)sqlite3_malloc(nByte);
- if( pHma==0 ){
- rc = SQLITE_NOMEM;
- }else{
- int i;
- memset(pHma, 0, nByte);
- pHma->zName = (char*)&pHma[1];
- pHma->nClient = 1;
- pHma->st_dev = sStat.st_dev;
- pHma->st_ino = sStat.st_ino;
- pHma->pNext = g_server.pHma;
- g_server.pHma = pHma;
-
- memcpy(pHma->zName, zPath, nPath);
- memcpy(&pHma->zName[nPath], "-hma", 5);
-
- pHma->fd = open(pHma->zName, O_RDWR|O_CREAT, 0644);
- if( pHma->fd<0 ){
- sqlite3_log(SQLITE_CANTOPEN, "Failed to open(%s)", pHma->zName);
- rc = SQLITE_ERROR;
- }
+ u8 *a = pDb->aJrnlFdSpace;
+ int i;
+ for(i=0; rc==SQLITE_OK && i<HMA_MAX_TRANSACTIONID; i++){
+ int bExists = 0;
+ ServerJournal *pJ = &pDb->aJrnl[i];
+ pJ->jfd = (sqlite3_file*)&a[ROUND8(pVfs->szOsFile)*i];
+ pJ->zJournal = sqlite3_mprintf("%s-journal%d", zFilename, i);
+ if( pJ->zJournal==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ break;
+ }
+ rc = sqlite3OsAccess(pVfs, pJ->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
+ if( rc==SQLITE_OK && bExists ){
+ int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
+ rc = sqlite3OsOpen(pVfs, pJ->zJournal, pJ->jfd, flags, &flags);
if( rc==SQLITE_OK ){
- /* Write-lock the DMS slot. If successful, initialize the hma file. */
- rc = posixLock(pHma->fd, 0, SERVER_WRITE_LOCK, 0);
- if( rc==SQLITE_OK ){
- res = ftruncate(pHma->fd, HMA_FILE_SIZE);
- if( res!=0 ){
- sqlite3_log(SQLITE_CANTOPEN,
- "Failed to ftruncate(%s)", pHma->zName
- );
- rc = SQLITE_ERROR;
- }
- if( rc==SQLITE_OK ){
- rc = serverMapFile(pHma);
- }
- if( rc==SQLITE_OK ){
- memset(pHma->aMap, 0, HMA_FILE_SIZE);
- }else{
- rc = SQLITE_ERROR;
- }
- for(i=0; rc==SQLITE_OK && i<HMA_CLIENT_SLOTS; i++){
- rc = sqlite3PagerRollbackJournal(pPager, i);
- }
- }else{
- rc = serverMapFile(pHma);
- }
- if( rc==SQLITE_OK ){
- rc = posixLock(pHma->fd, 0, SERVER_READ_LOCK, 1);
- }
- }
-
- if( rc!=SQLITE_OK ){
- serverDecrHmaRefcount(pHma);
- pHma = 0;
+ rc = sqlite3PagerRollbackJournal(pNew->pPager, pJ->jfd);
}
}
- }else{
- pHma->nClient++;
}
}
- *ppHma = pHma;
+ if( rc==SQLITE_OK ){
+ pDb->bInit = 1;
+ }else{
+ serverShutdownDatabase(pNew->pDb);
+ }
return rc;
}
-static u32 *serverPageLockSlot(Server *p, Pgno pgno){
- int iSlot = pgno % HMA_PAGELOCK_SLOTS;
- return &p->pHma->aMap[1 + HMA_CLIENT_SLOTS + iSlot];
-}
-static u32 *serverClientSlot(Server *p, int iClient){
- return &p->pHma->aMap[1 + iClient];
-}
-
/*
-** Close the "connection" and *-hma file. This deletes the object passed
-** as the first argument.
+** Close the connection.
*/
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){
- if( p->pHma ){
- ServerHMA *pHma = p->pHma;
- serverEnterMutex();
- if( p->iClient>=0 ){
- u32 *pSlot = serverClientSlot(p, p->iClient);
- *pSlot = 0;
- assert( pHma->aClient[p->iClient]==p );
- pHma->aClient[p->iClient] = 0;
- posixLock(pHma->fd, p->iClient+1, SERVER_NO_LOCK, 0);
- }
- if( dbfd
- && pHma->nClient==1
- && SQLITE_OK==sqlite3OsLock(dbfd, SQLITE_LOCK_EXCLUSIVE)
- ){
- unlink(pHma->zName);
- }
- serverDecrHmaRefcount(pHma);
- serverLeaveMutex();
+ ServerDb *pDb = p->pDb;
+
+ serverEnterMutex();
+ pDb->nClient--;
+ if( pDb->nClient==0 ){
+ ServerDb **pp;
+ serverShutdownDatabase(pDb);
+ for(pp=&g_server.pDb; *pp!=pDb; pp=&((*pp)->pNext));
+ *pp = pDb->pNext;
+ sqlite3_mutex_free(pDb->mutex);
+ sqlite3_free(pDb);
}
+ serverLeaveMutex();
+
sqlite3_free(p->aLock);
sqlite3_free(p);
}
-static int serverRollbackClient(Server *p, int iBlock){
+/*
+** Connect to the system.
+*/
+int sqlite3ServerConnect(
+ Pager *pPager,
+ Server **ppOut /* OUT: Server handle */
+){
+ Server *pNew = 0;
+ sqlite3_file *dbfd = sqlite3PagerFile(pPager);
+ i64 aFileId[2];
int rc;
- sqlite3_log(SQLITE_NOTICE, "Rolling back failed client %d", iBlock);
-
- /* Roll back any journal file for client iBlock. */
- rc = sqlite3PagerRollbackJournal(p->pPager, iBlock);
-
- /* Clear any locks held by client iBlock from the HMA file. */
+ rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_FILEID, (void*)aFileId);
if( rc==SQLITE_OK ){
- int i;
- for(i=0; i<HMA_PAGELOCK_SLOTS; i++){
- u32 *pSlot = serverPageLockSlot(p, (Pgno)i);
- u32 v = *pSlot;
- while( 1 ){
- u32 n = v & ~(1 << iBlock);
- if( (v>>HMA_CLIENT_SLOTS)==iBlock+1 ){
- n = n & ((1<<HMA_CLIENT_SLOTS)-1);
+ pNew = (Server*)sqlite3MallocZero(sizeof(Server));
+ if( pNew ){
+ pNew->pPager = pPager;
+ pNew->iTransId = -1;
+ rc = serverFindDatabase(pNew, aFileId);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pNew);
+ pNew = 0;
+ }else{
+ sqlite3_mutex_enter(pNew->pDb->mutex);
+ if( pNew->pDb->bInit==0 ){
+ rc = serverInitDatabase(pNew);
}
- if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
- v = *pSlot;
+ sqlite3_mutex_leave(pNew->pDb->mutex);
}
+ }else{
+ rc = SQLITE_NOMEM_BKPT;
}
}
+ *ppOut = pNew;
return rc;
}
-
/*
-** Open the *-hma file and "connect" to the system.
+** Begin a transaction.
*/
-int sqlite3ServerConnect(
- Pager *pPager,
- Server **ppOut,
- int *piClient
-){
- const char *zPath = sqlite3PagerFilename(pPager, 0);
+int sqlite3ServerBegin(Server *p){
int rc = SQLITE_OK;
- Server *p;
-
- p = (Server*)sqlite3_malloc(sizeof(Server));
- if( p==0 ){
- rc = SQLITE_NOMEM;
- }else{
- memset(p, 0, sizeof(Server));
- p->iClient = -1;
- p->pPager = pPager;
- serverEnterMutex();
- rc = serverOpenHma(pPager, zPath, &p->pHma);
+ if( p->iTransId<0 ){
+ int id;
+ ServerDb *pDb = p->pDb;
+ u32 t;
- /* File is now mapped. Find a free client slot. */
- if( rc==SQLITE_OK ){
- int i;
- Server **aClient = p->pHma->aClient;
- int fd = p->pHma->fd;
- for(i=0; i<HMA_CLIENT_SLOTS; i++){
- if( aClient[i]==0 ){
- int res = posixLock(fd, i+1, SERVER_WRITE_LOCK, 0);
- if( res==SQLITE_OK ){
- u32 *pSlot = serverClientSlot(p, i);
- if( *pSlot ){
- rc = serverRollbackClient(p, i);
- }
- posixLock(fd, i+1, (!rc ? SERVER_READ_LOCK : SERVER_NO_LOCK), 0);
- break;
- }
- }
- }
+ sqlite3_mutex_enter(pDb->mutex);
- if( rc==SQLITE_OK ){
- if( i>HMA_CLIENT_SLOTS ){
- rc = SQLITE_BUSY;
- }else{
- u32 *pSlot = serverClientSlot(p, i);
- *piClient = p->iClient = i;
- aClient[i] = p;
- *pSlot = 1;
- }
+ /* Find a transaction id to use */
+ rc = SQLITE_BUSY;
+ t = pDb->transmask;
+ for(id=0; id<HMA_MAX_TRANSACTIONID; id++){
+ if( (t & (1 << id))==0 ){
+ t = t | (1 << id);
+ rc = SQLITE_OK;
+ break;
}
}
+ pDb->transmask = t;
- serverLeaveMutex();
- }
-
- if( rc!=SQLITE_OK ){
- sqlite3ServerDisconnect(p, 0);
- p = 0;
- }
- *ppOut = p;
- return rc;
-}
-
-static int serverOvercomeLock(
- Server *p, /* Server connection */
- int bWrite, /* True for a write-lock */
- int bBlock, /* If true, block for this lock */
- u32 v, /* Value of blocking page locking slot */
- int *pbRetry /* OUT: True if caller should retry lock */
-){
- int rc = SQLITE_OK;
- int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1;
-
- if( iBlock<0 || iBlock==p->iClient ){
- for(iBlock=0; iBlock<HMA_CLIENT_SLOTS; iBlock++){
- if( iBlock!=p->iClient && (v & (1<<iBlock)) ) break;
- }
- }
- assert( iBlock<HMA_CLIENT_SLOTS );
-
- serverEnterMutex();
+ sqlite3_mutex_leave(pDb->mutex);
- if( 0==p->pHma->aClient[iBlock] ){
- rc = posixLock(p->pHma->fd, iBlock+1, SERVER_WRITE_LOCK, 0);
if( rc==SQLITE_OK ){
- rc = serverRollbackClient(p, iBlock);
-
- /* Release the lock on slot iBlock */
- posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
- if( rc==SQLITE_OK ){
- *pbRetry = 1;
- }
- }else if( rc==SQLITE_BUSY ){
- if( bBlock ){
- rc = posixLock(p->pHma->fd, iBlock+1, SERVER_READ_LOCK, 1);
- if( rc==SQLITE_OK ){
- posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
- *pbRetry = 1;
- }
- }
-
- if( rc==SQLITE_BUSY ){
- rc = SQLITE_OK;
- }
+ ServerJournal *pJrnl = &pDb->aJrnl[id];
+ sqlite3PagerServerJournal(p->pPager, pJrnl->jfd, pJrnl->zJournal);
+ p->iTransId = id;
}
}
- serverLeaveMutex();
-
return rc;
}
-/*
-** Begin a transaction.
-*/
-int sqlite3ServerBegin(Server *p){
-#if 1
- int rc = posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 1);
- if( rc ) return rc;
-#endif
- return sqlite3ServerLock(p, 1, 0, 1);
-}
-
-/*
-** End a transaction (and release all locks).
-*/
-int sqlite3ServerEnd(Server *p){
+static void serverReleaseLocks(Server *p){
+ ServerDb *pDb = p->pDb;
int i;
+ assert( sqlite3_mutex_held(pDb->mutex) );
+
for(i=0; i<p->nLock; i++){
- u32 *pSlot = serverPageLockSlot(p, p->aLock[i]);
- while( 1 ){
- u32 v = *pSlot;
- u32 n = v;
- if( (v>>HMA_CLIENT_SLOTS)==p->iClient+1 ){
- n = n & ((1 << HMA_CLIENT_SLOTS)-1);
- }
- n = n & ~(1 << p->iClient);
- if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
- }
- if( p->aLock[i]==0 ){
- struct timeval t2;
- i64 nUs;
- gettimeofday(&t2, 0);
- nUs = (i64)t2.tv_sec * 1000000 + t2.tv_usec - p->iUsWrite;
- p->nUsWrite += nUs;
- if( (p->nUsWrite / 1000000)!=((p->nUsWrite + nUs)/1000000) ){
- sqlite3_log(SQLITE_WARNING,
- "Cumulative WRITER time: %lldms\n", p->nUsWrite/1000
- );
- }
+ u32 *pSlot = &pDb->aSlot[p->aLock[i] % HMA_PAGELOCK_SLOTS];
+ if( slotGetWriter(*pSlot)==p->iTransId ){
+ *pSlot -= ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
}
+ *pSlot &= ~((u32)1 << p->iTransId);
}
+
p->nLock = 0;
-#if 1
- return posixLock(p->pHma->fd, p->iClient+1, SERVER_READ_LOCK, 0);
-#endif
- return SQLITE_OK;
}
/*
-** Release all write-locks.
+** End a transaction (and release all locks).
*/
-int sqlite3ServerReleaseWriteLocks(Server *p){
+int sqlite3ServerEnd(Server *p){
int rc = SQLITE_OK;
+ ServerDb *pDb = p->pDb;
+ sqlite3_mutex_enter(pDb->mutex);
+
+ serverReleaseLocks(p);
+ pDb->transmask &= ~((u32)1 << p->iTransId);
+
+ sqlite3_mutex_leave(pDb->mutex);
+ p->iTransId = -1;
return rc;
}
/*
-** Return the client id of the client that currently holds the EXCLUSIVE
-** or RESERVED lock according to page-locking slot value v. Or -1 if no
-** client holds such a lock.
+** Release all write-locks.
*/
-int serverWriteLocker(u32 v){
- return ((int)(v >> HMA_CLIENT_SLOTS)) - 1;
+int sqlite3ServerReleaseWriteLocks(Server *p){
+ int rc = SQLITE_OK;
+ return rc;
}
/*
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
int rc = SQLITE_OK;
- int bReserved = 0;
- u32 *pSlot = serverPageLockSlot(p, pgno);
+ ServerDb *pDb = p->pDb;
+ int iWriter;
+ int bSkip = 0;
+ u32 *pSlot;
- /* Grow the aLock[] array, if required */
+ assert( p->nLock<=p->nAlloc );
if( p->nLock==p->nAlloc ){
- int nNew = p->nAlloc ? p->nAlloc*2 : 128;
- u32 *aNew;
- aNew = (u32*)sqlite3_realloc(p->aLock, sizeof(u32)*nNew);
- if( aNew==0 ){
- rc = SQLITE_NOMEM_BKPT;
- }else{
- p->aLock = aNew;
- p->nAlloc = nNew;
- }
+ int nNew = p->nLock ? p->nLock*2 : 256;
+ u32 *aNew = sqlite3_realloc(p->aLock, nNew*sizeof(u32));
+ if( aNew==0 ) return SQLITE_NOMEM_BKPT;
+ memset(&aNew[p->nLock], 0, sizeof(u32) * (nNew - p->nLock));
+ p->nAlloc = nNew;
+ p->aLock = aNew;
}
- if( rc==SQLITE_OK ){
- u32 v = *pSlot;
-
- /* Check if the required lock is already held. If so, exit this function
- ** early. Otherwise, add an entry to the aLock[] array to record the fact
- ** that the lock may need to be released. */
- if( bWrite ){
- int iLock = ((int)(v>>HMA_CLIENT_SLOTS)) - 1;
- if( iLock==p->iClient ) goto server_lock_out;
- }else{
- if( v & (1<<p->iClient) ) goto server_lock_out;
- }
- p->aLock[p->nLock++] = pgno;
- while( 1 ){
- u32 n;
- int w;
- u32 mask = (bWrite ? (((1<<HMA_CLIENT_SLOTS)-1) & ~(1<<p->iClient)) : 0);
-
- while( ((w = serverWriteLocker(v))>=0 && w!=p->iClient) || (v & mask) ){
- int bRetry = 0;
-
- if( w<0 && bWrite && bBlock ){
- /* Attempt a RESERVED lock before anything else */
- n = v | ((p->iClient+1) << HMA_CLIENT_SLOTS);
- assert( serverWriteLocker(n)==p->iClient );
- if( __sync_val_compare_and_swap(pSlot, v, n)!=v ){
- v = *pSlot;
- continue;
- }
- v = n;
- bReserved = 1;
- }
-
- rc = serverOvercomeLock(p, bWrite, bBlock, v, &bRetry);
- if( rc!=SQLITE_OK ) goto server_lock_out;
- if( bRetry==0 ){
- /* There is a conflicting lock. Cannot obtain this lock. */
- sqlite3_log(SQLITE_BUSY_DEADLOCK, "Conflict at page %d", (int)pgno);
- rc = SQLITE_BUSY_DEADLOCK;
- goto server_lock_out;
- }
-
- v = *pSlot;
- }
-
- n = v | (1 << p->iClient);
- if( bWrite ){
- n = n | ((p->iClient+1) << HMA_CLIENT_SLOTS);
- }
- if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
- v = *pSlot;
+ assert( p->iTransId>=0 );
+
+ sqlite3_mutex_enter(pDb->mutex);
+ pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
+ iWriter = slotGetWriter(*pSlot);
+ if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){
+ bSkip = 1;
+ }else if( iWriter>=0 ){
+ rc = SQLITE_BUSY_DEADLOCK;
+ }else if( bWrite ){
+ if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){
+ *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
+ }else{
+ rc = SQLITE_BUSY_DEADLOCK;
}
+ }else{
+ *pSlot |= (1 << p->iTransId);
}
-server_lock_out:
- if( rc!=SQLITE_OK && bReserved ){
- u32 n;
- u32 v;
- do{
- v = *pSlot;
- assert( serverWriteLocker(v)==p->iClient );
- n = v & ((1<<HMA_CLIENT_SLOTS)-1);
- }while( __sync_val_compare_and_swap(pSlot, v, n)!=v );
- }
+ assert( slotGetWriter(*pSlot)<0
+ || slotReaderMask(*pSlot)==0
+ || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
+ );
+
+ sqlite3_mutex_leave(pDb->mutex);
- if( pgno==0 ){
- struct timeval t1;
- gettimeofday(&t1, 0);
- p->iUsWrite = ((i64)t1.tv_sec * 1000000) + (i64)t1.tv_usec;
+ if( bSkip==0 ){
+ p->aLock[p->nLock++] = pgno;
}
- assert( rc!=SQLITE_OK || sqlite3ServerHasLock(p, pgno, bWrite) );
return rc;
}
int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite){
- u32 v = *serverPageLockSlot(p, pgno);
- if( bWrite ){
- return (v>>HMA_CLIENT_SLOTS)==(p->iClient+1);
- }
- return (v & (1 << p->iClient))!=0;
+ assert( 0 );
+ return 0;
}
#endif /* ifdef SQLITE_SERVER_EDITION */