Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improve the logLockRegion() function in log.c. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | wal |
Files: | files | file ages | folders |
SHA1: |
5e9dd3bd8e829376408925fb4cfcd5bb |
User & Date: | dan 2010-04-14 15:49:40.000 |
Context
2010-04-14
| ||
18:06 | Add tests to check inter-process WAL locking. (check-in: 9435f31358 user: dan tags: wal) | |
15:49 | Improve the logLockRegion() function in log.c. (check-in: 5e9dd3bd8e user: dan tags: wal) | |
11:23 | Fixes for locking issues in WAL mode. (check-in: a9617eff39 user: dan tags: wal) | |
Changes
Changes to src/log.c.
︙ | ︙ | |||
985 986 987 988 989 990 991 | #define LOG_UNLOCK 0 #define LOG_RDLOCK 1 #define LOG_WRLOCK 2 static int logLockRegion(Log *pLog, u32 mRegion, int op){ LogSummary *pSummary = pLog->pSummary; LogLock *p; /* Used to iterate through in-process locks */ | > | < < | | | > > | | | > > > > > > | > > > > > > > > > > | | < | > | | < < > > > > > > > > | > | | | | | | < > > > > > > > > > > > > > > | > > > > > | > > | > > > > > > > > > > > > | < < < | < < < < | < < < < | | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 | #define LOG_UNLOCK 0 #define LOG_RDLOCK 1 #define LOG_WRLOCK 2 static int logLockRegion(Log *pLog, u32 mRegion, int op){ LogSummary *pSummary = pLog->pSummary; LogLock *p; /* Used to iterate through in-process locks */ u32 mOther; /* Locks held by other connections */ u32 mNew; /* New mask for pLog */ assert( /* Writer lock operations */ (op==LOG_WRLOCK && mRegion==(LOG_REGION_C|LOG_REGION_D)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_C|LOG_REGION_D)) /* Normal reader lock operations */ || (op==LOG_RDLOCK && mRegion==(LOG_REGION_A|LOG_REGION_B)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_B)) /* Region D reader lock operations */ || (op==LOG_RDLOCK && mRegion==(LOG_REGION_D)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_D)) /* Checkpointer lock operations */ || (op==LOG_WRLOCK && mRegion==(LOG_REGION_B|LOG_REGION_C)) || (op==LOG_WRLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_B|LOG_REGION_C)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_A|LOG_REGION_B|LOG_REGION_C)) ); /* Assert that a connection never tries to go from an EXCLUSIVE to a ** SHARED lock on a region. Moving from SHARED to EXCLUSIVE sometimes ** happens though (when a region D reader upgrades to a writer). */ assert( op!=LOG_RDLOCK || 0==(pLog->lock.mLock & (mRegion<<8)) ); sqlite3_mutex_enter(pSummary->mutex); /* Calculate a mask of logs held by all connections in this process apart ** from this one. The least significant byte of the mask contains a mask ** of the SHARED logs held. The next least significant byte of the mask ** indicates the EXCLUSIVE locks held. For example, to test if some other ** connection is holding a SHARED lock on region A, or an EXCLUSIVE lock ** on region C, do: ** ** hasSharedOnA = (mOther & (LOG_REGION_A<<0)); ** hasExclusiveOnC = (mOther & (LOG_REGION_C<<8)); ** ** In all masks, if the bit in the EXCLUSIVE byte mask is set, so is the ** corresponding bit in the SHARED mask. */ mOther = 0; for(p=pSummary->pLock; p; p=p->pNext){ assert( (p->mLock & (p->mLock<<8))==(p->mLock&0x0000FF00) ); if( p!=&pLog->lock ){ mOther |= p->mLock; } } /* If this call is to lock a region (not to unlock one), test if locks held ** by any other connection in this process prevent the new locks from ** begin granted. If so, exit the summary mutex and return SQLITE_BUSY. */ if( op && (mOther & (mRegion << (op==LOG_RDLOCK ? 8 : 0))) ){ sqlite3_mutex_leave(pSummary->mutex); return SQLITE_BUSY; } /* Figure out the new log mask for this connection. */ switch( op ){ case LOG_UNLOCK: mNew = (pLog->lock.mLock & ~(mRegion|(mRegion<<8))); break; case LOG_RDLOCK: mNew = (pLog->lock.mLock | mRegion); break; default: assert( op==LOG_WRLOCK ); mNew = (pLog->lock.mLock | (mRegion<<8) | mRegion); break; } /* Now modify the locks held on the log-summary file descriptor. This ** file descriptor is shared by all log connections in this process. ** Therefore: ** ** + If one or more log connections in this process hold a SHARED lock ** on a region, the file-descriptor should hold a SHARED lock on ** the file region. ** ** + If a log connection in this process holds an EXCLUSIVE lock on a ** region, the file-descriptor should also hold an EXCLUSIVE lock on ** the region in question. ** ** If this is an LOG_UNLOCK operation, only regions for which no other ** connection holds a lock should actually be unlocked. And if this ** is a LOG_RDLOCK operation and other connections already hold all ** the required SHARED locks, then no system call is required. */ if( op==LOG_UNLOCK ){ mRegion = (mRegion & ~mOther); } if( (op==LOG_WRLOCK) || (op==LOG_UNLOCK && mRegion) || (op==LOG_RDLOCK && (mOther&mRegion)!=mRegion) ){ struct LockMap { int iStart; /* Byte offset to start locking operation */ int iLen; /* Length field for locking operation */ } aMap[] = { /* 0000 */ {0, 0}, /* 0001 */ {4, 1}, /* 0010 */ {3, 1}, /* 0011 */ {3, 2}, /* 0100 */ {2, 1}, /* 0101 */ {0, 0}, /* 0110 */ {2, 2}, /* 0111 */ {2, 3}, /* 1000 */ {1, 1}, /* 1001 */ {0, 0}, /* 1010 */ {0, 0}, /* 1011 */ {0, 0}, /* 1100 */ {1, 2}, /* 1101 */ {0, 0}, /* 1110 */ {1, 3}, /* 1111 */ {0, 0} }; int rc; /* Return code of fcntl() */ struct flock f; /* Locking operation */ assert( mRegion<ArraySize(aMap) && aMap[mRegion].iStart!=0 ); memset(&f, 0, sizeof(f)); f.l_type = (op==LOG_WRLOCK?F_WRLCK:(op==LOG_RDLOCK?F_RDLCK:F_UNLCK)); f.l_whence = SEEK_SET; f.l_start = 32 + aMap[mRegion].iStart; f.l_len = aMap[mRegion].iLen; rc = fcntl(pSummary->fd, F_SETLK, &f); if( rc!=0 ){ sqlite3_mutex_leave(pSummary->mutex); return SQLITE_BUSY; } } pLog->lock.mLock = mNew; sqlite3_mutex_leave(pSummary->mutex); return SQLITE_OK; } /* ** Try to read the log-summary header. Attempt to verify the header ** checksum. If the checksum can be verified, copy the log-summary |
︙ | ︙ | |||
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 | if( op ){ /* Obtain the writer lock */ int rc = logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_WRLOCK); if( rc!=SQLITE_OK ){ return rc; } if( memcmp(&pLog->hdr, pLog->pSummary->aData, sizeof(pLog->hdr)) ){ return SQLITE_BUSY; } pLog->isWriteLocked = 1; }else if( pLog->isWriteLocked ){ | > > > > > > > | 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 | if( op ){ /* Obtain the writer lock */ int rc = logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_WRLOCK); if( rc!=SQLITE_OK ){ return rc; } /* TODO: What if this is a region D reader? And after writing this ** transaction it continues to hold a read-lock on the db? Maybe we ** need to switch it to a region A reader here so that unlocking C|D ** does not leave the connection with no lock at all. */ assert( pLog->isLocked!=LOG_REGION_D ); if( memcmp(&pLog->hdr, pLog->pSummary->aData, sizeof(pLog->hdr)) ){ return SQLITE_BUSY; } pLog->isWriteLocked = 1; }else if( pLog->isWriteLocked ){ |
︙ | ︙ |