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