|
|
1.1 root 1: /*
2: * Mach Operating System
3: * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University.
4: * Copyright (c) 1993,1994 The University of Utah and
5: * the Computer Systems Laboratory (CSL).
6: * All rights reserved.
7: *
8: * Permission to use, copy, modify and distribute this software and its
9: * documentation is hereby granted, provided that both the copyright
10: * notice and this permission notice appear in all copies of the
11: * software, derivative works or modified versions, and any portions
12: * thereof, and that both notices appear in supporting documentation.
13: *
14: * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
15: * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
16: * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
17: * THIS SOFTWARE.
18: *
19: * Carnegie Mellon requests users of this software to return to
20: *
21: * Software Distribution Coordinator or [email protected]
22: * School of Computer Science
23: * Carnegie Mellon University
24: * Pittsburgh PA 15213-3890
25: *
26: * any improvements or extensions that they make and grant Carnegie Mellon
27: * the rights to redistribute these changes.
28: */
29: /*
30: * File: kern/lock.c
31: * Author: Avadis Tevanian, Jr., Michael Wayne Young
32: * Date: 1985
33: *
34: * Locking primitives implementation
35: */
36:
37: #include <cpus.h>
38: #include <mach_kdb.h>
39:
40: #include <kern/lock.h>
41: #include <kern/thread.h>
42: #include <kern/sched_prim.h>
43: #if MACH_KDB
44: #include <machine/db_machdep.h>
45: #include <ddb/db_sym.h>
46: #endif
47:
48:
49: #if NCPUS > 1
50:
51: /*
52: * Module: lock
53: * Function:
54: * Provide reader/writer sychronization.
55: * Implementation:
56: * Simple interlock on a bit. Readers first interlock,
57: * increment the reader count, then let go. Writers hold
58: * the interlock (thus preventing further readers), and
59: * wait for already-accepted readers to go away.
60: */
61:
62: /*
63: * The simple-lock routines are the primitives out of which
64: * the lock package is built. The implementation is left
65: * to the machine-dependent code.
66: */
67:
68: #ifdef notdef
69: /*
70: * A sample implementation of simple locks.
71: * assumes:
72: * boolean_t test_and_set(boolean_t *)
73: * indivisibly sets the boolean to TRUE
74: * and returns its old value
75: * and that setting a boolean to FALSE is indivisible.
76: */
77: /*
78: * simple_lock_init initializes a simple lock. A simple lock
79: * may only be used for exclusive locks.
80: */
81:
82: void simple_lock_init(simple_lock_t l)
83: {
84: *(boolean_t *)l = FALSE;
85: }
86:
87: void simple_lock(simple_lock_t l)
88: {
89: while (test_and_set((boolean_t *)l))
90: continue;
91: }
92:
93: void simple_unlock(simple_lock_t l)
94: {
95: *(boolean_t *)l = FALSE;
96: }
97:
98: boolean_t simple_lock_try(simple_lock_t l)
99: {
100: return (!test_and_set((boolean_t *)l));
101: }
102: #endif /* notdef */
103: #endif /* NCPUS > 1 */
104:
105: #if NCPUS > 1
106: int lock_wait_time = 100;
107: #else /* NCPUS > 1 */
108:
109: /*
110: * It is silly to spin on a uni-processor as if we
111: * thought something magical would happen to the
112: * want_write bit while we are executing.
113: */
114: int lock_wait_time = 0;
115: #endif /* NCPUS > 1 */
116:
117: #if MACH_SLOCKS && NCPUS == 1
118: /*
119: * This code does not protect simple_locks_taken and simple_locks_info.
120: * It works despite the fact that interrupt code does use simple locks.
121: * This is because interrupts use locks in a stack-like manner.
122: * Each interrupt releases all the locks it acquires, so the data
123: * structures end up in the same state after the interrupt as before.
124: * The only precaution necessary is that simple_locks_taken be
125: * incremented first and decremented last, so that interrupt handlers
126: * don't over-write active slots in simple_locks_info.
127: */
128:
129: unsigned int simple_locks_taken = 0;
130:
131: #define NSLINFO 1000 /* maximum number of locks held */
132:
133: struct simple_locks_info {
134: simple_lock_t l;
135: unsigned int ra;
136: } simple_locks_info[NSLINFO];
137:
138: void check_simple_locks(void)
139: {
140: assert(simple_locks_taken == 0);
141: }
142:
143: /* Need simple lock sanity checking code if simple locks are being
144: compiled in, and we are compiling for a uniprocessor. */
145:
146: void simple_lock_init(
147: simple_lock_t l)
148: {
149: l->lock_data = 0;
150: }
151:
152: void simple_lock(
153: simple_lock_t l)
154: {
155: struct simple_locks_info *info;
156:
157: assert(l->lock_data == 0);
158:
159: l->lock_data = 1;
160:
161: info = &simple_locks_info[simple_locks_taken++];
162: info->l = l;
163: /* XXX we want our return address, if possible */
164: #ifdef i386
165: info->ra = *((unsigned int *)&l - 1);
166: #endif /* i386 */
167: }
168:
169: boolean_t simple_lock_try(
170: simple_lock_t l)
171: {
172: struct simple_locks_info *info;
173:
174: if (l->lock_data != 0)
175: return FALSE;
176:
177: l->lock_data = 1;
178:
179: info = &simple_locks_info[simple_locks_taken++];
180: info->l = l;
181: /* XXX we want our return address, if possible */
182: #ifdef i386
183: info->ra = *((unsigned int *)&l - 1);
184: #endif /* i386 */
185:
186: return TRUE;
187: }
188:
189: void simple_unlock(
190: simple_lock_t l)
191: {
192: assert(l->lock_data != 0);
193:
194: l->lock_data = 0;
195:
196: if (simple_locks_info[simple_locks_taken-1].l != l) {
197: unsigned int i = simple_locks_taken;
198:
199: /* out-of-order unlocking */
200:
201: do
202: if (i == 0)
203: panic("simple_unlock");
204: while (simple_locks_info[--i].l != l);
205:
206: simple_locks_info[i] = simple_locks_info[simple_locks_taken-1];
207: }
208: simple_locks_taken--;
209: }
210:
211: #endif /* MACH_SLOCKS && NCPUS == 1 */
212:
213: /*
214: * Routine: lock_init
215: * Function:
216: * Initialize a lock; required before use.
217: * Note that clients declare the "struct lock"
218: * variables and then initialize them, rather
219: * than getting a new one from this module.
220: */
221: void lock_init(
222: lock_t l,
223: boolean_t can_sleep)
224: {
225: bzero((char *)l, sizeof(lock_data_t));
226: simple_lock_init(&l->interlock);
227: l->want_write = FALSE;
228: l->want_upgrade = FALSE;
229: l->read_count = 0;
230: l->can_sleep = can_sleep;
231: l->thread = (struct thread *)-1; /* XXX */
232: l->recursion_depth = 0;
233: }
234:
235: void lock_sleepable(
236: lock_t l,
237: boolean_t can_sleep)
238: {
239: simple_lock(&l->interlock);
240: l->can_sleep = can_sleep;
241: simple_unlock(&l->interlock);
242: }
243:
244:
245: /*
246: * Sleep locks. These use the same data structure and algorithm
247: * as the spin locks, but the process sleeps while it is waiting
248: * for the lock. These work on uniprocessor systems.
249: */
250:
251: void lock_write(
252: register lock_t l)
253: {
254: register int i;
255:
256: check_simple_locks();
257: simple_lock(&l->interlock);
258:
259: if (l->thread == current_thread()) {
260: /*
261: * Recursive lock.
262: */
263: l->recursion_depth++;
264: simple_unlock(&l->interlock);
265: return;
266: }
267:
268: /*
269: * Try to acquire the want_write bit.
270: */
271: while (l->want_write) {
272: if ((i = lock_wait_time) > 0) {
273: simple_unlock(&l->interlock);
274: while (--i > 0 && l->want_write)
275: continue;
276: simple_lock(&l->interlock);
277: }
278:
279: if (l->can_sleep && l->want_write) {
280: l->waiting = TRUE;
281: thread_sleep(l,
282: simple_lock_addr(l->interlock), FALSE);
283: simple_lock(&l->interlock);
284: }
285: }
286: l->want_write = TRUE;
287:
288: /* Wait for readers (and upgrades) to finish */
289:
290: while ((l->read_count != 0) || l->want_upgrade) {
291: if ((i = lock_wait_time) > 0) {
292: simple_unlock(&l->interlock);
293: while (--i > 0 && (l->read_count != 0 ||
294: l->want_upgrade))
295: continue;
296: simple_lock(&l->interlock);
297: }
298:
299: if (l->can_sleep && (l->read_count != 0 || l->want_upgrade)) {
300: l->waiting = TRUE;
301: thread_sleep(l,
302: simple_lock_addr(l->interlock), FALSE);
303: simple_lock(&l->interlock);
304: }
305: }
306: simple_unlock(&l->interlock);
307: }
308:
309: void lock_done(
310: register lock_t l)
311: {
312: simple_lock(&l->interlock);
313:
314: if (l->read_count != 0)
315: l->read_count--;
316: else
317: if (l->recursion_depth != 0)
318: l->recursion_depth--;
319: else
320: if (l->want_upgrade)
321: l->want_upgrade = FALSE;
322: else
323: l->want_write = FALSE;
324:
325: /*
326: * There is no reason to wakeup a waiting thread
327: * if the read-count is non-zero. Consider:
328: * we must be dropping a read lock
329: * threads are waiting only if one wants a write lock
330: * if there are still readers, they can't proceed
331: */
332:
333: if (l->waiting && (l->read_count == 0)) {
334: l->waiting = FALSE;
335: thread_wakeup(l);
336: }
337:
338: simple_unlock(&l->interlock);
339: }
340:
341: void lock_read(
342: register lock_t l)
343: {
344: register int i;
345:
346: check_simple_locks();
347: simple_lock(&l->interlock);
348:
349: if (l->thread == current_thread()) {
350: /*
351: * Recursive lock.
352: */
353: l->read_count++;
354: simple_unlock(&l->interlock);
355: return;
356: }
357:
358: while (l->want_write || l->want_upgrade) {
359: if ((i = lock_wait_time) > 0) {
360: simple_unlock(&l->interlock);
361: while (--i > 0 && (l->want_write || l->want_upgrade))
362: continue;
363: simple_lock(&l->interlock);
364: }
365:
366: if (l->can_sleep && (l->want_write || l->want_upgrade)) {
367: l->waiting = TRUE;
368: thread_sleep(l,
369: simple_lock_addr(l->interlock), FALSE);
370: simple_lock(&l->interlock);
371: }
372: }
373:
374: l->read_count++;
375: simple_unlock(&l->interlock);
376: }
377:
378: /*
379: * Routine: lock_read_to_write
380: * Function:
381: * Improves a read-only lock to one with
382: * write permission. If another reader has
383: * already requested an upgrade to a write lock,
384: * no lock is held upon return.
385: *
386: * Returns TRUE if the upgrade *failed*.
387: */
388: boolean_t lock_read_to_write(
389: register lock_t l)
390: {
391: register int i;
392:
393: check_simple_locks();
394: simple_lock(&l->interlock);
395:
396: l->read_count--;
397:
398: if (l->thread == current_thread()) {
399: /*
400: * Recursive lock.
401: */
402: l->recursion_depth++;
403: simple_unlock(&l->interlock);
404: return(FALSE);
405: }
406:
407: if (l->want_upgrade) {
408: /*
409: * Someone else has requested upgrade.
410: * Since we've released a read lock, wake
411: * him up.
412: */
413: if (l->waiting && (l->read_count == 0)) {
414: l->waiting = FALSE;
415: thread_wakeup(l);
416: }
417:
418: simple_unlock(&l->interlock);
419: return TRUE;
420: }
421:
422: l->want_upgrade = TRUE;
423:
424: while (l->read_count != 0) {
425: if ((i = lock_wait_time) > 0) {
426: simple_unlock(&l->interlock);
427: while (--i > 0 && l->read_count != 0)
428: continue;
429: simple_lock(&l->interlock);
430: }
431:
432: if (l->can_sleep && l->read_count != 0) {
433: l->waiting = TRUE;
434: thread_sleep(l,
435: simple_lock_addr(l->interlock), FALSE);
436: simple_lock(&l->interlock);
437: }
438: }
439:
440: simple_unlock(&l->interlock);
441: return FALSE;
442: }
443:
444: void lock_write_to_read(
445: register lock_t l)
446: {
447: simple_lock(&l->interlock);
448:
449: l->read_count++;
450: if (l->recursion_depth != 0)
451: l->recursion_depth--;
452: else
453: if (l->want_upgrade)
454: l->want_upgrade = FALSE;
455: else
456: l->want_write = FALSE;
457:
458: if (l->waiting) {
459: l->waiting = FALSE;
460: thread_wakeup(l);
461: }
462:
463: simple_unlock(&l->interlock);
464: }
465:
466:
467: /*
468: * Routine: lock_try_write
469: * Function:
470: * Tries to get a write lock.
471: *
472: * Returns FALSE if the lock is not held on return.
473: */
474:
475: boolean_t lock_try_write(
476: register lock_t l)
477: {
478: simple_lock(&l->interlock);
479:
480: if (l->thread == current_thread()) {
481: /*
482: * Recursive lock
483: */
484: l->recursion_depth++;
485: simple_unlock(&l->interlock);
486: return TRUE;
487: }
488:
489: if (l->want_write || l->want_upgrade || l->read_count) {
490: /*
491: * Can't get lock.
492: */
493: simple_unlock(&l->interlock);
494: return FALSE;
495: }
496:
497: /*
498: * Have lock.
499: */
500:
501: l->want_write = TRUE;
502: simple_unlock(&l->interlock);
503: return TRUE;
504: }
505:
506: /*
507: * Routine: lock_try_read
508: * Function:
509: * Tries to get a read lock.
510: *
511: * Returns FALSE if the lock is not held on return.
512: */
513:
514: boolean_t lock_try_read(
515: register lock_t l)
516: {
517: simple_lock(&l->interlock);
518:
519: if (l->thread == current_thread()) {
520: /*
521: * Recursive lock
522: */
523: l->read_count++;
524: simple_unlock(&l->interlock);
525: return TRUE;
526: }
527:
528: if (l->want_write || l->want_upgrade) {
529: simple_unlock(&l->interlock);
530: return FALSE;
531: }
532:
533: l->read_count++;
534: simple_unlock(&l->interlock);
535: return TRUE;
536: }
537:
538: /*
539: * Routine: lock_try_read_to_write
540: * Function:
541: * Improves a read-only lock to one with
542: * write permission. If another reader has
543: * already requested an upgrade to a write lock,
544: * the read lock is still held upon return.
545: *
546: * Returns FALSE if the upgrade *failed*.
547: */
548: boolean_t lock_try_read_to_write(
549: register lock_t l)
550: {
551: check_simple_locks();
552: simple_lock(&l->interlock);
553:
554: if (l->thread == current_thread()) {
555: /*
556: * Recursive lock
557: */
558: l->read_count--;
559: l->recursion_depth++;
560: simple_unlock(&l->interlock);
561: return TRUE;
562: }
563:
564: if (l->want_upgrade) {
565: simple_unlock(&l->interlock);
566: return FALSE;
567: }
568: l->want_upgrade = TRUE;
569: l->read_count--;
570:
571: while (l->read_count != 0) {
572: l->waiting = TRUE;
573: thread_sleep(l,
574: simple_lock_addr(l->interlock), FALSE);
575: simple_lock(&l->interlock);
576: }
577:
578: simple_unlock(&l->interlock);
579: return TRUE;
580: }
581:
582: /*
583: * Allow a process that has a lock for write to acquire it
584: * recursively (for read, write, or update).
585: */
586: void lock_set_recursive(
587: lock_t l)
588: {
589: simple_lock(&l->interlock);
590: if (!l->want_write) {
591: panic("lock_set_recursive: don't have write lock");
592: }
593: l->thread = current_thread();
594: simple_unlock(&l->interlock);
595: }
596:
597: /*
598: * Prevent a lock from being re-acquired.
599: */
600: void lock_clear_recursive(
601: lock_t l)
602: {
603: simple_lock(&l->interlock);
604: if (l->thread != current_thread()) {
605: panic("lock_clear_recursive: wrong thread");
606: }
607: if (l->recursion_depth == 0)
608: l->thread = (struct thread *)-1; /* XXX */
609: simple_unlock(&l->interlock);
610: }
611:
612: #if MACH_KDB
613: #if MACH_SLOCKS && NCPUS == 1
614: void db_show_all_slocks(void)
615: {
616: int i;
617: struct simple_locks_info *info;
618: simple_lock_t l;
619:
620: for (i = 0; i < simple_locks_taken; i++) {
621: info = &simple_locks_info[i];
622: db_printf("%d: ", i);
623: db_printsym(info->l, DB_STGY_ANY);
624: #if i386
625: db_printf(" locked by ");
626: db_printsym(info->ra, DB_STGY_PROC);
627: #endif
628: db_printf("\n");
629: }
630: }
631: #else /* MACH_SLOCKS && NCPUS == 1 */
632: void db_show_all_slocks(void)
633: {
634: db_printf("simple lock info not available\n");
635: }
636: #endif /* MACH_SLOCKS && NCPUS == 1 */
637: #endif /* MACH_KDB */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.