LCOV - code coverage report
Current view: top level - lib - fat-rwlock.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 92 97 94.8 %
Date: 2016-09-14 01:02:56 Functions: 10 10 100.0 %
Branches: 24 32 75.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2013, 2014 Nicira, Inc.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at:
       7                 :            :  *
       8                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include <config.h>
      18                 :            : 
      19                 :            : #include "fat-rwlock.h"
      20                 :            : 
      21                 :            : #include <errno.h>
      22                 :            : 
      23                 :            : #include "openvswitch/hmap.h"
      24                 :            : #include "openvswitch/list.h"
      25                 :            : #include "ovs-thread.h"
      26                 :            : #include "random.h"
      27                 :            : 
      28                 :            : struct fat_rwlock_slot {
      29                 :            :     /* Membership in rwlock's list of "struct fat_rwlock_slot"s.
      30                 :            :      *
      31                 :            :      * fat_rwlock_destroy() sets 'rwlock' to NULL to indicate that this
      32                 :            :      * slot may be destroyed. */
      33                 :            :     struct ovs_list list_node;  /* In struct rwlock's 'threads' list. */
      34                 :            :     struct fat_rwlock *rwlock;  /* Owner. */
      35                 :            : 
      36                 :            :     /* Mutex.
      37                 :            :      *
      38                 :            :      * A thread holding the read-lock holds its own mutex.
      39                 :            :      *
      40                 :            :      * A thread holding the write-lock holds every thread's mutex, plus
      41                 :            :      * 'rwlock->mutex'. */
      42                 :            :     struct ovs_mutex mutex;
      43                 :            : 
      44                 :            :     /* This thread's locking status for 'rwlock':
      45                 :            :      *
      46                 :            :      *     - 0: This thread does not have any lock on 'rwlock'.  This thread
      47                 :            :      *       does not have 'mutex' locked.
      48                 :            :      *
      49                 :            :      *     - 1: This thread has a read-lock on 'rwlock' and holds 'mutex'.
      50                 :            :      *
      51                 :            :      *     - 2...UINT_MAX-1: This thread has recursively taken the read-lock on
      52                 :            :      *       'rwlock' to the level of 'depth'.  This thread holds 'mutex'.
      53                 :            :      *
      54                 :            :      *     - UINT_MAX: This thread has the write-lock on 'rwlock' and holds
      55                 :            :      *       'mutex' (plus the 'mutex' of all of 'rwlock''s other slots).
      56                 :            :      *
      57                 :            :      * Accessed only by the slot's own thread, so no synchronization is
      58                 :            :      * needed. */
      59                 :            :     unsigned int depth;
      60                 :            : };
      61                 :            : 
      62                 :            : static void
      63                 :        329 : free_slot(struct fat_rwlock_slot *slot)
      64                 :            : {
      65         [ -  + ]:        329 :     if (slot->depth) {
      66                 :          0 :         abort();
      67                 :            :     }
      68                 :            : 
      69                 :        329 :     ovs_list_remove(&slot->list_node);
      70                 :        329 :     free_cacheline(slot);
      71                 :        329 : }
      72                 :            : 
      73                 :            : static void
      74                 :        141 : slot_destructor(void *slot_)
      75                 :            : {
      76                 :        141 :     struct fat_rwlock_slot *slot = slot_;
      77                 :        141 :     struct fat_rwlock *rwlock = slot->rwlock;
      78                 :            : 
      79                 :        141 :     ovs_mutex_lock(&rwlock->mutex);
      80                 :        141 :     free_slot(slot);
      81                 :        141 :     ovs_mutex_unlock(&rwlock->mutex);
      82                 :        141 : }
      83                 :            : 
      84                 :            : /* Initialize 'rwlock' as a new fat_rwlock. */
      85                 :            : void
      86                 :       1353 : fat_rwlock_init(struct fat_rwlock *rwlock)
      87                 :            : {
      88                 :       1353 :     ovsthread_key_create(&rwlock->key, slot_destructor);
      89                 :       1353 :     ovs_mutex_init(&rwlock->mutex);
      90                 :       1353 :     ovs_mutex_lock(&rwlock->mutex);
      91                 :       1353 :     ovs_list_init(&rwlock->threads);
      92                 :       1353 :     ovs_mutex_unlock(&rwlock->mutex);
      93                 :       1353 : }
      94                 :            : 
      95                 :            : /* Destroys 'rwlock', which must not be locked or otherwise in use by any
      96                 :            :  * thread. */
      97                 :            : void
      98                 :        188 : fat_rwlock_destroy(struct fat_rwlock *rwlock)
      99                 :            : {
     100                 :            :     struct fat_rwlock_slot *slot, *next;
     101                 :            : 
     102                 :            :     /* Order is important here.  By destroying the thread-specific data first,
     103                 :            :      * before we destroy the slots, we ensure that the thread-specific
     104                 :            :      * data destructor can't race with our loop below. */
     105                 :        188 :     ovsthread_key_delete(rwlock->key);
     106                 :            : 
     107 [ +  + ][ +  + ]:        376 :     LIST_FOR_EACH_SAFE (slot, next, list_node, &rwlock->threads) {
     108                 :        188 :         free_slot(slot);
     109                 :            :     }
     110                 :        188 :     ovs_mutex_destroy(&rwlock->mutex);
     111                 :        188 : }
     112                 :            : 
     113                 :            : static struct fat_rwlock_slot *
     114                 :     111283 : fat_rwlock_get_slot__(struct fat_rwlock *rwlock)
     115                 :            : {
     116                 :            :     struct fat_rwlock_slot *slot;
     117                 :            : 
     118                 :            :     /* Fast path. */
     119                 :     111283 :     slot = ovsthread_getspecific(rwlock->key);
     120         [ +  + ]:     111283 :     if (slot) {
     121                 :     109793 :         return slot;
     122                 :            :     }
     123                 :            : 
     124                 :            :     /* Slow path: create a new slot for 'rwlock' in this thread. */
     125                 :            : 
     126                 :       1490 :     slot = xmalloc_cacheline(sizeof *slot);
     127                 :       1490 :     slot->rwlock = rwlock;
     128                 :       1490 :     ovs_mutex_init(&slot->mutex);
     129                 :       1490 :     slot->depth = 0;
     130                 :            : 
     131                 :       1490 :     ovs_mutex_lock(&rwlock->mutex);
     132                 :       1490 :     ovs_list_push_back(&rwlock->threads, &slot->list_node);
     133                 :       1490 :     ovs_mutex_unlock(&rwlock->mutex);
     134                 :            : 
     135                 :       1490 :     ovsthread_setspecific(rwlock->key, slot);
     136                 :            : 
     137                 :       1490 :     return slot;
     138                 :            : }
     139                 :            : 
     140                 :            : /* Locks 'rwlock' for reading.  The read-lock is recursive: it may be acquired
     141                 :            :  * any number of times by a single thread (which must then release it the same
     142                 :            :  * number of times for it to truly be released). */
     143                 :            : void
     144                 :      22675 : fat_rwlock_rdlock(const struct fat_rwlock *rwlock_)
     145                 :            :     OVS_ACQ_RDLOCK(rwlock_)
     146                 :            :     OVS_NO_THREAD_SAFETY_ANALYSIS
     147                 :            : {
     148                 :      22675 :     struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_);
     149                 :      22675 :     struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock);
     150                 :            : 
     151      [ -  +  - ]:      22675 :     switch (this->depth) {
     152                 :            :     case UINT_MAX:
     153                 :            :         /* This thread already holds the write-lock. */
     154                 :          0 :         abort();
     155                 :            : 
     156                 :            :     case 0:
     157                 :      22675 :         ovs_mutex_lock(&this->mutex);
     158                 :            :         /* fall through */
     159                 :            :     default:
     160                 :      22675 :         this->depth++;
     161                 :      22675 :         break;
     162                 :            :     }
     163                 :      22675 : }
     164                 :            : 
     165                 :            : static struct fat_rwlock_slot *
     166                 :       6185 : fat_rwlock_try_get_slot__(struct fat_rwlock *rwlock)
     167                 :            : {
     168                 :            :     struct fat_rwlock_slot *slot;
     169                 :            : 
     170                 :            :     /* Fast path. */
     171                 :       6185 :     slot = ovsthread_getspecific(rwlock->key);
     172         [ +  + ]:       6185 :     if (slot) {
     173                 :       6165 :         return slot;
     174                 :            :     }
     175                 :            : 
     176                 :            :     /* Slow path: create a new slot for 'rwlock' in this thread. */
     177                 :            : 
     178         [ +  - ]:         20 :     if (!ovs_mutex_trylock(&rwlock->mutex)) {
     179                 :         20 :         slot = xmalloc_cacheline(sizeof *slot);
     180                 :         20 :         slot->rwlock = rwlock;
     181                 :         20 :         ovs_mutex_init(&slot->mutex);
     182                 :         20 :         slot->depth = 0;
     183                 :            : 
     184                 :         20 :         ovs_list_push_back(&rwlock->threads, &slot->list_node);
     185                 :         20 :         ovs_mutex_unlock(&rwlock->mutex);
     186                 :         20 :         ovsthread_setspecific(rwlock->key, slot);
     187                 :            :     }
     188                 :            : 
     189                 :         20 :     return slot;
     190                 :            : }
     191                 :            : 
     192                 :            : /* Tries to lock 'rwlock' for reading.  If successful, returns 0.  If taking
     193                 :            :  * the lock would require blocking, returns EBUSY (without blocking). */
     194                 :            : int
     195                 :       6185 : fat_rwlock_tryrdlock(const struct fat_rwlock *rwlock_)
     196                 :            :     OVS_TRY_RDLOCK(0, rwlock_)
     197                 :            :     OVS_NO_THREAD_SAFETY_ANALYSIS
     198                 :            : {
     199                 :       6185 :     struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_);
     200                 :       6185 :     struct fat_rwlock_slot *this = fat_rwlock_try_get_slot__(rwlock);
     201                 :            :     int error;
     202                 :            : 
     203         [ -  + ]:       6185 :     if (!this) {
     204                 :          0 :         return EBUSY;
     205                 :            :     }
     206                 :            : 
     207      [ +  +  + ]:       6185 :     switch (this->depth) {
     208                 :            :     case UINT_MAX:
     209                 :          3 :         return EBUSY;
     210                 :            : 
     211                 :            :     case 0:
     212                 :       5727 :         error = ovs_mutex_trylock(&this->mutex);
     213         [ -  + ]:       5727 :         if (error) {
     214                 :          0 :             return error;
     215                 :            :         }
     216                 :            :         /* fall through */
     217                 :            :     default:
     218                 :       6182 :         this->depth++;
     219                 :       6182 :         break;
     220                 :            :     }
     221                 :            : 
     222                 :       6182 :     return 0;
     223                 :            : }
     224                 :            : 
     225                 :            : /* Locks 'rwlock' for writing.
     226                 :            :  *
     227                 :            :  * The write lock is not recursive. */
     228                 :            : void
     229                 :      30151 : fat_rwlock_wrlock(const struct fat_rwlock *rwlock_)
     230                 :            :     OVS_ACQ_WRLOCK(rwlock_)
     231                 :            :     OVS_NO_THREAD_SAFETY_ANALYSIS
     232                 :            : {
     233                 :      30151 :     struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_);
     234                 :      30151 :     struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock);
     235                 :            :     struct fat_rwlock_slot *slot;
     236                 :            : 
     237         [ -  + ]:      30151 :     ovs_assert(!this->depth);
     238                 :      30151 :     this->depth = UINT_MAX;
     239                 :            : 
     240                 :      30151 :     ovs_mutex_lock(&rwlock->mutex);
     241         [ +  + ]:      82912 :     LIST_FOR_EACH (slot, list_node, &rwlock->threads) {
     242                 :      52761 :         ovs_mutex_lock(&slot->mutex);
     243                 :            :     }
     244                 :      30151 : }
     245                 :            : 
     246                 :            : /* Unlocks 'rwlock', which the current thread must have locked for reading or
     247                 :            :  * for writing.  If the read lock has been taken recursively, it must be
     248                 :            :  * released the same number of times to be truly released. */
     249                 :            : void
     250                 :      58457 : fat_rwlock_unlock(const struct fat_rwlock *rwlock_)
     251                 :            :     OVS_RELEASES(rwlock_)
     252                 :            :     OVS_NO_THREAD_SAFETY_ANALYSIS
     253                 :            : {
     254                 :      58457 :     struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_);
     255                 :      58457 :     struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock);
     256                 :            :     struct fat_rwlock_slot *slot;
     257                 :            : 
     258   [ +  -  +  + ]:      58457 :     switch (this->depth) {
     259                 :            :     case UINT_MAX:
     260         [ +  + ]:      81794 :         LIST_FOR_EACH (slot, list_node, &rwlock->threads) {
     261                 :      52194 :             ovs_mutex_unlock(&slot->mutex);
     262                 :            :         }
     263                 :      29600 :         ovs_mutex_unlock(&rwlock->mutex);
     264                 :      29600 :         this->depth = 0;
     265                 :      29600 :         break;
     266                 :            : 
     267                 :            :     case 0:
     268                 :            :         /* This thread doesn't hold any lock. */
     269                 :          0 :         abort();
     270                 :            : 
     271                 :            :     case 1:
     272                 :      28402 :         ovs_mutex_unlock(&this->mutex);
     273                 :            :     default:
     274                 :      28857 :         this->depth--;
     275                 :      28857 :         break;
     276                 :            :     }
     277                 :      58457 : }

Generated by: LCOV version 1.12