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