1//------------------------------------------------------------------------------
2// Copyright (c) 2016 by Lukasz Janyst <lukasz@jany.st>
3//------------------------------------------------------------------------------
4// This file is part of thread-bites.
5//
6// thread-bites is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// thread-bites is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with thread-bites. If not, see <http://www.gnu.org/licenses/>.
18//------------------------------------------------------------------------------
19
20#include "tb.h"
21#include "tb-private.h"
22
23#include <limits.h>
24#include <linux/futex.h>
25
26//------------------------------------------------------------------------------
27// Lock for reading
28//------------------------------------------------------------------------------
29int tbthread_rwlock_rdlock(tbthread_rwlock_t *rwlock)
30{
31 while(1) {
32 tb_futex_lock(&rwlock->lock);
33
34 if(!rwlock->writer && !rwlock->writers_queued) {
35 ++rwlock->readers;
36 tb_futex_unlock(&rwlock->lock);
37 return 0;
38 }
39 int sleep_status = rwlock->rd_futex;
40
41 tb_futex_unlock(&rwlock->lock);
42
43 SYSCALL3(__NR_futex, &rwlock->rd_futex, FUTEX_WAIT, sleep_status);
44 }
45}
46
47//------------------------------------------------------------------------------
48// Lock for writing
49//------------------------------------------------------------------------------
50int tbthread_rwlock_wrlock(tbthread_rwlock_t *rwlock)
51{
52 int queued = 0;
53 while(1) {
54 tb_futex_lock(&rwlock->lock);
55
56 if(!queued) {
57 queued = 1;
58 ++rwlock->writers_queued;
59 }
60
61 if(!rwlock->writer && !rwlock->readers) {
62 rwlock->writer = tbthread_self();
63 --rwlock->writers_queued;
64 tb_futex_unlock(&rwlock->lock);
65 return 0;
66 }
67 int sleep_status = rwlock->wr_futex;
68
69 tb_futex_unlock(&rwlock->lock);
70
71 SYSCALL3(__NR_futex, &rwlock->wr_futex, FUTEX_WAIT, sleep_status);
72 }
73}
74
75//------------------------------------------------------------------------------
76// Unlock
77//------------------------------------------------------------------------------
78int tbthread_rwlock_unlock(tbthread_rwlock_t *rwlock)
79{
80 tb_futex_lock(&rwlock->lock);
81 if(rwlock->writer) {
82 rwlock->writer = 0;
83 if(rwlock->writers_queued) {
84 __sync_fetch_and_add(&rwlock->wr_futex, 1);
85 SYSCALL3(__NR_futex, &rwlock->wr_futex, FUTEX_WAKE, 1);
86 } else {
87 __sync_fetch_and_add(&rwlock->rd_futex, 1);
88 SYSCALL3(__NR_futex, &rwlock->rd_futex, FUTEX_WAKE, INT_MAX);
89 }
90 goto exit;
91 }
92
93 --rwlock->readers;
94 if(!rwlock->readers && rwlock->writers_queued) {
95 __sync_fetch_and_add(&rwlock->wr_futex, 1);
96 SYSCALL3(__NR_futex, &rwlock->wr_futex, FUTEX_WAKE, 1);
97 }
98
99exit:
100 tb_futex_unlock(&rwlock->lock);
101 return 0;
102}
103
104//------------------------------------------------------------------------------
105// Try to lock for reading
106//------------------------------------------------------------------------------
107int tbthread_rwlock_tryrdlock(tbthread_rwlock_t *rwlock)
108{
109 tb_futex_lock(&rwlock->lock);
110 int status = -EBUSY;
111 if(!rwlock->writer && !rwlock->writers_queued) {
112 ++rwlock->readers;
113 status = 0;
114 goto exit;
115 }
116exit:
117 tb_futex_unlock(&rwlock->lock);
118 return status;
119}
120
121//------------------------------------------------------------------------------
122// Try to lock for writing
123//------------------------------------------------------------------------------
124int tbthread_rwlock_trywrlock(tbthread_rwlock_t *rwlock)
125{
126 tb_futex_lock(&rwlock->lock);
127 int status = -EBUSY;
128 if(!rwlock->writer && !rwlock->readers) {
129 rwlock->writer = tbthread_self();
130 status = 0;
131 goto exit;
132 }
133exit:
134 tb_futex_unlock(&rwlock->lock);
135 return status;
136}
137