source: libcfa/src/bits/locks.hfa @ 25a1cb0

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 25a1cb0 was e67a82d, checked in by Peter A. Buhr <pabuhr@…>, 4 years ago

fix conflicts

  • Property mode set to 100644
File size: 6.4 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// bits/locks.hfa -- Fast internal locks.
8//
9// Author           : Thierry Delisle
10// Created On       : Tue Oct 31 15:14:38 2017
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Wed Aug 12 14:18:07 2020
13// Update Count     : 13
14//
15
16#pragma once
17
18#include "bits/debug.hfa"
19#include "bits/defs.hfa"
20#include <assert.h>
21
22#ifdef __cforall
23        extern "C" {
24                #include <pthread.h>
25        }
26#endif
27
28// pause to prevent excess processor bus usage
29#if defined( __i386 ) || defined( __x86_64 )
30        #define Pause() __asm__ __volatile__ ( "pause" : : : )
31#elif defined( __ARM_ARCH )
32        #define Pause() __asm__ __volatile__ ( "YIELD" : : : )
33#else
34        #error unsupported architecture
35#endif
36
37struct __spinlock_t {
38        // Wrap in struct to prevent false sharing with debug info
39        volatile bool lock;
40        #ifdef __CFA_DEBUG__
41                // previous function to acquire the lock
42                const char * prev_name;
43                // previous thread to acquire the lock
44                void* prev_thrd;
45        #endif
46};
47
48#ifdef __cforall
49        extern "C" {
50                extern void disable_interrupts() OPTIONAL_THREAD;
51                extern void enable_interrupts_noPoll() OPTIONAL_THREAD;
52
53                #ifdef __CFA_DEBUG__
54                        void __cfaabi_dbg_record_lock(__spinlock_t & this, const char prev_name[]);
55                #else
56                        #define __cfaabi_dbg_record_lock(x, y)
57                #endif
58        }
59
60        static inline void ?{}( __spinlock_t & this ) {
61                this.lock = 0;
62        }
63
64        // Lock the spinlock, return false if already acquired
65        static inline bool try_lock  ( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
66                disable_interrupts();
67                bool result = (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0);
68                if( result ) {
69                        __cfaabi_dbg_record_lock( this, caller );
70                } else {
71                        enable_interrupts_noPoll();
72                }
73                return result;
74        }
75
76        // Lock the spinlock, spin if already acquired
77        static inline void lock( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
78                #ifndef NOEXPBACK
79                        enum { SPIN_START = 4, SPIN_END = 64 * 1024, };
80                        unsigned int spin = SPIN_START;
81                #endif
82
83                disable_interrupts();
84                for ( unsigned int i = 1;; i += 1 ) {
85                        if ( (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0) ) break;
86                        #ifndef NOEXPBACK
87                                // exponential spin
88                                for ( volatile unsigned int s = 0; s < spin; s += 1 ) Pause();
89
90                                // slowly increase by powers of 2
91                                if ( i % 64 == 0 ) spin += spin;
92
93                                // prevent overflow
94                                if ( spin > SPIN_END ) spin = SPIN_START;
95                        #else
96                                Pause();
97                        #endif
98                }
99                __cfaabi_dbg_record_lock( this, caller );
100        }
101
102        static inline void unlock( __spinlock_t & this ) {
103                __atomic_clear( &this.lock, __ATOMIC_RELEASE );
104                enable_interrupts_noPoll();
105        }
106
107
108        #ifdef __CFA_WITH_VERIFY__
109                extern bool __cfaabi_dbg_in_kernel();
110        #endif
111
112        extern "C" {
113                char * strerror(int);
114        }
115        #define CHECKED(x) { int err = x; if( err != 0 ) abort("KERNEL ERROR: Operation \"" #x "\" return error %d - %s\n", err, strerror(err)); }
116
117        struct __bin_sem_t {
118                pthread_mutex_t         lock;
119                pthread_cond_t          cond;
120                int                     val;
121        };
122
123        static inline void ?{}(__bin_sem_t & this) with( this ) {
124                // Create the mutex with error checking
125                pthread_mutexattr_t mattr;
126                pthread_mutexattr_init( &mattr );
127                pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP);
128                pthread_mutex_init(&lock, &mattr);
129
130                pthread_cond_init (&cond, (const pthread_condattr_t *)0p);  // workaround trac#208: cast should not be required
131                val = 0;
132        }
133
134        static inline void ^?{}(__bin_sem_t & this) with( this ) {
135                CHECKED( pthread_mutex_destroy(&lock) );
136                CHECKED( pthread_cond_destroy (&cond) );
137        }
138
139        static inline void wait(__bin_sem_t & this) with( this ) {
140                verify(__cfaabi_dbg_in_kernel());
141                CHECKED( pthread_mutex_lock(&lock) );
142                        while(val < 1) {
143                                pthread_cond_wait(&cond, &lock);
144                        }
145                        val -= 1;
146                CHECKED( pthread_mutex_unlock(&lock) );
147        }
148
149        static inline bool post(__bin_sem_t & this) with( this ) {
150                bool needs_signal = false;
151
152                CHECKED( pthread_mutex_lock(&lock) );
153                        if(val < 1) {
154                                val += 1;
155                                pthread_cond_signal(&cond);
156                                needs_signal = true;
157                        }
158                CHECKED( pthread_mutex_unlock(&lock) );
159
160                return needs_signal;
161        }
162
163        #undef CHECKED
164
165        struct $thread;
166        extern void park( __cfaabi_dbg_ctx_param );
167        extern void unpark( struct $thread * this __cfaabi_dbg_ctx_param2 );
168        static inline struct $thread * active_thread ();
169
170        // Semaphore which only supports a single thread
171        struct single_sem {
172                struct $thread * volatile ptr;
173        };
174
175        static inline {
176                void  ?{}(single_sem & this) {
177                        this.ptr = 0p;
178                }
179
180                void ^?{}(single_sem & this) {}
181
182                bool wait(single_sem & this) {
183                        for() {
184                                struct $thread * expected = this.ptr;
185                                if(expected == 1p) {
186                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
187                                                return false;
188                                        }
189                                }
190                                else {
191                                        /* paranoid */ verify( expected == 0p );
192                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
193                                                park( __cfaabi_dbg_ctx );
194                                                return true;
195                                        }
196                                }
197
198                        }
199                }
200
201                bool post(single_sem & this) {
202                        for() {
203                                struct $thread * expected = this.ptr;
204                                if(expected == 1p) return false;
205                                if(expected == 0p) {
206                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 1p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
207                                                return false;
208                                        }
209                                }
210                                else {
211                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
212                                                unpark( expected __cfaabi_dbg_ctx2 );
213                                                return true;
214                                        }
215                                }
216                        }
217                }
218        }
219
220        // Semaphore which only supports a single thread and one post
221        // Semaphore which only supports a single thread
222        struct oneshot {
223                struct $thread * volatile ptr;
224        };
225
226        static inline {
227                void  ?{}(oneshot & this) {
228                        this.ptr = 0p;
229                }
230
231                void ^?{}(oneshot & this) {}
232
233                bool wait(oneshot & this) {
234                        for() {
235                                struct $thread * expected = this.ptr;
236                                if(expected == 1p) return false;
237                                /* paranoid */ verify( expected == 0p );
238                                if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
239                                        park( __cfaabi_dbg_ctx );
240                                        /* paranoid */ verify( this.ptr == 1p );
241                                        return true;
242                                }
243                        }
244                }
245
246                bool post(oneshot & this) {
247                        struct $thread * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
248                        if( got == 0p ) return false;
249                        unpark( got __cfaabi_dbg_ctx2 );
250                        return true;
251                }
252        }
253#endif
Note: See TracBrowser for help on using the repository browser.