summary refs log tree commit diff
path: root/nptl/DESIGN-rwlock.txt
blob: 6262a7a5b962de15e8aff605efcb2239406dcce5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
Reader Writer Locks pseudocode
==============================

	pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
	pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
	pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

struct pthread_rwlock_t {

   unsigned int lock:
         - internal mutex

   unsigned int writers_preferred;
         - locking mode: 0 recursive, readers preferred
                         1 nonrecursive, writers preferred

   unsigned int readers;
         - number of read-only references various threads have

   pthread_t writer;
         - descriptor of the writer or 0

   unsigned int readers_wakeup;
         - 'all readers should wake up' futex.

   unsigned int writer_wakeup;
         - 'one writer should wake up' futex.

   unsigned int nr_readers_queued;
         - number of readers queued up.

   unsigned int nr_writers_queued;
         - number of writers queued up.
}

pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
{
  lll_lock(rwlock->lock);
  for (;;) {
    if (!rwlock->writer && (!rwlock->nr_writers_queued ||
					!rwlock->writers_preferred))
        break;

    rwlock->nr_readers_queued++;
    lll_unlock(rwlock->lock);

    futex_wait(&rwlock->readers_wakeup, 0)

    lll_lock(rwlock->lock);
    if (!--rwlock->nr_readers_queued)
        rwlock->readers_wakeup = 0;
  }
  rwlock->readers++;
  lll_unlock(rwlock->lock);
}

pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
{
  int result = EBUSY;
  lll_lock(rwlock->lock);
  if (!rwlock->writer && (!rwlock->nr_writers_queued ||
					!rwlock->writers_preferred))
    rwlock->readers++;
  lll_unlock(rwlock->lock);
  return result;
}

pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
{
  lll_lock(rwlock->lock);
  for (;;) {
    if (!rwlock->writer && !rwlock->readers)
       break;

    rwlock->nr_writers_queued++;
    lll_unlock(rwlock->lock);

    futex_wait(&rwlock->writer_wakeup, 0);

    lll_lock(rwlock->lock);
    rwlock->nr_writers_queued--;
    rwlock->writer_wakeup = 0;
  }
  rwlock->writer = pthread_self();
  lll_unlock(rwlock->lock);
}

pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
{
  lll_lock(rwlock->lock);

  if (rwlock->writer)
    rwlock->writer = 0;
  else
    rwlock->readers--;

  if (!rwlock->readers) {
    if (rwlock->nr_writers_queued) {
      rwlock->writer_wakeup = 1;
      futex_wake(&rwlock->writer_wakeup, 1);
    } else
      if (rwlock->nr_readers_queued) {
        rwlock->readers_wakeup = 1;
        futex_wake(&rwlock->readers_wakeup, MAX_INT);
      }
  }

  lll_unlock(rwlock->lock);
}