1 // -*- Mode: C++; -*- 2 // Package : omniNames 3 // ReadersWritersLock.h Author : Tristan Richardson (tjr) 4 // 5 // Copyright (C) 1997-1999 AT&T Laboratories Cambridge 6 // 7 // This file is part of omniNames. 8 // 9 // omniNames is free software; you can redistribute it and/or modify 10 // it under the terms of the GNU General Public License as published by 11 // the Free Software Foundation; either version 2 of the License, or 12 // (at your option) any later version. 13 // 14 // This program is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with this program. If not, see http://www.gnu.org/licenses/ 21 // 22 23 // 24 // This class implements a multiple-readers, single-writer lock. It has 25 // a slight alteration from a standard such lock. Any thread which has already 26 // called writerIn() may call writerIn() or readerIn() again multiple times. 27 // 28 29 30 #ifndef _ReadersWritersLock_h_ 31 #define _ReadersWritersLock_h_ 32 33 #include <omnithread.h> 34 35 class ReadersWritersLock { 36 37 public: 38 39 omni_mutex m; 40 omni_condition c; 41 int n; // 0 means no-one active, > 0 means n readers, < 0 means writer 42 // (-n times). 43 int writerId; 44 ReadersWritersLock(void)45 ReadersWritersLock(void) : c(&m), n(0), writerId(0) {} 46 readerIn(void)47 void readerIn(void) { 48 m.lock(); 49 if ((n < 0) && (writerId == omni_thread::self()->id())) { 50 // this thread already has lock as writer, simply decrement n 51 n--; 52 m.unlock(); 53 return; 54 } 55 while (n < 0) 56 c.wait(); 57 n++; 58 m.unlock(); 59 } 60 readerOut(void)61 void readerOut(void) { 62 m.lock(); 63 if (n < 0) { 64 // this thread already had lock as writer, simply increment n 65 n++; 66 m.unlock(); 67 return; 68 } 69 n--; 70 if (n == 0) 71 c.signal(); 72 m.unlock(); 73 } 74 writerIn(void)75 void writerIn(void) { 76 m.lock(); 77 if ((n < 0) && (writerId == omni_thread::self()->id())) { 78 // this thread already has lock as writer, simply decrement n 79 n--; 80 m.unlock(); 81 return; 82 } 83 while (n != 0) 84 c.wait(); 85 n--; 86 writerId = omni_thread::self()->id(); 87 m.unlock(); 88 } 89 writerOut(void)90 void writerOut(void) { 91 m.lock(); 92 n++; 93 if (n == 0) 94 c.broadcast(); // might as well wake up all readers 95 m.unlock(); 96 } 97 98 }; 99 100 101 // 102 // As an alternative to: 103 // { 104 // lock.readerIn(); 105 // ..... 106 // lock.readerOut(); 107 // } 108 // 109 // you can use a single instance of the ReaderLock class: 110 // 111 // { 112 // ReaderLock r(lock); 113 // .... 114 // } 115 // 116 // This has the advantage that lock.readerOut() will be called automatically 117 // when an exception is thrown. 118 // 119 120 class ReaderLock { 121 ReadersWritersLock& rwl; 122 public: ReaderLock(ReadersWritersLock & l)123 ReaderLock(ReadersWritersLock& l) : rwl(l) { rwl.readerIn(); } ~ReaderLock(void)124 ~ReaderLock(void) { rwl.readerOut(); } 125 }; 126 127 128 // 129 // Similarly the WriterLock class can be used instead of lock.writerIn() and 130 // lock.writerOut(). 131 // 132 133 class WriterLock { 134 ReadersWritersLock& rwl; 135 public: WriterLock(ReadersWritersLock & l)136 WriterLock(ReadersWritersLock& l) : rwl(l) { rwl.writerIn(); } ~WriterLock(void)137 ~WriterLock(void) { rwl.writerOut(); } 138 }; 139 140 #endif 141