about summary refs log tree commit diff
path: root/db2/include/lock.h
blob: 13364ca7a50b35bb6b5c8132c76545186851370c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1996, 1997, 1998
 *	Sleepycat Software.  All rights reserved.
 *
 *	@(#)lock.h	10.17 (Sleepycat) 1/3/99
 */

typedef struct __db_lockobj	DB_LOCKOBJ;

#define DB_DEFAULT_LOCK_FILE	"__db_lock.share"

#ifndef DB_LOCK_DEFAULT_N
#define DB_LOCK_DEFAULT_N	5000	/* Default # of locks in region. */
#endif

/*
 * The locker id space is divided between the transaction manager and the lock
 * manager.  Lockid's start at 0 and go to DB_LOCK_MAXID.  Txn Id's start at
 * DB_LOCK_MAXID + 1 and go up to TXN_INVALID.
 */
#define DB_LOCK_MAXID		0x7fffffff

/* Check for region catastrophic shutdown. */
#define	LOCK_PANIC_CHECK(lt) {						\
	if ((lt)->region->hdr.panic)					\
		return (DB_RUNRECOVERY);				\
}

/*
 * The lock region consists of:
 *	The DB_LOCKREGION structure (sizeof(DB_LOCKREGION)).
 *	The conflict matrix of nmodes * nmodes bytes (nmodes * nmodes).
 *	The hash table for object lookup (hashsize * sizeof(DB_OBJ *)).
 *	The locks themselves (maxlocks * sizeof(struct __db_lock).
 *	The objects being locked (maxlocks * sizeof(DB_OBJ)).
 *	String space to represent the DBTs that are the objects being locked.
 */
struct __db_lockregion {
	RLAYOUT		hdr;		/* Shared region header. */
	u_int32_t	magic;		/* lock magic number */
	u_int32_t	version;	/* version number */
	u_int32_t	id;		/* unique id generator */
	u_int32_t	need_dd;	/* flag for deadlock detector */
	u_int32_t	detect;		/* run dd on every conflict */
	SH_TAILQ_HEAD(lock_header) free_locks;	/* free lock header */
	SH_TAILQ_HEAD(obj_header) free_objs;	/* free obj header */
	u_int32_t	maxlocks;	/* maximum number of locks in table */
	u_int32_t	table_size;	/* size of hash table */
	u_int32_t	nmodes;		/* number of lock modes */
	u_int32_t	numobjs;	/* number of objects */
	u_int32_t	nlockers;	/* number of lockers */
	size_t		increment;	/* how much to grow region */
	size_t		hash_off;	/* offset of hash table */
	size_t		mem_off;	/* offset of memory region */
	size_t		mem_bytes;	/* number of bytes in memory region */
	u_int32_t	nconflicts;	/* number of lock conflicts */
	u_int32_t	nrequests;	/* number of lock gets */
	u_int32_t	nreleases;	/* number of lock puts */
	u_int32_t	ndeadlocks;	/* number of deadlocks */
};

/* Macros to lock/unlock the region. */
#define	LOCK_LOCKREGION(lt)						\
	(void)__db_mutex_lock(&(lt)->region->hdr.lock, (lt)->reginfo.fd)
#define	UNLOCK_LOCKREGION(lt)						\
	(void)__db_mutex_unlock(&(lt)->region->hdr.lock, (lt)->reginfo.fd)

/*
 * Since we will be keeping DBTs in shared memory, we need the equivalent
 * of a DBT that will work in shared memory.
 */
typedef struct __sh_dbt {
	u_int32_t size;
	ssize_t off;
} SH_DBT;

#define SH_DBT_PTR(p)	((void *)(((u_int8_t *)(p)) + (p)->off))

struct __db_lockobj {
	SH_DBT	lockobj;		/* Identifies object locked. */
	SH_TAILQ_ENTRY links;		/* Links for free list. */
	union {
		SH_TAILQ_HEAD(_wait) _waiters;	/* List of waiting locks. */
		u_int32_t	_dd_id;		/* Deadlock detector id. */
	} wlinks;
	union {
		SH_LIST_HEAD(_held) _heldby;	/* Locks held by this locker. */
		SH_TAILQ_HEAD(_hold) _holders;	/* List of held locks. */
	} dlinks;
#define	DB_LOCK_OBJTYPE		1
#define	DB_LOCK_LOCKER		2
					/* Allocate room in the object to
					 * hold typical DB lock structures
					 * so that we do not have to
					 * allocate them from shalloc. */
	u_int8_t objdata[sizeof(struct __db_ilock)];
	u_int8_t type;			/* Real object or locker id. */
};

