Changeset 25407

Show
Ignore:
Timestamp:
05/09/08 19:53:13 (4 days ago)
Author:
bonefish
Message:
Advisory locking fixes:
* Made the access strategy to vnode::advisory_locking consistent.
  get_advisory_locking() was guarding it with sVnodeMutex,
  create_advisory_locking() was using atomic_pointer_test_and_set(), and
  release_advisory_lock() just set it unguardedly. We do use sVnodeMutex
  consequently, now.
* Beautified create_advisory_locking() (got rid of the gotos,
  reorganized the control flow).
* Fixed race conditions in acquire_advisory_lock(). It was always
  unlocking and relocking the advisory_locking object when it didn't
  have to wait, but in the meantime someone else could have changed the
  locking situation. Reorganized the control flow, so that it only drops
  the lock when it has to fail or wait. Using create_advisory_locking()
  upfront simplifies the code quite a bit (and fixes another race
  condition).

APR's testprocmutex test seems happy now, at least.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • haiku/trunk/src/system/kernel/fs/vfs.cpp

    r25378 r25407  
    2828#include <StorageDefs.h> 
    2929 
     30#include <AutoDeleter.h> 
    3031#include <block_cache.h> 
    3132#include <boot/kernel_args.h> 
     
    152153        sem_id                  wait_sem; 
    153154        LockList                locks; 
     155 
     156        advisory_locking() 
     157                : 
     158                lock(-1), 
     159                wait_sem(-1) 
     160        { 
     161        } 
     162 
     163        ~advisory_locking() 
     164        { 
     165                if (lock >= 0) 
     166                        delete_sem(lock); 
     167                if (wait_sem >= 0) 
     168                        delete_sem(wait_sem); 
     169        } 
    154170}; 
    155171 
     
    190206/*!     \brief Guards sVnodeTable. 
    191207 
    192         The holder is allowed to read/write access sVnodeTable and to 
     208        The holder is allowed read/write access to sVnodeTable and to 
    193209        any unbusy vnode in that table, save to the immutable fields (device, id, 
    194210        private_node, mount) to which 
     
    11231139                return B_FILE_ERROR; 
    11241140 
    1125         struct advisory_locking *locking = new(std::nothrow) advisory_locking; 
    1126         if (locking == NULL) 
    1127                 return B_NO_MEMORY; 
    1128  
    1129         status_t status; 
    1130  
    1131         locking->wait_sem = create_sem(0, "advisory lock"); 
    1132         if (locking->wait_sem < B_OK) { 
    1133                 status = locking->wait_sem; 
    1134                 goto err1; 
    1135         } 
    1136  
    1137         locking->lock = create_sem(0, "advisory locking"); 
    1138         if (locking->lock < B_OK) { 
    1139                 status = locking->lock; 
    1140                 goto err2; 
    1141         } 
    1142  
    1143         // We need to set the locking structure atomically - someone 
    1144         // else might set one at the same time 
    1145         do { 
    1146                 if (atomic_pointer_test_and_set(&vnode->advisory_locking, locking, 
    1147                                 (advisory_locking*)NULL) == NULL) 
     1141        ObjectDeleter<advisory_locking> lockingDeleter; 
     1142        struct advisory_locking *locking = NULL; 
     1143 
     1144        while (get_advisory_locking(vnode) == NULL) { 
     1145                // no locking object set on the vnode yet, create one 
     1146                if (locking == NULL) { 
     1147                        locking = new(std::nothrow) advisory_locking; 
     1148                        if (locking == NULL) 
     1149                                return B_NO_MEMORY; 
     1150                        lockingDeleter.SetTo(locking); 
     1151 
     1152                        locking->wait_sem = create_sem(0, "advisory lock"); 
     1153                        if (locking->wait_sem < B_OK) 
     1154                                return locking->wait_sem; 
     1155 
     1156                        locking->lock = create_sem(0, "advisory locking"); 
     1157                        if (locking->lock < B_OK) 
     1158                                return locking->lock; 
     1159                } 
     1160 
     1161                // set our newly created locking object 
     1162                MutexLocker _(sVnodeMutex); 
     1163                if (vnode->advisory_locking == NULL) { 
     1164                        vnode->advisory_locking = locking; 
     1165                        lockingDeleter.Detach(); 
    11481166                        return B_OK; 
    1149         } while (get_advisory_locking(vnode) == NULL); 
    1150  
    1151         status = B_OK; 
    1152                 // we delete the one we've just created, but nevertheless, the vnode 
    1153                 // does have a locking structure now 
    1154  
    1155         delete_sem(locking->lock); 
    1156 err2: 
    1157         delete_sem(locking->wait_sem); 
    1158 err1: 
    1159         delete locking; 
    1160         return status; 
     1167                } 
     1168        } 
     1169 
     1170        // The vnode already had a locking object. That's just as well. 
     1171 
     1172        return B_OK; 
    11611173} 
    11621174 
     
    12881300                locking = get_advisory_locking(vnode); 
    12891301                if (locking != NULL) { 
     1302                        MutexLocker locker(sVnodeMutex); 
     1303 
    12901304                        // the locking could have been changed in the mean time 
    12911305                        if (locking->locks.IsEmpty()) { 
    12921306                                vnode->advisory_locking = NULL; 
     1307                                locker.Unlock(); 
    12931308 
    12941309                                // we've detached the locking from the vnode, so we can 
     
    12991314                        } else { 
    13001315                                // the locking is in use again 
     1316                                locker.Unlock(); 
    13011317                                release_sem_etc(locking->lock, 1, B_DO_NOT_RESCHEDULE); 
    13021318                        } 
     
    13291345        // TODO: do deadlock detection! 
    13301346 
    1331 restart: 
    1332         // if this vnode has an advisory_locking structure attached, 
    1333         // lock that one and search for any colliding file lock 
    1334         struct advisory_locking *locking = get_advisory_locking(vnode); 
    1335         team_id team = team_get_current_team_id(); 
    1336         sem_id waitForLock = -1; 
    1337  
    1338         if (locking != NULL) { 
     1347        struct advisory_locking *locking; 
     1348        sem_id waitForLock; 
     1349 
     1350        while (true) { 
     1351                // if this vnode has an advisory_locking structure attached, 
     1352                // lock that one and search for any colliding file lock 
     1353                status = create_advisory_locking(vnode); 
     1354                if (status != B_OK) 
     1355                        return status; 
     1356 
     1357                locking = vnode->advisory_locking; 
     1358                team_id team = team_get_current_team_id(); 
     1359                waitForLock = -1; 
     1360 
    13391361                // test for collisions 
    13401362                LockList::Iterator iterator = locking->locks.GetIterator(); 
    13411363                while (iterator.HasNext()) { 
    13421364                        struct advisory_lock *lock = iterator.Next(); 
    1343  
     1365         
    13441366                        // TODO: locks from the same team might be joinable! 
    13451367                        if (lock->team != team && advisory_lock_intersects(lock, flock)) { 
     
    13531375                } 
    13541376 
    1355                 if (waitForLock < B_OK || !wait) 
     1377                if (waitForLock < 0) 
     1378                        break; 
     1379 
     1380                // We need to wait. Do that or fail now, if we've been asked not to. 
     1381 
     1382                if (!wait) { 
    13561383                        put_advisory_locking(locking); 
    1357         } 
    1358  
    1359         // wait for the lock if we have to, or else return immediately 
    1360  
    1361         if (waitForLock >= B_OK) { 
    1362                 if (!wait) 
    1363                         status = session != -1 ? B_WOULD_BLOCK : B_PERMISSION_DENIED; 
    1364                 else { 
    1365                         status = switch_sem_etc(locking->lock, waitForLock, 1, 
    1366                                 B_CAN_INTERRUPT, 0); 
    1367                         if (status == B_OK) { 
    1368                                 // see if we're still colliding 
    1369                                 goto restart; 
    1370                         } 
    1371                 } 
    1372         } 
    1373  
    1374         if (status < B_OK) 
    1375                 return status; 
     1384                        return session != -1 ? B_WOULD_BLOCK : B_PERMISSION_DENIED; 
     1385                } 
     1386 
     1387                status = switch_sem_etc(locking->lock, waitForLock, 1, 
     1388                        B_CAN_INTERRUPT, 0); 
     1389                if (status != B_OK && status != B_BAD_SEM_ID) 
     1390                        return status; 
     1391 
     1392                // We have been notified, but we need to re-lock the locking object. So 
     1393                // go another round... 
     1394        } 
    13761395 
    13771396        // install new lock 
    1378  
    1379         locking = get_advisory_locking(vnode); 
    1380         if (locking == NULL) { 
    1381                 // we need to create a new locking object 
    1382                 status = create_advisory_locking(vnode); 
    1383                 if (status < B_OK) 
    1384                         return status; 
    1385  
    1386                 locking = vnode->advisory_locking; 
    1387                         // we own the locking object, so it can't go away 
    1388         } 
    13891397 
    13901398        struct advisory_lock *lock = (struct advisory_lock *)malloc(