1 /*-------------------------------------------------------------------------
2  *
3  * spin.c
4  *	   Hardware-independent implementation of spinlocks.
5  *
6  *
7  * For machines that have test-and-set (TAS) instructions, s_lock.h/.c
8  * define the spinlock implementation.  This file contains only a stub
9  * implementation for spinlocks using PGSemaphores.  Unless semaphores
10  * are implemented in a way that doesn't involve a kernel call, this
11  * is too slow to be very useful :-(
12  *
13  *
14  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
15  * Portions Copyright (c) 1994, Regents of the University of California
16  *
17  *
18  * IDENTIFICATION
19  *	  src/backend/storage/lmgr/spin.c
20  *
21  *-------------------------------------------------------------------------
22  */
23 #include "postgres.h"
24 
25 #include "storage/pg_sema.h"
26 #include "storage/spin.h"
27 
28 
29 #ifndef HAVE_SPINLOCKS
30 
31 /*
32  * No TAS, so spinlocks are implemented as PGSemaphores.
33  */
34 
35 #ifndef HAVE_ATOMICS
36 #define NUM_EMULATION_SEMAPHORES (NUM_SPINLOCK_SEMAPHORES + NUM_ATOMICS_SEMAPHORES)
37 #else
38 #define NUM_EMULATION_SEMAPHORES (NUM_SPINLOCK_SEMAPHORES)
39 #endif /* DISABLE_ATOMICS */
40 
41 PGSemaphore SpinlockSemaArray;
42 
43 #else							/* !HAVE_SPINLOCKS */
44 
45 #define NUM_EMULATION_SEMAPHORES 0
46 
47 #endif							/* HAVE_SPINLOCKS */
48 
49 /*
50  * Report the amount of shared memory needed to store semaphores for spinlock
51  * support.
52  */
53 Size
SpinlockSemaSize(void)54 SpinlockSemaSize(void)
55 {
56 	return NUM_EMULATION_SEMAPHORES * sizeof(PGSemaphoreData);
57 }
58 
59 /*
60  * Report number of semaphores needed to support spinlocks.
61  */
62 int
SpinlockSemas(void)63 SpinlockSemas(void)
64 {
65 	return NUM_EMULATION_SEMAPHORES;
66 }
67 
68 #ifndef HAVE_SPINLOCKS
69 
70 /*
71  * Initialize semaphores.
72  */
73 extern void
SpinlockSemaInit(PGSemaphore spinsemas)74 SpinlockSemaInit(PGSemaphore spinsemas)
75 {
76 	int			i;
77 	int			nsemas = SpinlockSemas();
78 
79 	for (i = 0; i < nsemas; ++i)
80 		PGSemaphoreCreate(&spinsemas[i]);
81 	SpinlockSemaArray = spinsemas;
82 }
83 
84 /*
85  * s_lock.h hardware-spinlock emulation using semaphores
86  *
87  * We map all spinlocks onto NUM_EMULATION_SEMAPHORES semaphores.  It's okay to
88  * map multiple spinlocks onto one semaphore because no process should ever
89  * hold more than one at a time.  We just need enough semaphores so that we
90  * aren't adding too much extra contention from that.
91  *
92  * There is one exception to the restriction of only holding one spinlock at a
93  * time, which is that it's ok if emulated atomic operations are nested inside
94  * spinlocks. To avoid the danger of spinlocks and atomic using the same sema,
95  * we make sure "normal" spinlocks and atomics backed by spinlocks use
96  * distinct semaphores (see the nested argument to s_init_lock_sema).
97  *
98  * slock_t is just an int for this implementation; it holds the spinlock
99  * number from 1..NUM_EMULATION_SEMAPHORES.  We intentionally ensure that 0
100  * is not a valid value, so that testing with this code can help find
101  * failures to initialize spinlocks.
102  */
103 
104 static inline void
s_check_valid(int lockndx)105 s_check_valid(int lockndx)
106 {
107 	if (lockndx <= 0 || lockndx > NUM_EMULATION_SEMAPHORES)
108 		elog(ERROR, "invalid spinlock number: %d", lockndx);
109 }
110 
111 void
s_init_lock_sema(volatile slock_t * lock,bool nested)112 s_init_lock_sema(volatile slock_t *lock, bool nested)
113 {
114 	static uint32 counter = 0;
115 	uint32		offset;
116 	uint32		sema_total;
117 	uint32		idx;
118 
119 	if (nested)
120 	{
121 		/*
122 		 * To allow nesting atomics inside spinlocked sections, use a
123 		 * different spinlock. See comment above.
124 		 */
125 		offset = 1 + NUM_SPINLOCK_SEMAPHORES;
126 		sema_total = NUM_ATOMICS_SEMAPHORES;
127 	}
128 	else
129 	{
130 		offset = 1;
131 		sema_total = NUM_SPINLOCK_SEMAPHORES;
132 	}
133 
134 	idx = (counter++ % sema_total) + offset;
135 
136 	/* double check we did things correctly */
137 	s_check_valid(idx);
138 
139 	*lock = idx;
140 }
141 
142 void
s_unlock_sema(volatile slock_t * lock)143 s_unlock_sema(volatile slock_t *lock)
144 {
145 	int			lockndx = *lock;
146 
147 	s_check_valid(lockndx);
148 
149 	PGSemaphoreUnlock(&SpinlockSemaArray[lockndx - 1]);
150 }
151 
152 bool
s_lock_free_sema(volatile slock_t * lock)153 s_lock_free_sema(volatile slock_t *lock)
154 {
155 	/* We don't currently use S_LOCK_FREE anyway */
156 	elog(ERROR, "spin.c does not support S_LOCK_FREE()");
157 	return false;
158 }
159 
160 int
tas_sema(volatile slock_t * lock)161 tas_sema(volatile slock_t *lock)
162 {
163 	int			lockndx = *lock;
164 
165 	s_check_valid(lockndx);
166 
167 	/* Note that TAS macros return 0 if *success* */
168 	return !PGSemaphoreTryLock(&SpinlockSemaArray[lockndx - 1]);
169 }
170 
171 #endif   /* !HAVE_SPINLOCKS */
172