3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 2001 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <stdio.h>
30 #include <rpc/types.h>
31 #include <rpc/xdr.h>
32 #include "db_dictionary_c.h"
33 #include "nisdb_rw.h"
34 #include "nisdb_ldap.h"
35
36 /*
37 * Nesting-safe RW locking functions. Return 0 when successful, an
38 * error number from the E-series when not.
39 */
40
41 int
42 __nisdb_rwinit(__nisdb_rwlock_t *rw) {
43
44 int ret;
45
46 if (rw == 0) {
47 #ifdef NISDB_MT_DEBUG
48 abort();
90 find_reader(pthread_t id, __nisdb_rwlock_t *rw) {
91
92 __nisdb_rl_t *rr;
93
94 for (rr = &rw->reader; rr != 0; rr = rr->next) {
95 if (rr->id == INV_PTHREAD_ID) {
96 rr = 0;
97 break;
98 }
99 if (rr->id == id)
100 break;
101 }
102
103 return (rr);
104 }
105
106
107 int
108 __nisdb_rw_readlock_ok(__nisdb_rwlock_t *rw) {
109 int ret;
110 pthread_t myself = pthread_self();
111 __nisdb_rl_t *rr;
112
113 if (rw == 0)
114 return (EFAULT);
115
116 if (rw->destroyed != 0)
117 return (ESHUTDOWN);
118
119 if ((ret = mutex_lock(&rw->mutex)) != 0)
120 return (ret);
121
122 /*
123 * Only allow changing 'force_write' when it's really safe; i.e.,
124 * the lock hasn't been destroyed, and there are no readers.
125 */
126 if (rw->destroyed == 0 && rw->reader_count == 0) {
127 rw->force_write = 0;
128 ret = 0;
129 } else {
130 ret = EBUSY;
131 }
132
133 (void) mutex_unlock(&rw->mutex);
134
135 return (ret);
136 }
137
138
139 int
140 __nisdb_rw_force_writelock(__nisdb_rwlock_t *rw) {
141 int ret;
142 pthread_t myself = pthread_self();
143 __nisdb_rl_t *rr;
144
145 if (rw == 0 || rw->destroyed != 0)
146 return (ESHUTDOWN);
147
148 if ((ret = mutex_lock(&rw->mutex)) != 0)
149 return (ret);
150
151 /*
152 * Only allow changing 'force_write' when it's really safe; i.e.,
153 * the lock hasn't been destroyed, and there are no readers.
154 */
155 if (rw->destroyed == 0 && rw->reader_count == 0) {
156 rw->force_write = 1;
157 ret = 0;
158 } else {
159 ret = EBUSY;
160 }
161
162 (void) mutex_unlock(&rw->mutex);
163
189
190 if (rw->destroyed != 0) {
191 (void) mutex_unlock(&rw->mutex);
192 return (ESHUTDOWN);
193 }
194
195 /* Simplest (and probably most common) case: no readers or writers */
196 if (rw->reader_count == 0 && rw->writer_count == 0) {
197 rw->writer_count = 1;
198 rw->writer.id = myself;
199 rw->writer.count = 1;
200 return (mutex_unlock(&rw->mutex));
201 }
202
203 /*
204 * Need to know if we're holding a read lock already, and if
205 * all other readers are blocked waiting for the mutex.
206 */
207 if (rw->reader_count > 0) {
208 if ((rr = find_reader(myself, rw)) != 0) {
209 if (rr->count)
210 /*
211 * We're already holding a read lock, so
212 * if the number of readers equals the number
213 * of blocked readers plus one, all other
214 * readers are blocked.
215 */
216 if (rw->reader_count ==
217 (rw->reader_blocked + 1))
218 all_readers_blocked = 1;
219 else
220 /*
221 * We're not holding a read lock, so the
222 * number of readers should equal the number
223 * of blocked readers if all readers are
224 * blocked.
225 */
226 if (rw->reader_count == rw->reader_blocked)
227 all_readers_blocked = 1;
228 }
229 }
230
231 /* Wait for reader(s) or writer to finish */
232 while (1) {
233 /*
234 * We can stop looping if one of the following holds:
235 * - No readers, no writers
236 * - No writers (or writer is myself), and one of:
237 * - No readers
238 * - One reader, and it's us
239 * - N readers, but all blocked on the mutex
240 */
241 if (
242 (rw->writer_count == 0 && rw->reader_count == 0) ||
243 ((rw->writer_count == 0 || rw->writer.id == myself) &&
244 (rw->reader_count == 0) ||
245 (rw->reader_count == 1 &&
246 rw->reader.id == myself))) {
247 break;
248 }
249 /*
250 * Provided that all readers are blocked on the mutex
251 * we break a potential dead-lock by acquiring the
252 * write lock.
253 */
254 if (all_readers_blocked) {
255 if (rw->writer_count == 0 || rw->writer.id == myself) {
256 break;
257 }
258 }
259
260 /*
261 * If 'trylock' is set, tell the caller that we'd have to
262 * block to obtain the lock.
263 */
264 if (trylock) {
631
632 rr = &rw->reader;
633 do {
634 if (rr->id == myself) {
635 (void) mutex_unlock(&rw->mutex);
636 return (0);
637 }
638 rr = rr->next;
639 } while (rr != 0);
640
641 ret = mutex_unlock(&rw->mutex);
642 return ((ret == 0) ? EBUSY : ret);
643 }
644
645
646 int
647 __nisdb_destroy_lock(__nisdb_rwlock_t *rw) {
648
649 int ret;
650 pthread_t myself = pthread_self();
651 __nisdb_rl_t *rr;
652
653
654 if (rw == 0) {
655 #ifdef NISDB_MT_DEBUG
656 abort();
657 #endif /* NISDB_MT_DEBUG */
658 return (EFAULT);
659 }
660
661 if (rw->destroyed != 0)
662 return (ESHUTDOWN);
663
664 if ((ret = mutex_lock(&rw->mutex)) != 0)
665 return (ret);
666
667 if (rw->destroyed != 0) {
668 (void) mutex_unlock(&rw->mutex);
669 return (ESHUTDOWN);
670 }
671
672 /*
673 * Only proceed if if there are neither readers nor writers
674 * other than this thread. Also, no nested locks may be in
675 * effect.
676 */
677 if ((rw->writer_count > 0 &&
678 (rw->writer.id != myself || rw->writer.count != 1) ||
679 (rw->reader_count > 0 &&
680 !(rw->reader_count == 1 && rw->reader.id == myself &&
681 rw->reader.count == 1))) ||
682 (rw->writer_count > 0 && rw->reader_count > 0)) {
683 #ifdef NISDB_MT_DEBUG
684 abort();
685 #endif /* NISDB_MT_DEBUG */
686 (void) mutex_unlock(&rw->mutex);
687 return (ENOLCK);
688 }
689
690 /*
691 * Mark lock destroyed, so that any thread waiting on the mutex
692 * will know what's what. Of course, this is a bit iffy, since
693 * we're probably being called from a destructor, and the structure
694 * where we live will soon cease to exist (i.e., be freed and
695 * perhaps re-used). Still, we can only do our best, and give
696 * those other threads the best chance possible.
697 */
698 rw->destroyed++;
|
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2015 Gary Mills
24 * Copyright (c) 2001 by Sun Microsystems, Inc.
25 * All rights reserved.
26 */
27
28 #include <stdio.h>
29 #include <rpc/types.h>
30 #include <rpc/xdr.h>
31 #include "db_dictionary_c.h"
32 #include "nisdb_rw.h"
33 #include "nisdb_ldap.h"
34
35 /*
36 * Nesting-safe RW locking functions. Return 0 when successful, an
37 * error number from the E-series when not.
38 */
39
40 int
41 __nisdb_rwinit(__nisdb_rwlock_t *rw) {
42
43 int ret;
44
45 if (rw == 0) {
46 #ifdef NISDB_MT_DEBUG
47 abort();
89 find_reader(pthread_t id, __nisdb_rwlock_t *rw) {
90
91 __nisdb_rl_t *rr;
92
93 for (rr = &rw->reader; rr != 0; rr = rr->next) {
94 if (rr->id == INV_PTHREAD_ID) {
95 rr = 0;
96 break;
97 }
98 if (rr->id == id)
99 break;
100 }
101
102 return (rr);
103 }
104
105
106 int
107 __nisdb_rw_readlock_ok(__nisdb_rwlock_t *rw) {
108 int ret;
109
110 if (rw == 0)
111 return (EFAULT);
112
113 if (rw->destroyed != 0)
114 return (ESHUTDOWN);
115
116 if ((ret = mutex_lock(&rw->mutex)) != 0)
117 return (ret);
118
119 /*
120 * Only allow changing 'force_write' when it's really safe; i.e.,
121 * the lock hasn't been destroyed, and there are no readers.
122 */
123 if (rw->destroyed == 0 && rw->reader_count == 0) {
124 rw->force_write = 0;
125 ret = 0;
126 } else {
127 ret = EBUSY;
128 }
129
130 (void) mutex_unlock(&rw->mutex);
131
132 return (ret);
133 }
134
135
136 int
137 __nisdb_rw_force_writelock(__nisdb_rwlock_t *rw) {
138 int ret;
139
140 if (rw == 0 || rw->destroyed != 0)
141 return (ESHUTDOWN);
142
143 if ((ret = mutex_lock(&rw->mutex)) != 0)
144 return (ret);
145
146 /*
147 * Only allow changing 'force_write' when it's really safe; i.e.,
148 * the lock hasn't been destroyed, and there are no readers.
149 */
150 if (rw->destroyed == 0 && rw->reader_count == 0) {
151 rw->force_write = 1;
152 ret = 0;
153 } else {
154 ret = EBUSY;
155 }
156
157 (void) mutex_unlock(&rw->mutex);
158
184
185 if (rw->destroyed != 0) {
186 (void) mutex_unlock(&rw->mutex);
187 return (ESHUTDOWN);
188 }
189
190 /* Simplest (and probably most common) case: no readers or writers */
191 if (rw->reader_count == 0 && rw->writer_count == 0) {
192 rw->writer_count = 1;
193 rw->writer.id = myself;
194 rw->writer.count = 1;
195 return (mutex_unlock(&rw->mutex));
196 }
197
198 /*
199 * Need to know if we're holding a read lock already, and if
200 * all other readers are blocked waiting for the mutex.
201 */
202 if (rw->reader_count > 0) {
203 if ((rr = find_reader(myself, rw)) != 0) {
204 if (rr->count) {
205 /*
206 * We're already holding a read lock, so
207 * if the number of readers equals the number
208 * of blocked readers plus one, all other
209 * readers are blocked.
210 */
211 if (rw->reader_count ==
212 (rw->reader_blocked + 1))
213 all_readers_blocked = 1;
214 } else {
215 /*
216 * We're not holding a read lock, so the
217 * number of readers should equal the number
218 * of blocked readers if all readers are
219 * blocked.
220 */
221 if (rw->reader_count == rw->reader_blocked)
222 all_readers_blocked = 1;
223 }
224 }
225 }
226
227 /* Wait for reader(s) or writer to finish */
228 while (1) {
229 /*
230 * We can stop looping if one of the following holds:
231 * - No readers, no writers
232 * - No writers (or writer is myself), and one of:
233 * - No readers
234 * - One reader, and it's us
235 * - N readers, but all blocked on the mutex
236 */
237 if (
238 (rw->writer_count == 0 && rw->reader_count == 0) ||
239 (((rw->writer_count == 0 || rw->writer.id == myself) &&
240 (rw->reader_count == 0)) ||
241 (rw->reader_count == 1 &&
242 rw->reader.id == myself))) {
243 break;
244 }
245 /*
246 * Provided that all readers are blocked on the mutex
247 * we break a potential dead-lock by acquiring the
248 * write lock.
249 */
250 if (all_readers_blocked) {
251 if (rw->writer_count == 0 || rw->writer.id == myself) {
252 break;
253 }
254 }
255
256 /*
257 * If 'trylock' is set, tell the caller that we'd have to
258 * block to obtain the lock.
259 */
260 if (trylock) {
627
628 rr = &rw->reader;
629 do {
630 if (rr->id == myself) {
631 (void) mutex_unlock(&rw->mutex);
632 return (0);
633 }
634 rr = rr->next;
635 } while (rr != 0);
636
637 ret = mutex_unlock(&rw->mutex);
638 return ((ret == 0) ? EBUSY : ret);
639 }
640
641
642 int
643 __nisdb_destroy_lock(__nisdb_rwlock_t *rw) {
644
645 int ret;
646 pthread_t myself = pthread_self();
647
648
649 if (rw == 0) {
650 #ifdef NISDB_MT_DEBUG
651 abort();
652 #endif /* NISDB_MT_DEBUG */
653 return (EFAULT);
654 }
655
656 if (rw->destroyed != 0)
657 return (ESHUTDOWN);
658
659 if ((ret = mutex_lock(&rw->mutex)) != 0)
660 return (ret);
661
662 if (rw->destroyed != 0) {
663 (void) mutex_unlock(&rw->mutex);
664 return (ESHUTDOWN);
665 }
666
667 /*
668 * Only proceed if if there are neither readers nor writers
669 * other than this thread. Also, no nested locks may be in
670 * effect.
671 */
672 if (((rw->writer_count > 0 &&
673 (rw->writer.id != myself || rw->writer.count != 1)) ||
674 (rw->reader_count > 0 &&
675 !(rw->reader_count == 1 && rw->reader.id == myself &&
676 rw->reader.count == 1))) ||
677 (rw->writer_count > 0 && rw->reader_count > 0)) {
678 #ifdef NISDB_MT_DEBUG
679 abort();
680 #endif /* NISDB_MT_DEBUG */
681 (void) mutex_unlock(&rw->mutex);
682 return (ENOLCK);
683 }
684
685 /*
686 * Mark lock destroyed, so that any thread waiting on the mutex
687 * will know what's what. Of course, this is a bit iffy, since
688 * we're probably being called from a destructor, and the structure
689 * where we live will soon cease to exist (i.e., be freed and
690 * perhaps re-used). Still, we can only do our best, and give
691 * those other threads the best chance possible.
692 */
693 rw->destroyed++;
|