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