1 /* BLURB lgpl
2 
3                            Coda File System
4                               Release 5
5 
6             Copyright (c) 1999 Carnegie Mellon University
7                   Additional copyrights listed below
8 
9 This  code  is  distributed "AS IS" without warranty of any kind under
10 the  terms of the  GNU  Library General Public Licence  Version 2,  as
11 shown in the file LICENSE. The technical and financial contributors to
12 Coda are listed in the file CREDITS.
13 
14                         Additional copyrights
15 #*/
16 
17 #include <pthread.h>
18 #include <assert.h>
19 
20 #include <lwp/lwp.h>
21 #include <lwp/lock.h>
22 #include "lwp.private_pt.h"
23 
24 /**** RW locks *****/
25 /* Well, actually Read/Shared/Write locks
26  * Read lock   -- obtained when there are no (queued) write lockers.
27  *
28  *                blocks all writers.
29  *                 (shares an `exclusive lock' with other readers and at
30  *                  most one shared locker)
31  *
32  * Shared lock -- obtained when there are no (queued) write lockers.
33  *
34  *                blocks all others.
35  *                 (shares the lock with readers present when it entered, but
36  *                  sets excl to block access for new lockers)
37  *
38  * Write lock  -- obtained when there are no others holding the lock.
39  *
40  *                blocks all others.
41  *                 (sets the excl flag to block access)
42  */
43 
Lock_Init(struct Lock * lock)44 void Lock_Init(struct Lock *lock)
45 {
46     lock->initialized = 1;
47     lock->readers     = 0;
48     lock->excl        = NULL;
49     pthread_mutex_init(&lock->_access, NULL);
50     pthread_cond_init(&lock->wakeup, NULL);
51 
52     lwp_dbg(LWP_DBG_LOCKS, "I lock %p\n", lock)
53 }
54 
ObtainLock(struct Lock * lock,char type)55 static void ObtainLock(struct Lock *lock, char type)
56 {
57     PROCESS pid;
58     assert(LWP_CurrentProcess(&pid) == 0);
59 
60     if (!lock->initialized)
61 	Lock_Init(lock);
62 
63     lwp_LEAVE(pid);
64     lwp_mutex_lock(&lock->_access);
65     {
66 	/* now start waiting, writers wait until all readers have left, all
67 	 * lockers wait for the excl flag to be cleared */
68 	/* this is a safe cancellation point because we (should) only hold
69 	 * the access mutex, and we take ourselves off the pending list in the
70 	 * cleanup handler */
71 	while (lock->excl || (type == 'W' && lock->readers))
72 	    pthread_cond_wait(&lock->wakeup, &lock->_access);
73 
74 	/* Obtain the correct lock flags, read locks increment readers, write
75 	 * locks set the excl flag and shared locks do both */
76 	if (type != 'R') lock->excl = pid;
77 	if (type != 'W') lock->readers++;
78 
79 	    /* signal other threads, there might be more readers */
80 	if (type == 'R')
81 	    pthread_cond_broadcast(&lock->wakeup);
82 
83 	lwp_dbg(LWP_DBG_LOCKS, "%c+ pid %p lock %p\n", type, pid, lock);
84     }
85     lwp_mutex_unlock(&lock->_access);
86     lwp_YIELD(pid);
87 }
88 
ReleaseLock(struct Lock * lock,char type)89 static void ReleaseLock(struct Lock *lock, char type)
90 {
91     PROCESS pid;
92     assert(LWP_CurrentProcess(&pid) == 0);
93 
94     /* acquire the lock-access mutex */
95     lwp_mutex_lock(&lock->_access);
96 
97     if (type != 'R') {
98         assert(lock->excl == pid);
99         lock->excl = NULL;
100     }
101     if (type != 'W')
102         lock->readers--;
103 
104     lwp_dbg(LWP_DBG_LOCKS, "%c- pid %p lock %p\n", type, pid, lock)
105 
106     /* if we cleared the lock, signal the next pending locker */
107     if (!lock->excl && !lock->readers)
108         pthread_cond_signal(&lock->wakeup);
109 
110     /* and release the lock-access mutex */
111     lwp_mutex_unlock(&lock->_access);
112 }
113 
ObtainReadLock(struct Lock * lock)114 void ObtainReadLock(struct Lock *lock)    { ObtainLock(lock, 'R'); }
ObtainWriteLock(struct Lock * lock)115 void ObtainWriteLock(struct Lock *lock)   { ObtainLock(lock, 'W'); }
ObtainSharedLock(struct Lock * lock)116 void ObtainSharedLock(struct Lock *lock)  { ObtainLock(lock, 'S'); }
ReleaseReadLock(struct Lock * lock)117 void ReleaseReadLock(struct Lock *lock)   { ReleaseLock(lock, 'R'); }
ReleaseWriteLock(struct Lock * lock)118 void ReleaseWriteLock(struct Lock *lock)  { ReleaseLock(lock, 'W'); }
ReleaseSharedLock(struct Lock * lock)119 void ReleaseSharedLock(struct Lock *lock) { ReleaseLock(lock, 'S'); }
120 
121 /* This function is silly anyway you look at it.
122  * Think 2 concurrent threads trying to get a lock, that would use
123  * this function to check if it is available to avoid blocking.
124  * Accidents waiting to happen? */
CheckLock(struct Lock * lock)125 int CheckLock(struct Lock *lock)
126 {
127     if (lock->readers)     return lock->readers;
128     if (WriteLocked(lock)) return -1;
129     return 0;
130 }
131 
132 /* What is the purpose here? to see whether there is any active
133  * writer (0 readers, n writers), or any pending writers and or
134  * active shared locks (n writers), or whether a shared lock has
135  * become a write lock (1 reader, 1 writer)
136  * The original code didn't give many clues, maybe the LWP docs do? */
WriteLocked(struct Lock * lock)137 int WriteLocked(struct Lock *lock)
138 {
139     return (lock->excl != NULL);
140 }
141 
142