ubik-doxygen-20081121
[openafs.git] / src / ubik / lock.c
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 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <sys/types.h>
17 #ifndef AFS_NT40_ENV
18 #include <sys/file.h>
19 #endif
20 #include <errno.h>
21 #include <lock.h>
22 #include <rx/xdr.h>
23
24 #define UBIK_INTERNALS 1
25 #include "ubik.h"
26 #include "ubik_int.h"
27
28 /*! \file
29  * Locks hang off of each transaction, with all the transaction hanging off of
30  * the appropriate dbase.  This package expects to be used in a two-phase locking
31  * protocol, so it doesn't provide a way to release anything but all of the locks in the
32  * transaction.
33  *
34  * At present, it doesn't support the setting of more than one byte-position lock at a time, that is
35  * the length field must be 1.  This doesn't mean that a single transaction can't set more than
36  * one lock, however.
37  *
38  * It is the responsibility of the user to avoid deadlock by setting locks in a partial order.
39  *
40  * #EWOULDBLOCK has been replaced in this file by #EAGAIN. Many Unix's but not
41  * all (eg. HP) do not replace #EWOULDBLOCK with #EAGAIN. The bad news is this
42  * goes over the wire. The good news is that the code path is never triggered
43  * as it requires ulock_getLock to be called with await = 0. And ulock_SetLock
44  * isn't even used in this code base. Since NT doesn't have a native
45  * #EAGAIN, we are replacing all instances of #EWOULDBLOCK with #EAGAIN.
46  * 
47  */
48
49 #define WouldReadBlock(lock)\
50   ((((lock)->excl_locked & WRITE_LOCK) || (lock)->wait_states) ? 0 : 1)
51 #define WouldWriteBlock(lock)\
52   ((((lock)->excl_locked & WRITE_LOCK) || (lock)->readers_reading) ? 0 : 1)
53
54 struct Lock rwlock;
55 int rwlockinit = 1;
56
57 /*!
58  * \brief Set a transaction lock.
59  * \param atype is #LOCKREAD or #LOCKWRITE.
60  * \param await is TRUE if you want to wait for the lock instead of returning
61  * #EWOULDBLOCK.
62  *
63  * \note The #DBHOLD lock must be held.
64  */
65 ulock_getLock(atrans, atype, await)
66      struct ubik_trans *atrans;
67      int atype, await;
68 {
69     struct ubik_dbase *dbase = atrans->dbase;
70
71     /* On first pass, initialize the lock */
72     if (rwlockinit) {
73         Lock_Init(&rwlock);
74         rwlockinit = 0;
75     }
76
77     if ((atype != LOCKREAD) && (atype != LOCKWRITE))
78         return EINVAL;
79
80     if (atrans->flags & TRDONE)
81         return UDONE;
82
83     if (atrans->locktype != 0) {
84         ubik_print("Ubik: Internal Error: attempted to take lock twice\n");
85         abort();
86     }
87
88 /*
89  *ubik_print("Ubik: DEBUG: Thread 0x%x request %s lock\n", lwp_cpptr,
90  *           ((atype == LOCKREAD) ? "READ" : "WRITE"));
91  */
92
93     /* Check if the lock would would block */
94     if (!await) {
95         if (atype == LOCKREAD) {
96             if (WouldReadBlock(&rwlock))
97                 return EAGAIN;
98         } else {
99             if (WouldWriteBlock(&rwlock))
100                 return EAGAIN;
101         }
102     }
103
104     /* Create new lock record and add to spec'd transaction:
105      * #if defined(UBIK_PAUSE)
106      * * locktype.  Before doing that, set TRSETLOCK,
107      * * to tell udisk_end that another thread (us) is waiting.
108      * #else
109      * * locktype. This field also tells us if the thread is 
110      * * waiting for a lock: It will be equal to LOCKWAIT.
111      * #endif 
112      */
113 #if defined(UBIK_PAUSE)
114     if (atrans->flags & TRSETLOCK) {
115         printf("Ubik: Internal Error: TRSETLOCK already set?\n");
116         return EBUSY;
117     }
118     atrans->flags |= TRSETLOCK;
119 #else
120     atrans->locktype = LOCKWAIT;
121 #endif /* UBIK_PAUSE */
122     DBRELE(dbase);
123     if (atype == LOCKREAD) {
124         ObtainReadLock(&rwlock);
125     } else {
126         ObtainWriteLock(&rwlock);
127     }
128     DBHOLD(dbase);
129     atrans->locktype = atype;
130 #if defined(UBIK_PAUSE)
131     atrans->flags &= ~TRSETLOCK;
132 #if 0
133     /* We don't do this here, because this can only happen in SDISK_Lock,
134      *  and there's already code there to catch this condition.
135      */
136     if (atrans->flags & TRSTALE) {
137         udisk_end(atrans);
138         return UINTERNAL;
139     }
140 #endif
141 #endif /* UBIK_PAUSE */
142
143 /*
144  *ubik_print("Ubik: DEBUG: Thread 0x%x took %s lock\n", lwp_cpptr,
145  *           ((atype == LOCKREAD) ? "READ" : "WRITE"));
146  */
147     return 0;
148 }
149
150 /*!
151  * \brief Release the transaction lock.
152  */
153 void
154 ulock_relLock(atrans)
155      struct ubik_trans *atrans;
156 {
157     if (rwlockinit)
158         return;
159
160     if (atrans->locktype == LOCKREAD) {
161         ReleaseReadLock(&rwlock);
162     } else if (atrans->locktype == LOCKWRITE) {
163         ReleaseWriteLock(&rwlock);
164     }
165
166 /*
167  *ubik_print("Ubik: DEBUG: Thread 0x%x %s unlock\n", lwp_cpptr,
168  *           ((atrans->locktype == LOCKREAD) ? "READ" : "WRITE"));
169  */
170
171     atrans->locktype = 0;
172     return;
173 }
174
175 /*!
176  * \brief debugging hooks
177  */
178 ulock_Debug(aparm)
179      struct ubik_debug *aparm;
180 {
181     if (rwlockinit) {
182         aparm->anyReadLocks = 0;
183         aparm->anyWriteLocks = 0;
184     } else {
185         aparm->anyReadLocks = rwlock.readers_reading;
186         aparm->anyWriteLocks = ((rwlock.excl_locked == WRITE_LOCK) ? 1 : 0);
187     }
188     return 0;
189 }