Standardize License information
[openafs.git] / src / venus / lock.h
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #if !defined(lint) && !defined(LOCORE) && defined(RCS_HDRS)
11 #endif
12 #ifndef __AFSLOCK_INCLUDE__
13 #define __AFSLOCK_INCLUDE__         1
14
15 /*******************************************************************\
16 *                                                                   *
17 *       Information Technology Center                               *
18 *       Carnegie-Mellon University                                  *
19 *                                                                   *
20 *                                                                   *
21 *                                                                   *
22 \*******************************************************************/
23
24 /*
25         Include file for using Vice locking routines.
26 */
27
28 /* The following macros allow multi statement macros to be defined safely, i.e.
29    - the multi statement macro can be the object of an if statement;
30    - the call to the multi statement macro may be legally followed by a semi-colon.
31    BEGINMAC and ENDMAC have been tested with both the portable C compiler and
32    Hi-C.  Both compilers were from the Palo Alto 4.2BSD software releases, and
33    both optimized out the constant loop code.  For an example of the use
34    of BEGINMAC and ENDMAC, see the definition for ReleaseWriteLock, below.
35    An alternative to this, using "if(1)" for BEGINMAC is not used because it
36    may generate worse code with pcc, and may generate warning messages with hi-C.
37 */
38
39 #define BEGINMAC do {
40 #define ENDMAC   } while (0)
41
42 struct afs_bozoLock {
43     short count;    /* count of excl locks */
44     char flags;     /* bit 1: is anyone waiting? */
45     char spare;     /* for later */
46     char *proc;     /* process holding the lock, really a struct proc * */
47 };
48
49 #define AFS_BOZONWAITING    1       /* someone is waiting for this lock */
50
51 /* all locks wait on excl_locked except for READ_LOCK, which waits on readers_reading */
52 struct afs_lock {
53     unsigned char       wait_states;    /* type of lockers waiting */
54     unsigned char       excl_locked;    /* anyone have boosted, shared or write lock? */
55     unsigned char       readers_reading;        /* # readers actually with read locks */
56     unsigned char       num_waiting;    /* probably need this soon */
57 };
58
59 #define READ_LOCK       1
60 #define WRITE_LOCK      2
61 #define SHARED_LOCK     4
62 /* this next is not a flag, but rather a parameter to Afs_Lock_Obtain */
63 #define BOOSTED_LOCK 6
64
65 /* next defines wait_states for which we wait on excl_locked */
66 #define EXCL_LOCKS (WRITE_LOCK|SHARED_LOCK)
67
68 #define ObtainReadLock(lock)\
69         if (!((lock)->excl_locked & WRITE_LOCK))\
70             (lock) -> readers_reading++;\
71         else\
72             Afs_Lock_Obtain(lock, READ_LOCK)
73
74 #define ObtainWriteLock(lock)\
75         if (!(lock)->excl_locked && !(lock)->readers_reading)\
76             (lock) -> excl_locked = WRITE_LOCK;\
77         else\
78             Afs_Lock_Obtain(lock, WRITE_LOCK)
79
80 #define ObtainSharedLock(lock)\
81         if (!(lock)->excl_locked)\
82             (lock) -> excl_locked = SHARED_LOCK;\
83         else\
84             Afs_Lock_Obtain(lock, SHARED_LOCK)
85
86 #define UpgradeSToWLock(lock)\
87         if (!(lock)->readers_reading)\
88             (lock)->excl_locked = WRITE_LOCK;\
89         else\
90             Afs_Lock_Obtain(lock, BOOSTED_LOCK)
91
92 /* this must only be called with a WRITE or boosted SHARED lock! */
93 #define ConvertWToSLock(lock)\
94         BEGINMAC\
95             (lock)->excl_locked = SHARED_LOCK; \
96             if((lock)->wait_states) \
97                 Afs_Lock_ReleaseR(lock); \
98         ENDMAC
99
100 #define ConvertWToRLock(lock) \
101         BEGINMAC\
102             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
103             (lock)->readers_reading++;\
104             Afs_Lock_ReleaseR(lock);\
105         ENDMAC
106
107 #define ConvertSToRLock(lock) \
108         BEGINMAC\
109             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
110             (lock)->readers_reading++;\
111             Afs_Lock_ReleaseR(lock);\
112         ENDMAC
113
114 #define ReleaseReadLock(lock)\
115         BEGINMAC\
116             if (!--(lock)->readers_reading && (lock)->wait_states)\
117                 Afs_Lock_ReleaseW(lock) ; \
118         ENDMAC
119
120 #define ReleaseWriteLock(lock)\
121         BEGINMAC\
122             (lock)->excl_locked &= ~WRITE_LOCK;\
123             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
124         ENDMAC
125
126 /* can be used on shared or boosted (write) locks */
127 #define ReleaseSharedLock(lock)\
128         BEGINMAC\
129             (lock)->excl_locked &= ~(SHARED_LOCK | WRITE_LOCK);\
130             if ((lock)->wait_states) Afs_Lock_ReleaseR(lock);\
131         ENDMAC
132
133 /* I added this next macro to make sure it is safe to nuke a lock -- Mike K. */
134 #define LockWaiters(lock)\
135         ((int) ((lock)->num_waiting))
136
137 #define CheckLock(lock)\
138         ((lock)->excl_locked? (int) -1 : (int) (lock)->readers_reading)
139
140 #define WriteLocked(lock)\
141         ((lock)->excl_locked & WRITE_LOCK)
142
143 /*
144
145 You can also use the lock package for handling parent locks for independently-lockable sets of
146 small objects.  The concept here is that the parent lock is at the same level in the
147 locking hierarchy as the little locks, but certain restrictions apply.
148
149 The general usage pattern is as follows.  You have a set of entries to search.  When searching it, you
150 have a "scan" lock on the table.  If you find what you're looking for, you drop the lock down
151 to a "hold" lock, lock the entry, and release the parent lock.  If you don't find what
152 you're looking for, you create the entry, downgrade the "scan" lock to a "hold" lock,
153 lock the entry and unlock the parent.
154
155 To delete an item from the table, you initially obtain a "purge" lock on the parent.  Unlike all
156 of the other parent lock modes described herein, in order to obtain a "purge" lock mode, you
157 must have released all locks on any items in the table.  Once you have obtained the parent
158 lock in "purge" mode, you should check to see if the entry is locked.  If its not locked, you
159 are free to delete the entry, knowing that no one else can attempt to obtain a lock
160 on the entry while you have the purge lock held on the parent.  Unfortunately, if it *is* locked,
161 you can not lock it yourself and wait for the other dude to release it, since the entry's locker
162 may need to lock another entry before unlocking the entry you want (which would result in
163 deadlock).  Instead, then, you must release the parent lock, and try again "later" (see Lock_Wait
164 for assistance in waiting until later). Unfortunately, this is the best locking paradigm I've yet
165 come up with.
166
167 What are the advantages to this scheme?  First, the use of the parent lock ensures that
168 two people don't try to add the same entry at the same time or delete an entry while someone
169 else is adding it.  It also ensures that when one process is deleting an entry, no one else is
170 preparing to lock the entry.  Furthermore, when obtaining a lock on a little entry, you
171 are only holding a "hold" lock on the parent lock, so that others may come in and search
172 the table during this time.  Thus it will not hold up the system if a little entry takes
173 a great deal of time to free up.
174
175 Here's how to compute the compatibility matrix:
176
177 The invariants are:
178
179 add     no deletions, additions allowed, additions will be performed, will obtain little locks
180 hold    no deletions, additions allowed, no additions will be performed, will obtain little locks
181 purge   no deletions or additions allowed, deletions will be performed, don't obtain little locks
182
183 When we compute the locking matrix, we note that hold is compatible with hold and add.
184 Add is compatible only with hold.  purge is not compatible with anything.  This is the same
185 matrix as obtained by mapping add->S, hold->read and purge->write locks.  Thus we
186 can use the locks above to solve this problem, and we do.
187
188 */
189 #endif /* __AFSLOCK_INCLUDE__ */