Changes in / [54dcab1:8fc652e0]


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/concurrency/preemption.cfa

    r54dcab1 r8fc652e0  
    1010// Created On       : Mon Jun 5 14:20:42 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Nov  6 07:42:13 2020
    13 // Update Count     : 54
     12// Last Modified On : Wed Aug 26 16:46:03 2020
     13// Update Count     : 53
    1414//
    1515
     
    163163// Kernel Signal Tools
    164164//=============================================================================================
    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.
    216165
    217166//----------
     
    247196        return val;
    248197}
     198
     199// //----------
     200// // Write data to the TLS block
     201// // sadly it looses the type information and can only write 1 word at a time
     202// // use with __builtin_offsetof
     203// void __cfatls_set(uintptr_t offset, void * value) __attribute__((__noinline__));
     204// void __cfatls_set(uintptr_t offset, void * value) {
     205//     // create a assembler label before
     206//     // marked as clobber all to avoid movement
     207//     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 after
     213//     // marked as clobber all to avoid movement
     214//     asm volatile("__cfaasm_set_after:":::"memory");
     215// }
     216
     217// //----------
     218// #include <stdio.h>
     219// int main() {
     220//     // Get the information
     221//     // Must use inline assembly to get access to label
     222//     // 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 function
     224//     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; )
    249235
    250236extern "C" {
     
    508494#endif
    509495
    510 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )
    511 
    512496// Context switch signal handler
    513497// Receives SIGUSR1 signal and causes the current thread to yield
Note: See TracChangeset for help on using the changeset viewer.