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