1 /* 2 * Copyright 2015 Intel 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24 #ifndef _SIMPLE_MTX_H 25 #define _SIMPLE_MTX_H 26 27 #include "util/futex.h" 28 29 #include "c11/threads.h" 30 31 #if defined(__GNUC__) && defined(HAVE_LINUX_FUTEX_H) 32 33 /* mtx_t - Fast, simple mutex 34 * 35 * While modern pthread mutexes are very fast (implemented using futex), they 36 * still incur a call to an external DSO and overhead of the generality and 37 * features of pthread mutexes. Most mutexes in mesa only needs lock/unlock, 38 * and the idea here is that we can inline the atomic operation and make the 39 * fast case just two intructions. Mutexes are subtle and finicky to 40 * implement, so we carefully copy the implementation from Ulrich Dreppers 41 * well-written and well-reviewed paper: 42 * 43 * "Futexes Are Tricky" 44 * http://www.akkadia.org/drepper/futex.pdf 45 * 46 * We implement "mutex3", which gives us a mutex that has no syscalls on 47 * uncontended lock or unlock. Further, the uncontended case boils down to a 48 * locked cmpxchg and an untaken branch, the uncontended unlock is just a 49 * locked decr and an untaken branch. We use __builtin_expect() to indicate 50 * that contention is unlikely so that gcc will put the contention code out of 51 * the main code flow. 52 * 53 * A fast mutex only supports lock/unlock, can't be recursive or used with 54 * condition variables. 55 */ 56 57 typedef struct { 58 uint32_t val; 59 } simple_mtx_t; 60 61 #define _SIMPLE_MTX_INITIALIZER_NP { 0 } 62 63 static inline void 64 simple_mtx_init(simple_mtx_t *mtx, MAYBE_UNUSED int type) 65 { 66 assert(type == mtx_plain); 67 68 mtx->val = 0; 69 } 70 71 static inline void 72 simple_mtx_destroy(UNUSED simple_mtx_t *mtx) 73 { 74 } 75 76 static inline void 77 simple_mtx_lock(simple_mtx_t *mtx) 78 { 79 uint32_t c; 80 81 c = __sync_val_compare_and_swap(&mtx->val, 0, 1); 82 if (__builtin_expect(c != 0, 0)) { 83 if (c != 2) 84 c = __sync_lock_test_and_set(&mtx->val, 2); 85 while (c != 0) { 86 futex_wait(&mtx->val, 2, NULL); 87 c = __sync_lock_test_and_set(&mtx->val, 2); 88 } 89 } 90 } 91 92 static inline void 93 simple_mtx_unlock(simple_mtx_t *mtx) 94 { 95 uint32_t c; 96 97 c = __sync_fetch_and_sub(&mtx->val, 1); 98 if (__builtin_expect(c != 1, 0)) { 99 mtx->val = 0; 100 futex_wake(&mtx->val, 1); 101 } 102 } 103 104 #else 105 106 typedef mtx_t simple_mtx_t; 107 108 #define _SIMPLE_MTX_INITIALIZER_NP _MTX_INITIALIZER_NP 109 110 static inline void 111 simple_mtx_init(simple_mtx_t *mtx, int type) 112 { 113 mtx_init(mtx, type); 114 } 115 116 static inline void 117 simple_mtx_destroy(simple_mtx_t *mtx) 118 { 119 mtx_destroy(mtx); 120 } 121 122 static inline void 123 simple_mtx_lock(simple_mtx_t *mtx) 124 { 125 mtx_lock(mtx); 126 } 127 128 static inline void 129 simple_mtx_unlock(simple_mtx_t *mtx) 130 { 131 mtx_unlock(mtx); 132 } 133 134 #endif 135 136 #endif 137