aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--default.bld2
-rw-r--r--src/stable/randomcmwc.h1
-rw-r--r--src/tests/readwritemutex.cpp106
-rw-r--r--src/unstable/readwritemutex.cpp78
-rw-r--r--src/unstable/readwritemutex.h75
5 files changed, 261 insertions, 1 deletions
diff --git a/default.bld b/default.bld
index 0985159..9babbff 100644
--- a/default.bld
+++ b/default.bld
@@ -151,7 +151,7 @@ target "viewcsv"
151 LDFLAGS += "-lncurses"; 151 LDFLAGS += "-lncurses";
152} 152}
153 153
154target ["myriad", "myriadfs", "tests/myriad", "tests/myriadfs", "unit/myriad", "tests/bigmyriad", "tests/synchroqueue"] 154target ["myriad", "myriadfs", "tests/myriad", "tests/myriadfs", "unit/myriad", "tests/bigmyriad", "tests/synchroqueue", "tests/readwritemutex"]
155{ 155{
156 LDFLAGS += "-lpthread"; 156 LDFLAGS += "-lpthread";
157} 157}
diff --git a/src/stable/randomcmwc.h b/src/stable/randomcmwc.h
index 0508381..5e89404 100644
--- a/src/stable/randomcmwc.h
+++ b/src/stable/randomcmwc.h
@@ -26,6 +26,7 @@ namespace Bu
26 virtual void seed( int32_t iSeed ); 26 virtual void seed( int32_t iSeed );
27 27
28 virtual int32_t rand(); 28 virtual int32_t rand();
29 using RandomBase::rand;
29 30
30 private: 31 private:
31 uint32_t *q, c, i; 32 uint32_t *q, c, i;
diff --git a/src/tests/readwritemutex.cpp b/src/tests/readwritemutex.cpp
new file mode 100644
index 0000000..d00956d
--- /dev/null
+++ b/src/tests/readwritemutex.cpp
@@ -0,0 +1,106 @@
1#include <bu/readwritemutex.h>
2#include <bu/thread.h>
3#include <bu/randomcmwc.h>
4#include <bu/sio.h>
5
6using namespace Bu;
7
8ReadWriteMutex mRW;
9bool bRunning;
10
11class Writer : public Thread
12{
13public:
14 Writer( int iId ) :
15 iId( iId ),
16 rand( iId )
17 {
18 }
19
20 virtual ~Writer()
21 {
22 }
23
24protected:
25 virtual void run()
26 {
27 while( bRunning )
28 {
29 mRW.lockWrite();
30 println("Writer %1 locking.").arg( iId );
31 usleep( rand.rand(5,10)*100000 );
32 println("Writer %1 unlocking.").arg( iId );
33 mRW.unlockWrite();
34 usleep( rand.rand(5,10)*10000 );
35 }
36 }
37
38private:
39 int iId;
40 RandomCmwc rand;
41};
42
43class Reader : public Thread
44{
45public:
46 Reader( int iId ) :
47 iId( iId ),
48 rand( -iId )
49 {
50 }
51
52 virtual ~Reader()
53 {
54 }
55
56protected:
57 virtual void run()
58 {
59 while( bRunning )
60 {
61 mRW.lockRead();
62 println("Reader %1 locking.").arg( iId );
63 usleep( rand.rand(5,10)*100000 );
64 println("Reader %1 unlocking.").arg( iId );
65 mRW.unlockRead();
66 usleep( rand.rand(5,10)*10000 );
67 }
68 }
69
70private:
71 int iId;
72 RandomCmwc rand;
73};
74
75#define CNT 5
76
77int main()
78{
79 bRunning = true;
80
81 Thread **threads = new Thread*[CNT*2];
82 for( int j = 0; j < CNT; j++ )
83 {
84 threads[j] = new Reader( j+1 );
85 threads[j+CNT] = new Writer( j+1 );
86 }
87
88 println("Starting.");
89 for( int j = 0; j < CNT*2; j++ )
90 threads[j]->start();
91
92 sleep( 10 );
93 bRunning = false;
94
95 for( int j = 0; j < CNT*2; j++ )
96 {
97 threads[j]->join();
98 delete threads[j];
99 }
100
101 delete[] threads;
102
103 return 0;
104}
105
106
diff --git a/src/unstable/readwritemutex.cpp b/src/unstable/readwritemutex.cpp
new file mode 100644
index 0000000..b0a3b77
--- /dev/null
+++ b/src/unstable/readwritemutex.cpp
@@ -0,0 +1,78 @@
1#include "bu/readwritemutex.h"
2
3Bu::ReadWriteMutex::ReadWriteMutex() :
4 iCounter( 0 ),
5 bWantWrite( false )
6{
7}
8
9Bu::ReadWriteMutex::~ReadWriteMutex()
10{
11}
12
13void Bu::ReadWriteMutex::lockRead()
14{
15 // Check to see if someone wants to write
16 cWrite.lock();
17 if( bWantWrite )
18 {
19 // If so, wait patiently for them to finish
20 cWrite.wait();
21 }
22 cWrite.unlock();
23
24 // Now lock the read counter
25 mRead.lock();
26 iCounter++;
27 // If the lock counter is one, we just locked for the first time,
28 // so we lock the writer.
29 if( iCounter == 1 )
30 mWrite.lock();
31 mRead.unlock();
32}
33
34void Bu::ReadWriteMutex::unlockRead()
35{
36 // Lock the read counter
37 mRead.lock();
38 iCounter--;
39 // If we just decremented the counter back to zero then we can
40 // release the write lock
41 if( iCounter == 0 )
42 mWrite.unlock();
43 mRead.unlock();
44}
45
46void Bu::ReadWriteMutex::lockWrite()
47{
48 // Lock the read counter
49 mRead.lock();
50 if( iCounter > 0 )
51 {
52 // If there is an active read in progress then we set the bWantWrite
53 // flag to make sure no more readers start working.
54 cWrite.lock();
55 bWantWrite = true;
56 cWrite.unlock();
57 }
58 mRead.unlock();
59
60 // Lock the write lock
61 mWrite.lock();
62}
63
64void Bu::ReadWriteMutex::unlockWrite()
65{
66 // Just always set the bWantWrite flag to false at this point, as long
67 // as we're locked.
68 cWrite.lock();
69 bWantWrite = false;
70 cWrite.unlock();
71
72 // Release all waiting readers, they won't actually do much until we
73 // unlock the write lock though
74 cWrite.broadcast();
75
76 // Unlock the write lock
77 mWrite.unlock();
78}
diff --git a/src/unstable/readwritemutex.h b/src/unstable/readwritemutex.h
new file mode 100644
index 0000000..9e07047
--- /dev/null
+++ b/src/unstable/readwritemutex.h
@@ -0,0 +1,75 @@
1#ifndef BU_READ_WRITE_MUTEX_H
2#define BU_READ_WRITE_MUTEX_H
3
4#include "bu/mutex.h"
5#include "bu/condition.h"
6
7namespace Bu
8{
9 /**
10 * Mutex designed for situations where overlapped reading is safe, but
11 * overlapped writing isn't. There are many, many good examples of this
12 * including most data structures, streams, etc. etc.
13 *
14 * Use this just like a normal mutex except that you use the
15 * lockRead/unlockRead and lockWrite/unlockWrite functions depending on
16 * weather the section of code your locking is reading data or changing
17 * data.
18 *
19 * This particular mutex is designed so that while a read operation is
20 * happening other read operations can also happen, but no write operations
21 * can occur. While a write is happening, no other write or read operation
22 * can continue. There is an extra feature to ensure writes get a chance
23 * to complete, when a lockWrite is issued, all current read operations
24 * continue, but future read operations block until the write is complete.
25 */
26 class ReadWriteMutex
27 {
28 public:
29 ReadWriteMutex();
30 virtual ~ReadWriteMutex();
31
32 /**
33 * Lock the mutex for reading. Multiple code sections can hold a read
34 * lock at the same time, but write locks will wait for all read locks
35 * to be released before continuing. Read locks will also wait if
36 * there is an active write lock.
37 *
38 * It is very important to not make any changes to your data within
39 * a read lock.
40 */
41 void lockRead();
42
43 /**
44 * Release a read lock.
45 */
46 void unlockRead();
47
48 /**
49 * Lock the mutex for writing. Only one code section can have a write
50 * lock at any given time. No code sections can be in a locked read
51 * section while a write lock is held. When a write lock is requested
52 * all following read locks will block until the write operation is
53 * started, ensuring writes always get a chance to execute.
54 *
55 * Within a write locked code section feel free to change your data
56 * and read your data. It is imparative to spend as little time as
57 * possible in a write-locked section.
58 */
59 void lockWrite();
60
61 /**
62 * Release a write lock.
63 */
64 void unlockWrite();
65
66 private:
67 Bu::Mutex mRead;
68 int iCounter;
69 Bu::Mutex mWrite;
70 Bu::Condition cWrite;
71 bool bWantWrite;
72 };
73};
74
75#endif