#define dd_id	wlinks._dd_id
#define	waiters	wlinks._waiters
#define	holders	dlinks._holders
#define	heldby	dlinks._heldby

/*
 * The lock table is the per-process cookie returned from a lock_open call.
 */
struct __db_locktab {
	DB_ENV		*dbenv;		/* Environment. */
	REGINFO		 reginfo;	/* Region information. */
	DB_LOCKREGION	*region;	/* Address of shared memory region. */
	DB_HASHTAB 	*hashtab; 	/* Beginning of hash table. */
	void		*mem;		/* Beginning of string space. */
	u_int8_t 	*conflicts;	/* Pointer to conflict matrix. */
};

/* Test for conflicts. */
#define CONFLICTS(T, HELD, WANTED) \
	T->conflicts[HELD * T->region->nmodes + WANTED]

/*
 * Resources in the lock region.  Used to indicate which resource
 * is running low when we need to grow the region.
 */
typedef enum {
	DB_LOCK_MEM, DB_LOCK_OBJ, DB_LOCK_LOCK
} db_resource_t;

struct __db_lock {
	/*
	 * Wait on mutex to wait on lock.  You reference your own mutex with
	 * ID 0 and others reference your mutex with ID 1.
	 */
	db_mutex_t	mutex;

	u_int32_t	holder;		/* Who holds this lock. */
	SH_TAILQ_ENTRY	links;		/* Free or holder/waiter list. */
	SH_LIST_ENTRY	locker_links;	/* List of locks held by a locker. */
	u_int32_t	refcount;	/* Reference count the lock. */
	db_lockmode_t	mode;		/* What sort of lock. */
	ssize_t		obj;		/* Relative offset of object struct. */
	size_t		txnoff;		/* Offset of holding transaction. */
	db_status_t	status;		/* Status of this lock. */
};

/*
 * This is a serious layering violation.  To support nested transactions, we
 * need to be able to tell that a lock is held by a transaction (as opposed to
 * some other locker) and to be able to traverse the parent/descendent chain.
 * In order to do this, each lock held by a transaction maintains a reference
 * to the shared memory transaction structure so it can be accessed during lock
 * promotion.  As the structure is in shared memory, we cannot store a pointer
 * to it, so we use the offset within the region.  As nothing lives at region
 * offset 0, we use that to indicate that there is no transaction associated
 * with the current lock.
 */
#define TXN_IS_HOLDING(L)	((L)->txnoff != 0 /* INVALID_REG_OFFSET */)

/*
 * We cannot return pointers to the user (else we cannot easily grow regions),
 * so we return offsets in the region.  These must be converted to and from
 * regular pointers.  Always use the macros below.
 */
#define OFFSET_TO_LOCK(lt, off)	\
	((struct __db_lock *)((u_int8_t *)((lt)->region) + (off)))
#define LOCK_TO_OFFSET(lt, lock) \
	((size_t)((u_int8_t *)(lock) - (u_int8_t *)lt->region))
#define OFFSET_TO_OBJ(lt, off)	\
	((DB_LOCKOBJ *)((u_int8_t *)((lt)->region) + (off)))
#define OBJ_TO_OFFSET(lt, obj) \
	((size_t)((u_int8_t *)(obj) - (u_int8_t *)lt->region))

/*
 * The lock header contains the region structure and the conflict matrix.
 * Aligned to a large boundary because we don't know what the underlying
 * type of the hash table elements are.
 */
#define LOCK_HASH_ALIGN	8
#define LOCK_HEADER_SIZE(M)	\
	((size_t)(sizeof(DB_LOCKREGION) + ALIGN((M * M), LOCK_HASH_ALIGN)))

/*
 * For the full region, we need to add the locks, the objects, the hash table
 * and the string space (which is 16 bytes per lock).
 */
#define STRING_SIZE(N) (16 * N)

#define LOCK_REGION_SIZE(M, N, H)					\
	(ALIGN(LOCK_HEADER_SIZE(M) +					\
	(H) * sizeof(DB_HASHTAB), MUTEX_ALIGNMENT) +			\
	(N) * ALIGN(sizeof(struct __db_lock), MUTEX_ALIGNMENT) +	\
	ALIGN((N) * sizeof(DB_LOCKOBJ), sizeof(size_t)) +		\
	ALIGN(STRING_SIZE(N), sizeof(size_t)))

#include "lock_ext.h"