aboutsummaryrefslogtreecommitdiff
path: root/src/include/storage/s_lock.h
blob: 6b6e4ecfb0fbf30f3a3e09a4b791c7ed9c999f5b (plain)
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/*-------------------------------------------------------------------------
 *
 * s_lock.h
 *	   This file contains the implementation (if any) for spinlocks.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.64 1999/07/15 23:04:13 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */

/*
 *	 DESCRIPTION
 *		The public macros that must be provided are:
 *
 *		void S_INIT_LOCK(slock_t *lock)
 *
 *		void S_LOCK(slock_t *lock)
 *
 *		void S_UNLOCK(slock_t *lock)
 *
 *		void S_LOCK_FREE(slock_t *lock)
 *			Tests if the lock is free. Returns non-zero if free, 0 if locked.
 *
 *		The S_LOCK() macro	implements a primitive but still useful random
 *		backoff to avoid hordes of busywaiting lockers chewing CPU.
 *
 *		Effectively:
 *		void
 *		S_LOCK(slock_t *lock)
 *		{
 *			while (TAS(lock))
 *			{
 *			// back off the cpu for a semi-random short time
 *			}
 *		}
 *
 *		This implementation takes advantage of a tas function written
 *		(in assembly language) on machines that have a native test-and-set
 *		instruction. Alternative mutex implementations may also be used.
 *		This function is hidden under the TAS macro to allow substitutions.
 *
 *		#define TAS(lock) tas(lock)
 *		int tas(slock_t *lock)		// True if lock already set
 *
 *		There are default implementations for all these macros at the bottom
 *		of this file. Check if your platform can use these or needs to
 *		override them.
 *
 *	NOTES
 *		If none of this can be done, POSTGRES will default to using
 *		System V semaphores (and take a large performance hit -- around 40%
 *		of its time on a DS5000/240 is spent in semop(3)...).
 *
 *		AIX has a test-and-set but the recommended interface is the cs(3)
 *		system call.  This provides an 8-instruction (plus system call
 *		overhead) uninterruptible compare-and-set operation.  True
 *		spinlocks might be faster but using cs(3) still speeds up the
 *		regression test suite by about 25%.  I don't have an assembler
 *		manual for POWER in any case.
 *
 */
#if !defined(S_LOCK_H)
#define S_LOCK_H

#include "storage/ipc.h"

extern void s_lock_sleep(unsigned spin);

#if defined(HAS_TEST_AND_SET)


#if defined(__GNUC__)
/*************************************************************************
 * All the gcc inlines
 */

#if defined(__alpha__)
#define TAS(lock) tas(lock)
#define S_UNLOCK(lock) { __asm__("mb"); *(lock) = 0; }

static __inline__ int
tas(volatile slock_t *lock)
{
	register slock_t _res;

__asm__("    ldq   $0, %0              \n\
                 bne   $0, 3f          \n\
                 ldq_l $0, %0	           \n\
                 bne   $0, 3f          \n\
                 or    $31, 1, $0          \n\
                 stq_c $0, %0	           \n\
                 beq   $0, 2f              \n\
                 bis   $31, $31, %1        \n\
                 mb		                   \n\
                 jmp   $31, 4f	           \n\
              2: or    $31, 1, $0	       \n\
              3: bis   $0, $0, %1	       \n\
              4: nop      ": "=m"(*lock), "=r"(_res): :"0");

	return (int) _res;
}

#endif	 /* __alpha__ */



#if defined(__i386__)
#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register slock_t _res = 1;

__asm__("lock; xchgb %0,%1": "=q"(_res), "=m"(*lock):"0"(_res));
	return (int) _res;
}

#endif	 /* __i386__ */



#if defined(__arm32__)
#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
        register slock_t _res = 1;

__asm__("swpb %0, %0, [%3]": "=r"(_res), "=m"(*lock):"0"(_res), "r" (lock));
        return (int) _res;
}

#endif   /* __arm32__ */



#if defined(__sparc__)
#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register slock_t _res = 1;

	__asm__("ldstub [%2], %0" \
:			"=r"(_res), "=m"(*lock) \
:			"r"(lock));
	return (int) _res;
}

#endif	 /* __sparc__ */


#if defined(__mc68000__) && defined(__linux__)
#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register int rv;
	
	__asm__ __volatile__ (
		"tas %1; sne %0"
		: "=d" (rv), "=m"(*lock)
		: "1" (*lock)
		: "cc" );
	return rv;
}

#endif /* defined(__mc68000__) && defined(__linux__) */


#if defined(NEED_VAX_TAS_ASM)
/*
 * VAXen -- even multiprocessor ones
 * (thanks to Tom Ivar Helbekkmo)
 */
#define TAS(lock) tas(lock)

typedef unsigned char slock_t;

static __inline__ int
tas(volatile slock_t *lock)
{
	register	_res;

	__asm__("	movl $1, r0 \
			bbssi $0, (%1), 1 f \
			clrl r0 \
1:			movl r0, %0 "
:			"=r"(_res)			/* return value, in register */
:			"r"(lock)			/* argument, 'lock pointer', in register */
:			"r0");				/* inline code uses this register */
	return (int) _res;
}

#endif	 /* NEED_VAX_TAS_ASM */



#if defined(NEED_NS32K_TAS_ASM)
#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
  register _res;
  __asm__("sbitb 0, %0 \n\
	sfsd %1"
	: "=m"(*lock), "=r"(_res));
  return (int) _res; 
}

