X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Frwlock.c;h=b1ce1e2e9827e0a2f2853d33382e1a5bca8c7d62;hb=b5db61d8cb9075592caf167e8b23fe5f6be125e8;hp=f6912b519de5eac9632687353607cabc47dd1706;hpb=cbd692e657c8da407cd8f3e2f1fc87b0f79eef76;p=bacula%2Fbacula diff --git a/bacula/src/lib/rwlock.c b/bacula/src/lib/rwlock.c index f6912b519d..b1ce1e2e98 100644 --- a/bacula/src/lib/rwlock.c +++ b/bacula/src/lib/rwlock.c @@ -1,3 +1,30 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2001-2010 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ /* * Bacula Thread Read/Write locking code. It permits * multiple readers but only one writer. Note, however, @@ -12,40 +39,23 @@ * David R. Butenhof * */ -/* - Copyright (C) 2000-2003 Kern Sibbald and John Walker - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. - - */ +#define _LOCKMGR_COMPLIANT #include "bacula.h" -/* +/* * Initialize a read/write lock * * Returns: 0 on success - * errno on failure + * errno on failure */ -int rwl_init(brwlock_t *rwl) +int rwl_init(brwlock_t *rwl, int priority) { int stat; - + rwl->r_active = rwl->w_active = 0; rwl->r_wait = rwl->w_wait = 0; + rwl->priority = priority; if ((stat = pthread_mutex_init(&rwl->mutex, NULL)) != 0) { return stat; } @@ -66,7 +76,7 @@ int rwl_init(brwlock_t *rwl) * Destroy a read/write lock * * Returns: 0 on success - * errno on failure + * errno on failure */ int rwl_destroy(brwlock_t *rwl) { @@ -79,7 +89,7 @@ int rwl_destroy(brwlock_t *rwl) return stat; } - /* + /* * If any threads are active, report EBUSY */ if (rwl->r_active > 0 || rwl->w_active) { @@ -90,7 +100,7 @@ int rwl_destroy(brwlock_t *rwl) /* * If any threads are waiting, report EBUSY */ - if (rwl->r_wait > 0 || rwl->w_wait > 0) { + if (rwl->r_wait > 0 || rwl->w_wait > 0) { pthread_mutex_unlock(&rwl->mutex); return EBUSY; } @@ -99,7 +109,7 @@ int rwl_destroy(brwlock_t *rwl) if ((stat = pthread_mutex_unlock(&rwl->mutex)) != 0) { return stat; } - stat = pthread_mutex_destroy(&rwl->mutex); + stat = pthread_mutex_destroy(&rwl->mutex); stat1 = pthread_cond_destroy(&rwl->read); stat2 = pthread_cond_destroy(&rwl->write); return (stat != 0 ? stat : (stat1 != 0 ? stat1 : stat2)); @@ -135,7 +145,7 @@ static void rwl_write_release(void *arg) int rwl_readlock(brwlock_t *rwl) { int stat; - + if (rwl->valid != RWLOCK_VALID) { return EINVAL; } @@ -143,31 +153,31 @@ int rwl_readlock(brwlock_t *rwl) return stat; } if (rwl->w_active) { - rwl->r_wait++; /* indicate that we are waiting */ + rwl->r_wait++; /* indicate that we are waiting */ pthread_cleanup_push(rwl_read_release, (void *)rwl); while (rwl->w_active) { - stat = pthread_cond_wait(&rwl->read, &rwl->mutex); - if (stat != 0) { - break; /* error, bail out */ - } + stat = pthread_cond_wait(&rwl->read, &rwl->mutex); + if (stat != 0) { + break; /* error, bail out */ + } } pthread_cleanup_pop(0); - rwl->r_wait--; /* we are no longer waiting */ + rwl->r_wait--; /* we are no longer waiting */ } if (stat == 0) { - rwl->r_active++; /* we are running */ + rwl->r_active++; /* we are running */ } pthread_mutex_unlock(&rwl->mutex); return stat; } -/* +/* * Attempt to lock for read access, don't wait */ int rwl_readtrylock(brwlock_t *rwl) { int stat, stat2; - + if (rwl->valid != RWLOCK_VALID) { return EINVAL; } @@ -177,19 +187,19 @@ int rwl_readtrylock(brwlock_t *rwl) if (rwl->w_active) { stat = EBUSY; } else { - rwl->r_active++; /* we are running */ + rwl->r_active++; /* we are running */ } stat2 = pthread_mutex_unlock(&rwl->mutex); return (stat == 0 ? stat2 : stat); } - -/* + +/* * Unlock read lock */ int rwl_readunlock(brwlock_t *rwl) { int stat, stat2; - + if (rwl->valid != RWLOCK_VALID) { return EINVAL; } @@ -198,7 +208,7 @@ int rwl_readunlock(brwlock_t *rwl) } rwl->r_active--; if (rwl->r_active == 0 && rwl->w_wait > 0) { /* if writers waiting */ - stat = pthread_cond_signal(&rwl->write); + stat = pthread_cond_broadcast(&rwl->write); } stat2 = pthread_mutex_unlock(&rwl->mutex); return (stat == 0 ? stat2 : stat); @@ -209,10 +219,10 @@ int rwl_readunlock(brwlock_t *rwl) * Lock for write access, wait until locked (or error). * Multiple nested write locking is permitted. */ -int rwl_writelock(brwlock_t *rwl) +int rwl_writelock_p(brwlock_t *rwl, const char *file, int line) { int stat; - + if (rwl->valid != RWLOCK_VALID) { return EINVAL; } @@ -224,32 +234,35 @@ int rwl_writelock(brwlock_t *rwl) pthread_mutex_unlock(&rwl->mutex); return 0; } + lmgr_pre_lock(rwl, rwl->priority, file, line); if (rwl->w_active || rwl->r_active > 0) { - rwl->w_wait++; /* indicate that we are waiting */ + rwl->w_wait++; /* indicate that we are waiting */ pthread_cleanup_push(rwl_write_release, (void *)rwl); while (rwl->w_active || rwl->r_active > 0) { - if ((stat = pthread_cond_wait(&rwl->write, &rwl->mutex)) != 0) { - break; /* error, bail out */ - } + if ((stat = pthread_cond_wait(&rwl->write, &rwl->mutex)) != 0) { + lmgr_do_unlock(rwl); + break; /* error, bail out */ + } } pthread_cleanup_pop(0); - rwl->w_wait--; /* we are no longer waiting */ + rwl->w_wait--; /* we are no longer waiting */ } if (stat == 0) { - rwl->w_active++; /* we are running */ + rwl->w_active++; /* we are running */ rwl->writer_id = pthread_self(); /* save writer thread's id */ - } + lmgr_post_lock(); + } pthread_mutex_unlock(&rwl->mutex); return stat; } -/* +/* * Attempt to lock for write access, don't wait */ int rwl_writetrylock(brwlock_t *rwl) { int stat, stat2; - + if (rwl->valid != RWLOCK_VALID) { return EINVAL; } @@ -264,21 +277,22 @@ int rwl_writetrylock(brwlock_t *rwl) if (rwl->w_active || rwl->r_active > 0) { stat = EBUSY; } else { - rwl->w_active = 1; /* we are running */ + rwl->w_active = 1; /* we are running */ rwl->writer_id = pthread_self(); /* save writer thread's id */ + lmgr_do_lock(rwl, rwl->priority, __FILE__, __LINE__); } stat2 = pthread_mutex_unlock(&rwl->mutex); return (stat == 0 ? stat2 : stat); } - -/* + +/* * Unlock write lock * Start any waiting writers in preference to waiting readers */ int rwl_writeunlock(brwlock_t *rwl) { int stat, stat2; - + if (rwl->valid != RWLOCK_VALID) { return EINVAL; } @@ -286,20 +300,23 @@ int rwl_writeunlock(brwlock_t *rwl) return stat; } if (rwl->w_active <= 0) { - Emsg0(M_ABORT, 0, "rwl_writeunlock called too many times.\n"); + pthread_mutex_unlock(&rwl->mutex); + Jmsg0(NULL, M_ABORT, 0, _("rwl_writeunlock called too many times.\n")); } rwl->w_active--; if (!pthread_equal(pthread_self(), rwl->writer_id)) { - Emsg0(M_ABORT, 0, "rwl_writeunlock by non-owner.\n"); + pthread_mutex_unlock(&rwl->mutex); + Jmsg0(NULL, M_ABORT, 0, _("rwl_writeunlock by non-owner.\n")); } if (rwl->w_active > 0) { - stat = 0; /* writers still active */ + stat = 0; /* writers still active */ } else { + lmgr_do_unlock(rwl); /* No more writers, awaken someone */ - if (rwl->r_wait > 0) { /* if readers waiting */ - stat = pthread_cond_broadcast(&rwl->read); + if (rwl->r_wait > 0) { /* if readers waiting */ + stat = pthread_cond_broadcast(&rwl->read); } else if (rwl->w_wait > 0) { - stat = pthread_cond_signal(&rwl->write); + stat = pthread_cond_broadcast(&rwl->write); } } stat2 = pthread_mutex_unlock(&rwl->mutex); @@ -308,9 +325,9 @@ int rwl_writeunlock(brwlock_t *rwl) #ifdef TEST_RWLOCK -#define THREADS 5 +#define THREADS 300 #define DATASIZE 15 -#define ITERATIONS 10000 +#define ITERATIONS 1000000 /* * Keep statics for each thread. @@ -323,7 +340,7 @@ typedef struct thread_tag { int interval; } thread_t; -/* +/* * Read/write lock and shared data. */ typedef struct data_tag { @@ -332,10 +349,10 @@ typedef struct data_tag { int writes; } data_t; -thread_t threads[THREADS]; -data_t data[DATASIZE]; +static thread_t threads[THREADS]; +static data_t data[DATASIZE]; -/* +/* * Thread start routine that uses read/write locks. */ void *thread_routine(void *arg) @@ -352,44 +369,70 @@ void *thread_routine(void *arg) * update operation (write lock instead of read * lock). */ - if ((iteration % self->interval) == 0) { - status = rwl_writelock(&data[element].lock); - if (status != 0) { - Emsg1(M_ABORT, 0, "Write lock failed. ERR=%s\n", strerror(status)); - } - data[element].data = self->thread_num; - data[element].writes++; - self->writes++; - status = rwl_writeunlock(&data[element].lock); - if (status != 0) { - Emsg1(M_ABORT, 0, "Write unlock failed. ERR=%s\n", strerror(status)); - } +// if ((iteration % self->interval) == 0) { + status = rwl_writelock(&data[element].lock); + if (status != 0) { + berrno be; + printf("Write lock failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } + data[element].data = self->thread_num; + data[element].writes++; + self->writes++; + status = rwl_writelock(&data[element].lock); + if (status != 0) { + berrno be; + printf("Write lock failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } + data[element].data = self->thread_num; + data[element].writes++; + self->writes++; + status = rwl_writeunlock(&data[element].lock); + if (status != 0) { + berrno be; + printf("Write unlock failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } + status = rwl_writeunlock(&data[element].lock); + if (status != 0) { + berrno be; + printf("Write unlock failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } + +#ifdef xxx } else { - /* - * Look at the current data element to see whether - * the current thread last updated it. Count the - * times to report later. - */ - status = rwl_readlock(&data[element].lock); - if (status != 0) { - Emsg1(M_ABORT, 0, "Read lock failed. ERR=%s\n", strerror(status)); - } - self->reads++; - if (data[element].data == self->thread_num) - repeats++; - status = rwl_readunlock(&data[element].lock); - if (status != 0) { - Emsg1(M_ABORT, 0, "Read unlock failed. ERR=%s\n", strerror(status)); - } + /* + * Look at the current data element to see whether + * the current thread last updated it. Count the + * times to report later. + */ + status = rwl_readlock(&data[element].lock); + if (status != 0) { + berrno be; + printf("Read lock failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } + self->reads++; + if (data[element].data == self->thread_num) + repeats++; + status = rwl_readunlock(&data[element].lock); + if (status != 0) { + berrno be; + printf("Read unlock failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } } +#endif element++; if (element >= DATASIZE) { - element = 0; + element = 0; } } if (repeats > 0) { - Pmsg2(000, "Thread %d found unchanged elements %d times\n", - self->thread_num, repeats); + Pmsg2(000, _("Thread %d found unchanged elements %d times\n"), + self->thread_num, repeats); } return NULL; } @@ -416,27 +459,34 @@ int main (int argc, char *argv[]) * Initialize the shared data. */ for (data_count = 0; data_count < DATASIZE; data_count++) { - data[data_count].data = 0; - data[data_count].writes = 0; - status = rwl_init (&data[data_count].lock); - if (status != 0) { - Emsg1(M_ABORT, 0, "Init rwlock failed. ERR=%s\n", strerror(status)); - } + data[data_count].data = 0; + data[data_count].writes = 0; + status = rwl_init(&data[data_count].lock); + if (status != 0) { + berrno be; + printf("Init rwlock failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } } /* * Create THREADS threads to access shared data. */ for (count = 0; count < THREADS; count++) { - threads[count].thread_num = count + 1; - threads[count].writes = 0; - threads[count].reads = 0; - threads[count].interval = rand_r (&seed) % 71; - status = pthread_create (&threads[count].thread_id, - NULL, thread_routine, (void*)&threads[count]); - if (status != 0) { - Emsg1(M_ABORT, 0, "Create thread failed. ERR=%s\n", strerror(status)); - } + threads[count].thread_num = count + 1; + threads[count].writes = 0; + threads[count].reads = 0; + threads[count].interval = rand_r(&seed) % 71; + if (threads[count].interval <= 0) { + threads[count].interval = 1; + } + status = pthread_create (&threads[count].thread_id, + NULL, thread_routine, (void*)&threads[count]); + if (status != 0 || (int)threads[count].thread_id == 0) { + berrno be; + printf("Create thread failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } } /* @@ -444,28 +494,30 @@ int main (int argc, char *argv[]) * statistics. */ for (count = 0; count < THREADS; count++) { - status = pthread_join (threads[count].thread_id, NULL); - if (status != 0) { - Emsg1(M_ABORT, 0, "Join thread failed. ERR=%s\n", strerror(status)); - } - thread_writes += threads[count].writes; - printf ("%02d: interval %d, writes %d, reads %d\n", - count, threads[count].interval, - threads[count].writes, threads[count].reads); + status = pthread_join (threads[count].thread_id, NULL); + if (status != 0) { + berrno be; + printf("Join thread failed. ERR=%s\n", be.bstrerror(status)); + exit(1); + } + thread_writes += threads[count].writes; + printf (_("%02d: interval %d, writes %d, reads %d\n"), + count, threads[count].interval, + threads[count].writes, threads[count].reads); } /* * Collect statistics for the data. */ for (data_count = 0; data_count < DATASIZE; data_count++) { - data_writes += data[data_count].writes; - printf ("data %02d: value %d, %d writes\n", - data_count, data[data_count].data, data[data_count].writes); - rwl_destroy (&data[data_count].lock); + data_writes += data[data_count].writes; + printf (_("data %02d: value %d, %d writes\n"), + data_count, data[data_count].data, data[data_count].writes); + rwl_destroy (&data[data_count].lock); } - printf ("Total: %d thread writes, %d data writes\n", - thread_writes, data_writes); + printf (_("Total: %d thread writes, %d data writes\n"), + thread_writes, data_writes); return 0; } @@ -485,29 +537,29 @@ int main (int argc, char *argv[]) #include "rwlock.h" #include "errors.h" -#define THREADS 5 -#define ITERATIONS 1000 -#define DATASIZE 15 +#define THREADS 5 +#define ITERATIONS 1000 +#define DATASIZE 15 /* * Keep statistics for each thread. */ typedef struct thread_tag { - int thread_num; - pthread_t thread_id; - int r_collisions; - int w_collisions; - int updates; - int interval; + int thread_num; + pthread_t thread_id; + int r_collisions; + int w_collisions; + int updates; + int interval; } thread_t; /* * Read-write lock and shared data */ typedef struct data_tag { - brwlock_t lock; - int data; - int updates; + brwlock_t lock; + int data; + int updates; } data_t; thread_t threads[THREADS]; @@ -522,40 +574,41 @@ void *thread_routine (void *arg) int iteration; int element; int status; - - element = 0; /* Current data element */ + lmgr_init_thread(); + element = 0; /* Current data element */ for (iteration = 0; iteration < ITERATIONS; iteration++) { - if ((iteration % self->interval) == 0) { - status = rwl_writetrylock (&data[element].lock); - if (status == EBUSY) - self->w_collisions++; - else if (status == 0) { - data[element].data++; - data[element].updates++; - self->updates++; - rwl_writeunlock (&data[element].lock); - } else - err_abort (status, "Try write lock"); - } else { - status = rwl_readtrylock (&data[element].lock); - if (status == EBUSY) - self->r_collisions++; - else if (status != 0) { - err_abort (status, "Try read lock"); - } else { - if (data[element].data != data[element].updates) + if ((iteration % self->interval) == 0) { + status = rwl_writetrylock (&data[element].lock); + if (status == EBUSY) + self->w_collisions++; + else if (status == 0) { + data[element].data++; + data[element].updates++; + self->updates++; + rwl_writeunlock (&data[element].lock); + } else + err_abort (status, _("Try write lock")); + } else { + status = rwl_readtrylock (&data[element].lock); + if (status == EBUSY) + self->r_collisions++; + else if (status != 0) { + err_abort (status, _("Try read lock")); + } else { + if (data[element].data != data[element].updates) printf ("%d: data[%d] %d != %d\n", - self->thread_num, element, - data[element].data, data[element].updates); - rwl_readunlock (&data[element].lock); - } - } - - element++; - if (element >= DATASIZE) - element = 0; + self->thread_num, element, + data[element].data, data[element].updates); + rwl_readunlock (&data[element].lock); + } + } + + element++; + if (element >= DATASIZE) + element = 0; } + lmgr_cleanup_thread(); return NULL; } @@ -580,24 +633,24 @@ int main (int argc, char *argv[]) * Initialize the shared data. */ for (data_count = 0; data_count < DATASIZE; data_count++) { - data[data_count].data = 0; - data[data_count].updates = 0; - rwl_init (&data[data_count].lock); + data[data_count].data = 0; + data[data_count].updates = 0; + rwl_init(&data[data_count].lock); } /* * Create THREADS threads to access shared data. */ for (count = 0; count < THREADS; count++) { - threads[count].thread_num = count; - threads[count].r_collisions = 0; - threads[count].w_collisions = 0; - threads[count].updates = 0; - threads[count].interval = rand_r (&seed) % ITERATIONS; - status = pthread_create (&threads[count].thread_id, - NULL, thread_routine, (void*)&threads[count]); - if (status != 0) - err_abort (status, "Create thread"); + threads[count].thread_num = count; + threads[count].r_collisions = 0; + threads[count].w_collisions = 0; + threads[count].updates = 0; + threads[count].interval = rand_r (&seed) % ITERATIONS; + status = pthread_create (&threads[count].thread_id, + NULL, thread_routine, (void*)&threads[count]); + if (status != 0) + err_abort (status, _("Create thread")); } /* @@ -605,25 +658,25 @@ int main (int argc, char *argv[]) * statistics. */ for (count = 0; count < THREADS; count++) { - status = pthread_join (threads[count].thread_id, NULL); - if (status != 0) - err_abort (status, "Join thread"); - thread_updates += threads[count].updates; - printf ("%02d: interval %d, updates %d, " - "r_collisions %d, w_collisions %d\n", - count, threads[count].interval, - threads[count].updates, - threads[count].r_collisions, threads[count].w_collisions); + status = pthread_join (threads[count].thread_id, NULL); + if (status != 0) + err_abort (status, _("Join thread")); + thread_updates += threads[count].updates; + printf (_("%02d: interval %d, updates %d, " + "r_collisions %d, w_collisions %d\n"), + count, threads[count].interval, + threads[count].updates, + threads[count].r_collisions, threads[count].w_collisions); } /* * Collect statistics for the data. */ for (data_count = 0; data_count < DATASIZE; data_count++) { - data_updates += data[data_count].updates; - printf ("data %02d: value %d, %d updates\n", - data_count, data[data_count].data, data[data_count].updates); - rwl_destroy (&data[data_count].lock); + data_updates += data[data_count].updates; + printf (_("data %02d: value %d, %d updates\n"), + data_count, data[data_count].data, data[data_count].updates); + rwl_destroy (&data[data_count].lock); } return 0;