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
31PGSemaphore *SpinlockSemaArray;
32#endif
33
34/*
35 * Report the amount of shared memory needed to store semaphores for spinlock
36 * support.
37 */
38Size
39SpinlockSemaSize(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 */
49int
50SpinlockSemas(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 */
64int
65SpinlockSemas(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 */
75void
76SpinlockSemaInit(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
106void
107s_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
114void
115s_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
124bool
125s_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
132int
133tas_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