summary refs log tree commit diff
path: root/db2/include/lock.h
blob: 5031b65d0631292ab6de12d8e70a1cbedc12d392 (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
198
199
200
201
202
203
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1996, 1997
 *	Sleepycat Software.  All rights reserved.
 *
 *	@(#)lock.h	10.10 (Sleepycat) 11/13/97
 */

typedef struct __db_lockobj	DB_LOCKOBJ;

#define DB_DEFAULT_LOCK_FILE	"__db_lock.share"
#define DB_LOCK_DEFAULT_N	5000

/*
 * 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

/*
 * 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)->fd)
#define	UNLOCK_LOCKREGION(lt)						\
	(void)__db_mutex_unlock(&(lt)->region->hdr.lock, (lt)->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))

/*
 * The lock table is the per-process cookie returned from a lock_open call.
 */
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

struct __db_locktab {
	DB_ENV		*dbenv;		/* Environment. */
	int		 fd;		/* mapped file descriptor */
	DB_LOCKREGION	*region;	/* address of shared memory region */
	DB_HASHTAB 	*hashtab; 	/* Beginning of hash table. */
	size_t		reg_size;	/* last known size of lock region */
	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]

/*
 * Status of a lock.
 */
typedef enum {
	DB_LSTAT_ABORTED,		/* Lock belongs to an aborted txn. */
	DB_LSTAT_ERR,			/* Lock is bad. */
	DB_LSTAT_FREE,			/* Lock is unallocated. */
	DB_LSTAT_HELD,			/* Lock is currently held. */
	DB_LSTAT_NOGRANT,		/* Lock was not granted. */
	DB_LSTAT_PENDING,		/* Lock was waiting and has been
					 * promoted; waiting for the owner
					 * to run and upgrade it to held. */
	DB_LSTAT_WAITING		/* Lock is on the wait queue. */
} db_status_t;

/*
 * 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. */
	db_status_t	status;		/* Status of this lock. */
};

/*
 * 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)))

#ifdef DEBUG
#define	LOCK_DEBUG_LOCKERS	0x0001
#define	LOCK_DEBUG_LOCK	 	0x0002
#define	LOCK_DEBUG_OBJ	 	0x0004
#define	LOCK_DEBUG_CONF	 	0x0008
#define	LOCK_DEBUG_MEM	 	0x0010
#define	LOCK_DEBUG_BUCKET	0x0020
#define LOCK_DEBUG_OBJECTS	0x0040
#define	LOCK_DEBUG_ALL	 	0xFFFF

#define	LOCK_DEBUG_NOMUTEX	0x0100
#endif

#include "lock_ext.h"