Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -484,11 +484,11 @@ static void releaseOpenCnt(struct openCnt *pOpen){ assert( sqlite3OsInMutex() ); pOpen->nRef--; if( pOpen->nRef==0 ){ sqlite3HashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0); - sqliteFree(pOpen->aPending); + free(pOpen->aPending); sqliteFree(pOpen); } } /* @@ -579,10 +579,28 @@ exit_findlockinfo: return rc; } +#ifdef SQLITE_DEBUG +/* +** Helper function for printing out trace information from debugging +** binaries. This returns the string represetation of the supplied +** integer lock-type. +*/ +static const char *locktypeName(int locktype){ + switch( locktype ){ + case NO_LOCK: return "NONE"; + case SHARED_LOCK: return "SHARED"; + case RESERVED_LOCK: return "RESERVED"; + case PENDING_LOCK: return "PENDING"; + case EXCLUSIVE_LOCK: return "EXCLUSIVE"; + } + return "ERROR"; +} +#endif + /* ** If we are currently in a different thread than the thread that the ** unixFile argument belongs to, then transfer ownership of the unixFile ** over to the current thread. ** @@ -594,27 +612,34 @@ ** If the unixFile is locked and an ownership is wrong, then return ** SQLITE_MISUSE. Otherwise return SQLITE_OK. */ #ifdef SQLITE_UNIX_THREADS static int transferOwnership(unixFile *pFile){ + int rc; pthread_t hSelf; if( threadsOverrideEachOthersLocks ){ /* Ownership transfers not needed on this system */ return SQLITE_OK; } hSelf = pthread_self(); if( pthread_equal(pFile->tid, hSelf) ){ /* We are still in the same thread */ + TRACE1("No-transfer, same thread\n"); return SQLITE_OK; } if( pFile->locktype!=NO_LOCK ){ /* We cannot change ownership while we are holding a lock! */ return SQLITE_MISUSE; } + TRACE4("Transfer ownership of %d from %d to %d\n", pFile->h,pFile->tid,hSelf); pFile->tid = hSelf; releaseLockInfo(pFile->pLock); - return findLockInfo(pFile->h, &pFile->pLock, 0); + rc = findLockInfo(pFile->h, &pFile->pLock, 0); + TRACE5("LOCK %d is now %s(%s,%d)\n", pFile->h, + locktypeName(pFile->locktype), + locktypeName(pFile->pLock->locktype), pFile->pLock->cnt); + return rc; } #else # define transferOwnership(X) SQLITE_OK #endif @@ -1119,28 +1144,10 @@ TRACE3("TEST WR-LOCK %d %d\n", pFile->h, r); return r; } -#ifdef SQLITE_DEBUG -/* -** Helper function for printing out trace information from debugging -** binaries. This returns the string represetation of the supplied -** integer lock-type. -*/ -static const char *locktypeName(int locktype){ - switch( locktype ){ - case NO_LOCK: return "NONE"; - case SHARED_LOCK: return "SHARED"; - case RESERVED_LOCK: return "RESERVED"; - case PENDING_LOCK: return "PENDING"; - case EXCLUSIVE_LOCK: return "EXCLUSIVE"; - } - return "ERROR"; -} -#endif - /* ** Lock the file with the lock specified by parameter locktype - one ** of the following: ** ** (1) SHARED_LOCK @@ -1238,10 +1245,11 @@ rc = transferOwnership(pFile); if( rc!=SQLITE_OK ){ sqlite3OsLeaveMutex(); return rc; } + pLock = pFile->pLock; /* If some thread using this PID has a lock via a different OsFile* ** handle that precludes the requested lock, return BUSY. */ if( (pFile->locktype!=pLock->locktype && @@ -1437,11 +1445,11 @@ if( pOpen->nLock==0 && pOpen->nPending>0 ){ int i; for(i=0; inPending; i++){ close(pOpen->aPending[i]); } - sqliteFree(pOpen->aPending); + free(pOpen->aPending); pOpen->nPending = 0; pOpen->aPending = 0; } } sqlite3OsLeaveMutex(); @@ -1456,24 +1464,23 @@ unixFile *id = (unixFile*)*pId; int rc; if( !id ) return SQLITE_OK; rc = unixUnlock(*pId, NO_LOCK); - if( rc ) return rc; if( id->dirfd>=0 ) close(id->dirfd); id->dirfd = -1; sqlite3OsEnterMutex(); - if( id->pOpen->nLock ){ + if( id->pOpen->nLock && rc==SQLITE_OK ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pOpen->aPending. It will be automatically closed when ** the last lock is cleared. */ int *aNew; struct openCnt *pOpen = id->pOpen; - aNew = sqliteRealloc( pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); + aNew = realloc( pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); if( aNew==0 ){ /* If a malloc fails, just leak the file descriptor */ }else{ pOpen->aPending = aNew; pOpen->aPending[pOpen->nPending] = id->h; Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -11,11 +11,11 @@ ************************************************************************* ** Code for testing the printf() interface to SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.190 2006/01/15 00:13:16 drh Exp $ +** $Id: test1.c,v 1.191 2006/01/15 02:30:58 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include "os.h" #include @@ -164,11 +164,11 @@ ** behavior varies from machine to machine. The solution used her is ** to test the string right after it is generated to see if it can be ** understood by scanf, and if not, try prepending an "0x" to see if ** that helps. If nothing works, a fatal error is generated. */ -static int makePointerStr(Tcl_Interp *interp, char *zPtr, void *p){ +int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p){ sqlite3_snprintf(100, zPtr, "%p", p); return TCL_OK; } /* @@ -2094,11 +2094,11 @@ Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0); return TCL_ERROR; } if( pStmt ){ - if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; + if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); } return TCL_OK; } @@ -2151,11 +2151,11 @@ Tcl_IncrRefCount(pTail); Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0); Tcl_DecrRefCount(pTail); if( pStmt ){ - if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; + if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; } Tcl_AppendResult(interp, zBuf, 0); #endif /* SQLITE_OMIT_UTF16 */ return TCL_OK; } @@ -2181,11 +2181,11 @@ } zFilename = Tcl_GetString(objv[1]); rc = sqlite3_open(zFilename, &db); - if( makePointerStr(interp, zBuf, db) ) return TCL_ERROR; + if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* @@ -2210,11 +2210,11 @@ } zFilename = Tcl_GetByteArrayFromObj(objv[1], 0); rc = sqlite3_open16(zFilename, &db); - if( makePointerStr(interp, zBuf, db) ) return TCL_ERROR; + if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); #endif /* SQLITE_OMIT_UTF16 */ return TCL_OK; } @@ -2604,11 +2604,11 @@ rc = sqlite3OsOpenReadWrite(Tcl_GetString(objv[1]), &pFile, &dummy); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char *)errorName(rc), TCL_STATIC); return TCL_ERROR; } - makePointerStr(interp, zBuf, pFile); + sqlite3TestMakePointerStr(interp, zBuf, pFile); Tcl_SetResult(interp, zBuf, 0); return TCL_ERROR; } /* Index: src/test4.c ================================================================== --- src/test4.c +++ src/test4.c @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the the SQLite library in a multithreaded environment. ** -** $Id: test4.c,v 1.14 2006/01/11 23:40:34 drh Exp $ +** $Id: test4.c,v 1.15 2006/01/15 02:30:58 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include "os.h" #if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1 @@ -612,10 +612,77 @@ temp = threadset[i].db; threadset[i].db = threadset[j].db; threadset[j].db = temp; return TCL_OK; } + +/* +** Usage: thread_db_get ID +** +** Return the database connection pointer for the given thread. Then +** remove the pointer from the thread itself. Afterwards, the thread +** can be stopped and the connection can be used by the main thread. +*/ +static int tcl_thread_db_get( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + int i; + char zBuf[100]; + extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID", 0); + return TCL_ERROR; + } + i = parse_thread_id(interp, argv[1]); + if( i<0 ) return TCL_ERROR; + if( !threadset[i].busy ){ + Tcl_AppendResult(interp, "no such thread", 0); + return TCL_ERROR; + } + thread_wait(&threadset[i]); + sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db); + threadset[i].db = 0; + Tcl_SetResult(interp, zBuf, 0); + return TCL_OK; +} + +/* +** Usage: thread_stmt_get ID +** +** Return the database stmt pointer for the given thread. Then +** remove the pointer from the thread itself. +*/ +static int tcl_thread_stmt_get( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + int i; + char zBuf[100]; + extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID", 0); + return TCL_ERROR; + } + i = parse_thread_id(interp, argv[1]); + if( i<0 ) return TCL_ERROR; + if( !threadset[i].busy ){ + Tcl_AppendResult(interp, "no such thread", 0); + return TCL_ERROR; + } + thread_wait(&threadset[i]); + sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt); + threadset[i].pStmt = 0; + Tcl_SetResult(interp, zBuf, 0); + return TCL_OK; +} /* ** Register commands with the TCL interpreter. */ int Sqlitetest4_Init(Tcl_Interp *interp){ @@ -633,10 +700,12 @@ { "thread_error", (Tcl_CmdProc*)tcl_thread_error }, { "thread_compile", (Tcl_CmdProc*)tcl_thread_compile }, { "thread_step", (Tcl_CmdProc*)tcl_thread_step }, { "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize }, { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap }, + { "thread_db_get", (Tcl_CmdProc*)tcl_thread_db_get }, + { "thread_stmt_get", (Tcl_CmdProc*)tcl_thread_stmt_get }, }; int i; for(i=0; i