Changeset 54dcab1 for libcfa/src/concurrency
- Timestamp:
- Nov 6, 2020, 11:25:30 AM (4 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 82a2fed, 836c9925
- Parents:
- 8fc652e0 (diff), 231b18f (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/concurrency/preemption.cfa
r8fc652e0 r54dcab1 10 10 // Created On : Mon Jun 5 14:20:42 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Aug 26 16:46:03 202013 // Update Count : 5 312 // Last Modified On : Fri Nov 6 07:42:13 2020 13 // Update Count : 54 14 14 // 15 15 … … 163 163 // Kernel Signal Tools 164 164 //============================================================================================= 165 // In a user-level threading system, there are handful of thread-local variables where this problem occurs on the ARM. 166 // 167 // For each kernel thread running user-level threads, there is a flag variable to indicate if interrupts are 168 // enabled/disabled for that kernel thread. Therefore, this variable is made thread local. 169 // 170 // For example, this code fragment sets the state of the "interrupt" variable in thread-local memory. 171 // 172 // _Thread_local volatile int interrupts; 173 // int main() { 174 // interrupts = 0; // disable interrupts } 175 // 176 // which generates the following code on the ARM 177 // 178 // (gdb) disassemble main 179 // Dump of assembler code for function main: 180 // 0x0000000000000610 <+0>: mrs x1, tpidr_el0 181 // 0x0000000000000614 <+4>: mov w0, #0x0 // #0 182 // 0x0000000000000618 <+8>: add x1, x1, #0x0, lsl #12 183 // 0x000000000000061c <+12>: add x1, x1, #0x10 184 // 0x0000000000000620 <+16>: str wzr, [x1] 185 // 0x0000000000000624 <+20>: ret 186 // 187 // The mrs moves a pointer from coprocessor register tpidr_el0 into register x1. Register w0 is set to 0. The two adds 188 // increase the TLS pointer with the displacement (offset) 0x10, which is the location in the TSL of variable 189 // "interrupts". Finally, 0 is stored into "interrupts" through the pointer in register x1 that points into the 190 // TSL. Now once x1 has the pointer to the location of the TSL for kernel thread N, it can be be preempted at a 191 // user-level and the user thread is put on the user-level ready-queue. When the preempted thread gets to the front of 192 // the user-level ready-queue it is run on kernel thread M. It now stores 0 into "interrupts" back on kernel thread N, 193 // turning off interrupt on the wrong kernel thread. 194 // 195 // On the x86, the following code is generated for the same code fragment. 196 // 197 // (gdb) disassemble main 198 // Dump of assembler code for function main: 199 // 0x0000000000400420 <+0>: movl $0x0,%fs:0xfffffffffffffffc 200 // 0x000000000040042c <+12>: xor %eax,%eax 201 // 0x000000000040042e <+14>: retq 202 // 203 // and there is base-displacement addressing used to atomically reset variable "interrupts" off of the TSL pointer in 204 // register "fs". 205 // 206 // Hence, the ARM has base-displacement address for the general purpose registers, BUT not to the coprocessor 207 // registers. As a result, generating the address for the write into variable "interrupts" is no longer atomic. 208 // 209 // Note this problem does NOT occur when just using multiple kernel threads because the preemption ALWAYS restarts the 210 // thread on the same kernel thread. 211 // 212 // The obvious question is why does ARM use a coprocessor register to store the TSL pointer given that coprocessor 213 // registers are second-class registers with respect to the instruction set. One possible answer is that they did not 214 // want to dedicate one of the general registers to hold the TLS pointer and there was a free coprocessor register 215 // available. 165 216 166 217 //---------- … … 196 247 return val; 197 248 } 198 199 // //----------200 // // Write data to the TLS block201 // // sadly it looses the type information and can only write 1 word at a time202 // // use with __builtin_offsetof203 // void __cfatls_set(uintptr_t offset, void * value) __attribute__((__noinline__));204 // void __cfatls_set(uintptr_t offset, void * value) {205 // // create a assembler label before206 // // marked as clobber all to avoid movement207 // asm volatile("__cfaasm_set_before:":::"memory");208 209 // // access tls as normal (except for type information)210 // *(void**)(offset + (uintptr_t)&my_tls) = value;211 212 // // create a assembler label after213 // // marked as clobber all to avoid movement214 // asm volatile("__cfaasm_set_after:":::"memory");215 // }216 217 // //----------218 // #include <stdio.h>219 // int main() {220 // // Get the information221 // // Must use inline assembly to get access to label222 // // C is annoying here because this could easily be a static const but "initializer element is not a compile-time constant"223 // // The big advantage of this approach is that there is 0 overhead for the read and writes function224 // void * __cfaasm_addr_get_before = ({ void * value; asm("movq $__cfaasm_get_before, %[v]\n\t" : [v]"=r"(value) ); value; });225 // void * __cfaasm_addr_get_after = ({ void * value; asm("movq $__cfaasm_get_after , %[v]\n\t" : [v]"=r"(value) ); value; });226 // void * __cfaasm_addr_set_before = ({ void * value; asm("movq $__cfaasm_set_before, %[v]\n\t" : [v]"=r"(value) ); value; });227 // void * __cfaasm_addr_set_after = ({ void * value; asm("movq $__cfaasm_set_after , %[v]\n\t" : [v]"=r"(value) ); value; });228 229 // printf("%p to %p\n", __cfaasm_addr_get_before, __cfaasm_addr_get_after);230 // printf("%p to %p\n", __cfaasm_addr_set_before, __cfaasm_addr_set_after);231 // return 0;232 // }233 234 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )235 249 236 250 extern "C" { … … 494 508 #endif 495 509 510 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; ) 511 496 512 // Context switch signal handler 497 513 // Receives SIGUSR1 signal and causes the current thread to yield
Note: See TracChangeset
for help on using the changeset viewer.