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