#endif  /* NEED_NS32K_TAS_ASM */



#else							/* __GNUC__ */
/***************************************************************************
 * All non gcc
 */

#if defined(__alpha__)
/*
 * OSF/1 (Alpha AXP)
 *
 * Note that slock_t on the Alpha AXP is msemaphore instead of char
 * (see storage/ipc.h).
 */
#define TAS(lock)	(msem_lock((lock), MSEM_IF_NOWAIT) < 0)
#define S_UNLOCK(lock)	msem_unlock((lock), 0)
#define S_INIT_LOCK(lock)	msem_init((lock), MSEM_UNLOCKED)
#define S_LOCK_FREE(lock)	(!(lock)->msem_state)
#endif	 /* __alpha__ */



#if defined(NEED_I386_TAS_ASM)
/* non gcc i386 based things */

#if defined(USE_UNIVEL_CC)
#define TAS(lock)	tas(lock)

asm int
tas(slock_t *s_lock)
{
/* UNIVEL wants %mem in column 1, so we don't pg_indent this file */
%mem s_lock
	pushl %ebx
	movl s_lock, %ebx
	movl $255, %eax
	lock
	xchgb %al, (%ebx)
	popl %ebx
}

#endif	 /* USE_UNIVEL_CC */

#endif	 /* NEED_I386_TAS_ASM */

#endif	 /* defined(__GNUC__) */



/*************************************************************************
 * These are the platforms that have common code for gcc and non-gcc
 */

#if defined(__hpux)
/*
 * HP-UX (PA-RISC)
 *
 * Note that slock_t on PA-RISC is a structure instead of char
 * (see include/port/hpux.h).
 *
 * a "set" slock_t has a single word cleared.  a "clear" slock_t has
 * all words set to non-zero. tas() in tas.s
 */

#define S_UNLOCK(lock) \
{ \
	volatile slock_t *lock_ = (volatile slock_t *) (lock); \
	lock_->sema[0] = lock_->sema[1] = lock_->sema[2] = lock_->sema[3] = -1; \
}

#define S_LOCK_FREE(lock)	( *(int *) (((long) (lock) + 15) & ~15) != 0)

#endif	 /* __hpux */


#if defined(__sgi)
/*
 * SGI IRIX 5
 * slock_t is defined as a unsigned long. We use the standard SGI
 * mutex API. 
 *
 * The following comment is left for historical reasons, but is probably
 * not a good idea since the mutex ABI is supported.
 *
 * This stuff may be supplemented in the future with Masato Kataoka's MIPS-II
 * assembly from his NECEWS SVR4 port, but we probably ought to retain this
 * for the R3000 chips out there.
 */
#include "mutex.h"
#define TAS(lock)	(test_and_set(lock,1))
#define S_UNLOCK(lock)	(test_then_and(lock,0))
#define S_INIT_LOCK(lock)	(test_then_and(lock,0))
#define S_LOCK_FREE(lock)	(test_then_add(lock,0) == 0)
#endif	 /* __sgi */

#if defined(sinix)
/*
 * SINIX / Reliant UNIX 
 * slock_t is defined as a struct abilock_t, which has a single unsigned long
 * member. (Basically same as SGI)
 *
 */
#define TAS(lock)	(!acquire_lock(lock))
#define S_UNLOCK(lock)	release_lock(lock)
#define S_INIT_LOCK(lock)	init_lock(lock)
#define S_LOCK_FREE(lock)	(stat_lock(lock) == UNLOCKED)
#endif	 /* sinix */
 

#if defined(_AIX)
/*
 * AIX (POWER)
 *
 * Note that slock_t on POWER/POWER2/PowerPC is int instead of char
 * (see storage/ipc.h).
 */
#define TAS(lock)	cs((int *) (lock), 0, 1)
#endif	 /* _AIX */


#if defined (nextstep)
/*
 * NEXTSTEP (mach)
 * slock_t is defined as a struct mutex.
 */

#define S_LOCK(lock)	mutex_lock(lock)
#define S_UNLOCK(lock)	mutex_unlock(lock)
#define S_INIT_LOCK(lock)	mutex_init(lock)
/* For Mach, we have to delve inside the entrails of `struct mutex'.  Ick! */
#define S_LOCK_FREE(alock)	((alock)->lock == 0)
#endif	 /* nextstep */




/****************************************************************************
 * Default Definitions - override these above as needed.
 */

#if !defined(S_LOCK)
extern void s_lock(volatile slock_t *lock, const char *file, const int line);

#define S_LOCK(lock) \
	do { \
		if (TAS((volatile slock_t *) lock)) \
			s_lock((volatile slock_t *) lock, __FILE__, __LINE__); \
	} while (0)
#endif	 /* S_LOCK */

#if !defined(S_LOCK_FREE)
#define S_LOCK_FREE(lock)	(*(lock) == 0)
#endif	 /* S_LOCK_FREE */

#if !defined(S_UNLOCK)
#define S_UNLOCK(lock)		(*(lock) = 0)
#endif	 /* S_UNLOCK */

#if !defined(S_INIT_LOCK)
#define S_INIT_LOCK(lock)	S_UNLOCK(lock)
#endif	 /* S_INIT_LOCK */

#if !defined(TAS)
int			tas(volatile slock_t *lock);		/* port/.../tas.s, or
												 * s_lock.c */

#define TAS(lock)		tas((volatile slock_t *) lock)
#endif	 /* TAS */

#endif	 /* HAS_TEST_AND_SET */
#endif	 /* S_LOCK_H */