Changeset 24020

Show
Ignore:
Timestamp:
02/19/08 14:16:36 (9 months ago)
Author:
axeld
Message:

Improved (and tested) the advisory file locking mechanism a bit:
* our flock::l_len was inclusive, while it's exclusive (the last byte locked

is (l_start - 1 + l_len) not just (l_start + l_len).

* F_UNLCK removes all locks of the calling process that are within the specified

region - existing locks might also cut or divided.

* Apparently, a single team can lock the same region as often as it wants.
* advisory_locking is now using a DoublyLinkedList instead of its C counterpart.
* advisory_lock now has start + end fields, instead of offset + len, it's

handier this way.

* This fixes bug #1791.

Files:
1 modified

Legend:

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

    r23983 r24020  
    122122}; 
    123123 
     124struct advisory_lock : public DoublyLinkedListLinkImpl<advisory_lock> { 
     125        list_link               link; 
     126        team_id                 team; 
     127        pid_t                   session; 
     128        off_t                   start; 
     129        off_t                   end; 
     130        bool                    shared; 
     131}; 
     132 
     133typedef DoublyLinkedList<advisory_lock> LockList; 
     134 
    124135struct advisory_locking { 
    125136        sem_id                  lock; 
    126137        sem_id                  wait_sem; 
    127         struct list             locks; 
    128 }; 
    129  
    130 struct advisory_lock { 
    131         list_link               link; 
    132         team_id                 team; 
    133         pid_t                   session; 
    134         off_t                   offset; 
    135         off_t                   length; 
    136         bool                    shared; 
     138        LockList                locks; 
    137139}; 
    138140 
     
    10481050                return B_FILE_ERROR; 
    10491051 
    1050         struct advisory_locking *locking = (struct advisory_locking *)malloc( 
    1051                 sizeof(struct advisory_locking)); 
     1052        struct advisory_locking *locking = new(std::nothrow) advisory_locking; 
    10521053        if (locking == NULL) 
    10531054                return B_NO_MEMORY; 
     
    10671068        } 
    10681069 
    1069         list_init(&locking->locks); 
    1070  
    10711070        // We need to set the locking structure atomically - someone 
    10721071        // else might set one at the same time 
    10731072        do { 
    1074                 if (atomic_test_and_set((vint32 *)&vnode->advisory_locking, (addr_t)locking, 
    1075                                 NULL) == NULL) 
     1073                if (atomic_test_and_set((vint32 *)&vnode->advisory_locking, 
     1074                                (addr_t)locking, NULL) == NULL) 
    10761075                        return B_OK; 
    10771076        } while (get_advisory_locking(vnode) == NULL); 
     
    10851084        delete_sem(locking->wait_sem); 
    10861085err1: 
    1087         free(locking); 
     1086        delete locking; 
    10881087        return status; 
    10891088} 
     
    11031102        status_t status = B_BAD_VALUE; 
    11041103 
    1105         struct advisory_lock *lock = NULL; 
    1106         while ((lock = (struct advisory_lock *)list_get_next_item(&locking->locks, lock)) != NULL) { 
     1104        LockList::Iterator iterator = locking->locks.GetIterator(); 
     1105        while (iterator.HasNext()) { 
     1106                struct advisory_lock *lock = iterator.Next(); 
     1107 
    11071108                if (lock->team == team) { 
    1108                         flock->l_start = lock->offset; 
    1109                         flock->l_len = lock->length; 
     1109                        flock->l_start = lock->start; 
     1110                        flock->l_len = lock->end - lock->start + 1; 
    11101111                        status = B_OK; 
    11111112                        break; 
     
    11151116        put_advisory_locking(locking); 
    11161117        return status; 
     1118} 
     1119 
     1120 
     1121/*! Returns \c true when either \a flock is \c NULL or the \a flock intersects 
     1122        with the advisory_lock \a lock. 
     1123*/ 
     1124static bool 
     1125advisory_lock_intersects(struct advisory_lock *lock, struct flock *flock) 
     1126{ 
     1127        if (flock == NULL) 
     1128                return true; 
     1129 
     1130        return lock->start <= flock->l_start - 1 + flock->l_len 
     1131                && lock->end >= flock->l_start; 
    11171132} 
    11181133 
     
    11281143        struct advisory_locking *locking = get_advisory_locking(vnode); 
    11291144        if (locking == NULL) 
    1130                 return flock != NULL ? B_BAD_VALUE : B_OK; 
    1131  
     1145                return B_OK; 
     1146 
     1147        // TODO: use the thread ID instead?? 
    11321148        team_id team = team_get_current_team_id(); 
    11331149        pid_t session = thread_get_current_thread()->team->session_id; 
    11341150 
    1135         // find matching lock entry 
    1136  
    1137         status_t status = B_BAD_VALUE; 
    1138         struct advisory_lock *lock = NULL; 
    1139         while ((lock = (struct advisory_lock *)list_get_next_item(&locking->locks, 
    1140                         lock)) != NULL) { 
    1141                 if (lock->team == team && (flock == NULL 
    1142                                 || (flock != NULL && lock->offset == flock->l_start 
    1143                                         && lock->length == flock->l_len)) 
    1144                         || lock->session == session) { 
    1145                         // we found our lock, free it 
    1146                         list_remove_item(&locking->locks, lock); 
     1151        // find matching lock entries 
     1152 
     1153        LockList::Iterator iterator = locking->locks.GetIterator(); 
     1154        while (iterator.HasNext()) { 
     1155                struct advisory_lock *lock = iterator.Next(); 
     1156                bool removeLock = false; 
     1157 
     1158                if (lock->session == session) 
     1159                        removeLock = true; 
     1160                else if (lock->team == team && advisory_lock_intersects(lock, flock)) { 
     1161                        bool endsBeyond = false; 
     1162                        bool startsBefore = false; 
     1163                        if (flock != NULL) { 
     1164                                startsBefore = lock->start < flock->l_start; 
     1165                                endsBeyond = lock->end > flock->l_start - 1 + flock->l_len; 
     1166                        } 
     1167 
     1168                        if (!startsBefore && !endsBeyond) { 
     1169                                // lock is completely contained in flock 
     1170                                removeLock = true; 
     1171                        } else if (startsBefore && !endsBeyond) { 
     1172                                // cut the end of the lock 
     1173                                lock->end = flock->l_start - 1; 
     1174                        } else if (!startsBefore && endsBeyond) { 
     1175                                // cut the start of the lock 
     1176                                lock->start = flock->l_start + flock->l_len; 
     1177                        } else { 
     1178                                // divide the lock into two locks 
     1179                                struct advisory_lock *secondLock = new advisory_lock; 
     1180                                if (secondLock == NULL) { 
     1181                                        // TODO: we should probably revert the locks we already 
     1182                                        // changed... (ie. allocate upfront) 
     1183                                        put_advisory_locking(locking); 
     1184                                        return B_NO_MEMORY; 
     1185                                } 
     1186 
     1187                                lock->end = flock->l_start - 1; 
     1188 
     1189                                secondLock->team = lock->team; 
     1190                                secondLock->session = lock->session; 
     1191                                // values must already be normalized when getting here 
     1192                                secondLock->start = flock->l_start + flock->l_len; 
     1193                                secondLock->end = lock->end; 
     1194                                secondLock->shared = lock->shared; 
     1195 
     1196                                locking->locks.Add(secondLock); 
     1197                        } 
     1198                } 
     1199 
     1200                if (removeLock) { 
     1201                        // this lock is no longer used 
     1202                        iterator.Remove(); 
    11471203                        free(lock); 
    1148                         status = B_OK; 
    1149                         break; 
    1150                 } 
    1151         } 
    1152  
    1153         bool removeLocking = list_is_empty(&locking->locks); 
     1204                } 
     1205        } 
     1206 
     1207        bool removeLocking = locking->locks.IsEmpty(); 
    11541208        release_sem_etc(locking->wait_sem, 1, B_RELEASE_ALL); 
    11551209 
    11561210        put_advisory_locking(locking); 
    11571211 
    1158         if (status < B_OK) 
    1159                 return status; 
    1160  
    11611212        if (removeLocking) { 
    1162                 // we can remove the whole advisory locking structure; it's no longer used 
     1213                // We can remove the whole advisory locking structure; it's no 
     1214                // longer used 
    11631215                locking = get_advisory_locking(vnode); 
    11641216                if (locking != NULL) { 
    11651217                        // the locking could have been changed in the mean time 
    1166                         if (list_is_empty(&locking->locks)) { 
     1218                        if (locking->locks.IsEmpty()) { 
    11671219                                vnode->advisory_locking = NULL; 
    11681220 
    1169                                 // we've detached the locking from the vnode, so we can safely delete it 
     1221                                // we've detached the locking from the vnode, so we can 
     1222                                // safely delete it 
    11701223                                delete_sem(locking->lock); 
    11711224                                delete_sem(locking->wait_sem); 
    1172                                 free(locking); 
     1225                                delete locking; 
    11731226                        } else { 
    11741227                                // the locking is in use again 
     
    12071260        // lock that one and search for any colliding file lock 
    12081261        struct advisory_locking *locking = get_advisory_locking(vnode); 
     1262        team_id team = team_get_current_team_id(); 
    12091263        sem_id waitForLock = -1; 
    12101264 
    12111265        if (locking != NULL) { 
    12121266                // test for collisions 
    1213                 struct advisory_lock *lock = NULL; 
    1214                 while ((lock = (struct advisory_lock *)list_get_next_item( 
    1215                                 &locking->locks, lock)) != NULL) { 
    1216                         if (lock->offset <= flock->l_start + flock->l_len 
    1217                                 && lock->offset + lock->length > flock->l_start) { 
     1267                LockList::Iterator iterator = locking->locks.GetIterator(); 
     1268                while (iterator.HasNext()) { 
     1269                        struct advisory_lock *lock = iterator.Next(); 
     1270 
     1271                        // TODO: locks from the same team might be joinable! 
     1272                        if (lock->team != team && advisory_lock_intersects(lock, flock)) { 
    12181273                                // locks do overlap 
    12191274                                if (!shared || !lock->shared) { 
     
    12721327        lock->session = session; 
    12731328        // values must already be normalized when getting here 
    1274         lock->offset = flock->l_start; 
    1275         lock->length = flock->l_len; 
     1329        lock->start = flock->l_start; 
     1330        lock->end = flock->l_start - 1 + flock->l_len; 
    12761331        lock->shared = shared; 
    12771332 
    1278         list_add_item(&locking->locks, lock); 
     1333        locking->locks.Add(lock); 
    12791334        put_advisory_locking(locking); 
    12801335 
     
    23682423        kprintf("   wait_sem:    %ld", locking->wait_sem); 
    23692424 
    2370         struct advisory_lock *lock = NULL; 
    23712425        int32 index = 0; 
    2372         while ((lock = (advisory_lock *)list_get_next_item(&locking->locks, lock)) != NULL) { 
    2373                 kprintf("   [%2ld] team:   %ld\n", index, lock->team); 
    2374                 kprintf("        offset: %Ld\n", lock->offset); 
    2375                 kprintf("        length: %Ld\n", lock->length); 
     2426        LockList::Iterator iterator = locking->locks.GetIterator(); 
     2427        while (iterator.HasNext()) { 
     2428                struct advisory_lock *lock = iterator.Next(); 
     2429 
     2430                kprintf("   [%2ld] team:   %ld\n", index++, lock->team); 
     2431                kprintf("        start:  %Ld\n", lock->start); 
     2432                kprintf("        end:    %Ld\n", lock->end); 
    23762433                kprintf("        shared? %s\n", lock->shared ? "yes" : "no"); 
    23772434        }