nfs-ganesha 1.4

RW_Lock.c

Go to the documentation of this file.
00001 /*
00002  * vim:expandtab:shiftwidth=8:tabstop=8:
00003  *
00004  * Copyright CEA/DAM/DIF  (2008)
00005  * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
00006  *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
00007  *
00008  *
00009  * This program is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 3 of the License, or (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00022  *
00023  * ---------------------------------------
00024  */
00025 
00035 #ifdef HAVE_CONFIG_H
00036 #include "config.h"
00037 #endif
00038 
00039 #include <pthread.h>
00040 #include <stdio.h>
00041 #include <stdlib.h>
00042 #include <string.h>
00043 #include <execinfo.h>
00044 #include "RW_Lock.h"
00045 #include <execinfo.h>
00046 #include <malloc.h>
00047 #include <assert.h>
00048 
00049 /*
00050  * Debugging function
00051  */
00052 static inline void print_lock(char *s, rw_lock_t * plock)
00053 {
00054   
00055   LogFullDebug(COMPONENT_RW_LOCK,
00056                "%s: id = %u:  Lock:%p State: nbr_active = %d, nbr_waiting = %d, nbw_active = %d, nbw_waiting = %d",
00057                s, (unsigned int)pthread_self(), plock, plock->nbr_active, plock->nbr_waiting,
00058                plock->nbw_active, plock->nbw_waiting);
00059 }                               /* print_lock */
00060 
00061 #define DEBUG_STACK_SIZE 1000
00062 void dbg_backtrace(void)
00063 {
00064   int j, nptrs;
00065   void *buffer[DEBUG_STACK_SIZE];
00066   char **strings;
00067 
00068   nptrs = backtrace(buffer, DEBUG_STACK_SIZE);
00069 
00070   strings = backtrace_symbols(buffer, nptrs);
00071   if (strings == NULL)
00072     {
00073       LogFullDebug(COMPONENT_RW_LOCK, "dbg_backtrace...No symbols found.\n");
00074       return;
00075     }
00076 
00077   for (j = 0; j < nptrs; j++)
00078     LogFullDebug(COMPONENT_RW_LOCK, "backtrace: %s\n", strings[j]);
00079 
00080   free(strings);
00081 }
00082 
00083 
00084 /* 
00085  * Take the lock for reading 
00086  */
00087 int P_r(rw_lock_t * plock)
00088 {
00089   P(plock->mutexProtect);
00090 
00091   print_lock("P_r.1", plock);
00092 
00093   plock->nbr_waiting++;
00094 
00095   /* no new read lock is granted if writters are waiting or active */
00096   while(plock->nbw_active > 0 || plock->nbw_waiting > 0)
00097     pthread_cond_wait(&(plock->condRead), &(plock->mutexProtect));
00098   
00099   assert(plock->nbw_active == 0);
00100   assert(plock->nbw_waiting == 0);
00101 
00102   /* There is no active or waiting writters, readers can go ... */
00103   plock->nbr_waiting--;
00104   plock->nbr_active++;
00105   assert(plock->nbr_waiting >= 0);
00106 
00107   print_lock("P_r.end", plock);
00108   V(plock->mutexProtect);
00109 
00110 
00111   return 0;
00112 }                               /* P_r */
00113 
00114 /*
00115  * Release the lock after reading 
00116  */
00117 int V_r(rw_lock_t * plock)
00118 {
00119   P(plock->mutexProtect);
00120 
00121   print_lock("V_r.1", plock);
00122 
00123   /* I am a reader that is no more active */
00124   if(plock->nbr_active == 0)
00125     // TODO: Failing on virtual machines
00126     // dbg_backtrace();
00127     print_lock("V_r.1_1", plock);
00128   else
00129     plock->nbr_active--;
00130 
00131 
00132   /* I was the last active reader, and there are some waiting writters, I let one of them go */
00133   if(plock->nbr_active == 0 && plock->nbw_waiting > 0)
00134     {
00135       print_lock("V_r.2 lecteur libere un redacteur", plock);
00136       pthread_cond_signal(&plock->condWrite);
00137     }
00138 
00139   print_lock("V_r.end", plock);
00140 
00141   V(plock->mutexProtect);
00142 
00143   return 0;
00144 }                               /* V_r */
00145 
00146 /*
00147  * Take the lock for writting 
00148  */
00149 int P_w(rw_lock_t * plock)
00150 {
00151   P(plock->mutexProtect);
00152 
00153   print_lock("P_w.1", plock);
00154 
00155   plock->nbw_waiting++;
00156 
00157   /* nobody must be active obtain exclusive lock */
00158   while(plock->nbr_active > 0 || plock->nbw_active > 0)
00159     pthread_cond_wait(&plock->condWrite, &plock->mutexProtect);
00160   assert(plock->nbr_active == 0);
00161   assert(plock->nbw_active == 0);
00162 
00163   assert(plock->nbr_active==0);
00164   assert(plock->nbw_active==0);
00165 
00166   /* I become active and no more waiting */
00167   plock->nbw_waiting--;
00168   plock->nbw_active++;
00169   assert(plock->nbw_waiting >= 0);
00170 
00171   print_lock("P_w.end", plock);
00172   V(plock->mutexProtect);
00173 
00174   return 0;
00175 }                               /* P_w */
00176 
00177 /*
00178  * Release the lock after writting 
00179  */
00180 int V_w(rw_lock_t * plock)
00181 {
00182   P(plock->mutexProtect);
00183 
00184   print_lock("V_w.1", plock);
00185 
00186   /* I was the active writter, I am not it any more */
00187   if(plock->nbw_active == 0)
00188     // TODO: Failing on virtual machines
00189     //dbg_backtrace();
00190     print_lock("V_w.1_1", plock);
00191   else
00192     plock->nbw_active--;
00193 
00194   if(plock->nbw_waiting > 0)
00195     {
00196 
00197       print_lock("V_w.4 redacteur libere un lecteur", plock);
00198 
00199       /* There are waiting writters, but no waiting readers, I let a writter go */
00200       pthread_cond_signal(&(plock->condWrite));
00201 
00202       print_lock("V_w.5", plock);
00203 
00204     }
00205   else if(plock->nbr_waiting > 0)
00206     {
00207       /* if readers are waiting, let them go */
00208       print_lock("V_w.2 redacteur libere les lecteurs", plock);
00209       pthread_cond_broadcast(&(plock->condRead));
00210 
00211       print_lock("V_w.3", plock);
00212 
00213     }
00214   print_lock("V_w.end", plock);
00215   V(plock->mutexProtect);
00216 
00217 
00218   return 0;
00219 }                               /* V_w */
00220 
00221 /* Roughly, downgrading a writer lock is making a V_w atomically followed by a P_r */
00222 int rw_lock_downgrade(rw_lock_t * plock)
00223 {
00224   P(plock->mutexProtect);
00225 
00226   print_lock("downgrade.1", plock);
00227 
00228   /* I was the active writter, I am not it any more */
00229   if(plock->nbw_active == 0)
00230     // TODO: Failing on virtual machines
00231     // dbg_backtrace();
00232     print_lock("downgrade.1_1", plock);
00233   else
00234     plock->nbw_active--;
00235 
00236   if(plock->nbr_waiting > 0)
00237     {
00238 
00239       /* there are waiting readers, I let all the readers go */
00240       print_lock("downgrade.2 libere les lecteurs", plock);
00241       pthread_cond_broadcast(&(plock->condRead));
00242 
00243     }
00244 
00245   /* nobody must break caller's read lock, so don't consider or unlock writers */
00246 
00247   /* caller is also a reader, now */
00248   plock->nbr_active++;
00249 
00250   print_lock("downgrade.end", plock);
00251   V(plock->mutexProtect);
00252 
00253 
00254   return 0;
00255 
00256 }                               /* rw_lock_downgrade */
00257 
00258 /*
00259  * Routine for initializing a lock
00260  */
00261 int rw_lock_init(rw_lock_t * plock)
00262 {
00263   int rc = 0;
00264   pthread_mutexattr_t mutex_attr;
00265   pthread_condattr_t cond_attr;
00266 
00267   if((rc = pthread_mutexattr_init(&mutex_attr)) != 0)
00268     return 1;
00269   if((rc = pthread_condattr_init(&cond_attr)) != 0)
00270     return 1;
00271 
00272   if((rc = pthread_mutex_init(&(plock->mutexProtect), &mutex_attr)) != 0)
00273     return 1;
00274 
00275   if((rc = pthread_cond_init(&(plock->condRead), &cond_attr)) != 0)
00276     return 1;
00277   if((rc = pthread_cond_init(&(plock->condWrite), &cond_attr)) != 0)
00278     return 1;
00279 
00280   plock->nbr_waiting = 0;
00281   plock->nbr_active = 0;
00282 
00283   plock->nbw_waiting = 0;
00284   plock->nbw_active = 0;
00285 
00286   return 0;
00287 }                               /* rw_lock_init */
00288 
00289 /*
00290  * Routine for destroying a lock
00291  */
00292 int rw_lock_destroy(rw_lock_t * plock)
00293 {
00294   int rc = 0;
00295 
00296   if((rc = pthread_mutex_destroy(&(plock->mutexProtect))) != 0)
00297     return 1;
00298 
00299   if((rc = pthread_cond_destroy(&(plock->condWrite))) != 0)
00300     return 1;
00301   if((rc = pthread_cond_destroy(&(plock->condRead))) != 0)
00302     return 1;
00303 
00304   memset(plock, 0, sizeof(rw_lock_t));
00305 
00306   return 0;
00307 }                               /* rw_lock_init */