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-2019, 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/shmem.h" |
27 | #include "storage/spin.h" |
28 | |
29 | |
30 | #ifndef HAVE_SPINLOCKS |
31 | PGSemaphore *SpinlockSemaArray; |
32 | #endif |
33 | |
34 | /* |
35 | * Report the amount of shared memory needed to store semaphores for spinlock |
36 | * support. |
37 | */ |
38 | Size |
39 | SpinlockSemaSize(void) |
40 | { |
41 | return SpinlockSemas() * sizeof(PGSemaphore); |
42 | } |
43 | |
44 | #ifdef HAVE_SPINLOCKS |
45 | |
46 | /* |
47 | * Report number of semaphores needed to support spinlocks. |
48 | */ |
49 | int |
50 | SpinlockSemas(void) |
51 | { |
52 | return 0; |
53 | } |
54 | #else /* !HAVE_SPINLOCKS */ |
55 | |
56 | /* |
57 | * No TAS, so spinlocks are implemented as PGSemaphores. |
58 | */ |
59 | |
60 | |
61 | /* |
62 | * Report number of semaphores needed to support spinlocks. |
63 | */ |
64 | int |
65 | SpinlockSemas(void) |
66 | { |
67 | return NUM_SPINLOCK_SEMAPHORES + NUM_ATOMICS_SEMAPHORES; |
68 | } |
69 | |
70 | /* |
71 | * Initialize spinlock emulation. |
72 | * |
73 | * This must be called after PGReserveSemaphores(). |
74 | */ |
75 | void |
76 | SpinlockSemaInit(void) |
77 | { |
78 | PGSemaphore *spinsemas; |
79 | int nsemas = SpinlockSemas(); |
80 | int i; |
81 | |
82 | /* |
83 | * We must use ShmemAllocUnlocked(), since the spinlock protecting |
84 | * ShmemAlloc() obviously can't be ready yet. |
85 | */ |
86 | spinsemas = (PGSemaphore *) ShmemAllocUnlocked(SpinlockSemaSize()); |
87 | for (i = 0; i < nsemas; ++i) |
88 | spinsemas[i] = PGSemaphoreCreate(); |
89 | SpinlockSemaArray = spinsemas; |
90 | } |
91 | |
92 | /* |
93 | * s_lock.h hardware-spinlock emulation using semaphores |
94 | * |
95 | * We map all spinlocks onto a set of NUM_SPINLOCK_SEMAPHORES semaphores. |
96 | * It's okay to map multiple spinlocks onto one semaphore because no process |
97 | * should ever hold more than one at a time. We just need enough semaphores |
98 | * so that we aren't adding too much extra contention from that. |
99 | * |
100 | * slock_t is just an int for this implementation; it holds the spinlock |
101 | * number from 1..NUM_SPINLOCK_SEMAPHORES. We intentionally ensure that 0 |
102 | * is not a valid value, so that testing with this code can help find |
103 | * failures to initialize spinlocks. |
104 | */ |
105 | |
106 | void |
107 | s_init_lock_sema(volatile slock_t *lock, bool nested) |
108 | { |
109 | static int counter = 0; |
110 | |
111 | *lock = ((++counter) % NUM_SPINLOCK_SEMAPHORES) + 1; |
112 | } |
113 | |
114 | void |
115 | s_unlock_sema(volatile slock_t *lock) |
116 | { |
117 | int lockndx = *lock; |
118 | |
119 | if (lockndx <= 0 || lockndx > NUM_SPINLOCK_SEMAPHORES) |
120 | elog(ERROR, "invalid spinlock number: %d" , lockndx); |
121 | PGSemaphoreUnlock(SpinlockSemaArray[lockndx - 1]); |
122 | } |
123 | |
124 | bool |
125 | s_lock_free_sema(volatile slock_t *lock) |
126 | { |
127 | /* We don't currently use S_LOCK_FREE anyway */ |
128 | elog(ERROR, "spin.c does not support S_LOCK_FREE()" ); |
129 | return false; |
130 | } |
131 | |
132 | int |
133 | tas_sema(volatile slock_t *lock) |
134 | { |
135 | int lockndx = *lock; |
136 | |
137 | if (lockndx <= 0 || lockndx > NUM_SPINLOCK_SEMAPHORES) |
138 | elog(ERROR, "invalid spinlock number: %d" , lockndx); |
139 | /* Note that TAS macros return 0 if *success* */ |
140 | return !PGSemaphoreTryLock(SpinlockSemaArray[lockndx - 1]); |
141 | } |
142 | |
143 | #endif /* !HAVE_SPINLOCKS */ |
144 | |