source: src/libcfa/concurrency/preemption.c @ 1c273d0

aaron-thesisarm-ehcleanup-dtorsdeferred_resndemanglerenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprnew-envno_listpersistent-indexerpthread-emulationqualifiedEnumresolv-newwith_gc
Last change on this file since 1c273d0 was 1c273d0, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

preemption works for threads

  • Property mode set to 100644
File size: 11.4 KB
Line 
1//                              -*- Mode: CFA -*-
2//
3// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
4//
5// The contents of this file are covered under the licence agreement in the
6// file "LICENCE" distributed with Cforall.
7//
8// signal.c --
9//
10// Author           : Thierry Delisle
11// Created On       : Mon Jun 5 14:20:42 2017
12// Last Modified By : Thierry Delisle
13// Last Modified On : --
14// Update Count     : 0
15//
16
17#include "preemption.h"
18
19
20extern "C" {
21#include <errno.h>
22#include <execinfo.h>
23#define __USE_GNU
24#include <signal.h>
25#undef __USE_GNU
26#include <stdio.h>
27#include <string.h>
28#include <unistd.h>
29}
30
31
32#ifdef __USE_STREAM__
33#include "fstream"
34#endif
35#include "libhdr.h"
36
37#define __CFA_DEFAULT_PREEMPTION__ 10000
38
39__attribute__((weak)) unsigned int default_preemption() {
40        return __CFA_DEFAULT_PREEMPTION__;
41}
42
43#define __CFA_SIGCXT__ ucontext_t *
44#define __CFA_SIGPARMS__ __attribute__((unused)) int sig, __attribute__((unused)) siginfo_t *sfp, __attribute__((unused)) __CFA_SIGCXT__ cxt
45
46static void preempt( processor   * this );
47static void timeout( thread_desc * this );
48
49void sigHandler_ctxSwitch( __CFA_SIGPARMS__ );
50void sigHandler_alarm    ( __CFA_SIGPARMS__ );
51void sigHandler_segv     ( __CFA_SIGPARMS__ );
52void sigHandler_abort    ( __CFA_SIGPARMS__ );
53
54static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags );
55
56//=============================================================================================
57// Kernel Preemption logic
58//=============================================================================================
59
60void kernel_start_preemption() {
61        LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n");
62        __kernel_sigaction( SIGUSR1, sigHandler_ctxSwitch, SA_SIGINFO );
63        __kernel_sigaction( SIGALRM, sigHandler_alarm    , SA_SIGINFO );
64        __kernel_sigaction( SIGSEGV, sigHandler_segv     , SA_SIGINFO );
65        __kernel_sigaction( SIGBUS , sigHandler_segv     , SA_SIGINFO );
66        // __kernel_sigaction( SIGABRT, sigHandler_abort    , SA_SIGINFO );
67}
68
69void kernel_stop_preemption() {
70        //Block all signals, we are no longer in a position to handle them
71        sigset_t mask;
72        sigfillset( &mask );
73        sigprocmask( SIG_BLOCK, &mask, NULL );
74        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n");
75
76        // assert( !systemProcessor->alarms.head );
77        // assert( systemProcessor->alarms.tail == &systemProcessor->alarms.head );
78}
79
80LIB_DEBUG_DO( bool validate( alarm_list_t * this ); )
81
82void tick_preemption() {
83        // LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Ticking preemption\n" );
84
85        alarm_list_t * alarms = &systemProcessor->alarms;
86        __cfa_time_t currtime = __kernel_get_time();
87        while( alarms->head && alarms->head->alarm < currtime ) {
88                alarm_node_t * node = pop(alarms);
89                // LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ticking %p\n", node );
90
91                if( node->kernel_alarm ) {
92                        preempt( node->proc );
93                }
94                else {
95                        timeout( node->thrd );
96                }
97
98                verify( validate( alarms ) );
99
100                if( node->period > 0 ) {
101                        node->alarm = currtime + node->period;
102                        insert( alarms, node );
103                }
104                else {
105                        node->set = false;
106                }
107        }
108
109        if( alarms->head ) {
110                __kernel_set_timer( alarms->head->alarm - currtime );
111        }
112
113        verify( validate( alarms ) );
114        // LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ticking preemption done\n" );
115}
116
117void update_preemption( processor * this, __cfa_time_t duration ) {
118        LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Processor : %p updating preemption to %lu\n", this, duration );
119
120        alarm_node_t * alarm = this->preemption_alarm;
121        duration *= 1000;
122
123        // Alarms need to be enabled
124        if ( duration > 0 && !alarm->set ) {
125                alarm->alarm = __kernel_get_time() + duration;
126                alarm->period = duration;
127                register_self( alarm );
128        }
129        // Zero duraction but alarm is set
130        else if ( duration == 0 && alarm->set ) {
131                unregister_self( alarm );
132                alarm->alarm = 0;
133                alarm->period = 0;
134        }
135        // If alarm is different from previous, change it
136        else if ( duration > 0 && alarm->period != duration ) {
137                unregister_self( alarm );
138                alarm->alarm = __kernel_get_time() + duration;
139                alarm->period = duration;
140                register_self( alarm );
141        }
142}
143
144void ?{}( preemption_scope * this, processor * proc ) {
145        (&this->alarm){ proc };
146        this->proc = proc;
147        this->proc->preemption_alarm = &this->alarm;
148        update_preemption( this->proc, this->proc->preemption );
149}
150
151void ^?{}( preemption_scope * this ) {
152        disable_interrupts();
153
154        update_preemption( this->proc, 0 );
155}
156
157//=============================================================================================
158// Kernel Signal logic
159//=============================================================================================
160
161extern "C" {
162        void disable_interrupts() {
163                __attribute__((unused)) unsigned short new_val = __atomic_add_fetch_2( &disable_preempt_count, 1, __ATOMIC_SEQ_CST );
164                verify( new_val < (unsigned short)65_000 );
165                verify( new_val != (unsigned short) 0 );
166        }
167
168        void enable_interrupts_noRF() {
169                unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
170                verify( prev != (unsigned short) 0 );
171        }
172
173        void enable_interrupts( const char * func ) {
174                processor * proc   = this_processor;
175                thread_desc * thrd = this_thread;
176                unsigned short prev = __atomic_fetch_add_2( &disable_preempt_count, -1, __ATOMIC_SEQ_CST );
177                verify( prev != (unsigned short) 0 );
178                if( prev == 1 && proc->pending_preemption ) {
179                        proc->pending_preemption = false;
180                        LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Executing deferred CtxSwitch on %p\n", this_processor );
181                        BlockInternal( thrd );
182                        LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Executing deferred back\n" );
183                }
184
185                proc->last_enable = func;
186        }
187}
188
189static inline void signal_unblock( int sig ) {
190        LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Processor : %p unblocking sig %i\n", this_processor, sig );
191
192        sigset_t mask;
193        sigemptyset( &mask );
194        sigaddset( &mask, sig );
195
196        if ( sigprocmask( SIG_UNBLOCK, &mask, NULL ) == -1 ) {
197            abortf( "internal error, sigprocmask" );
198        } // if
199}
200
201static inline bool preemption_ready() {
202        return disable_preempt_count == 0;
203}
204
205static inline void defer_ctxSwitch() {
206        this_processor->pending_preemption = true;
207}
208
209static inline void defer_alarm() {
210        systemProcessor->pending_alarm = true;
211}
212
213extern "C" {
214        __attribute__((noinline)) void __debug_break() {
215                pthread_kill( pthread_self(), SIGTRAP );
216        }
217}
218
219void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) {
220        LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Ctx Switch IRH %p running %p @ %p\n", this_processor, this_thread, (void *)(cxt->uc_mcontext.gregs[REG_RIP]) );
221
222        if( preemption_ready() ) {
223                LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ctx Switch IRH : Blocking thread %p on %p\n", this_thread, this_processor );
224                signal_unblock( SIGUSR1 );
225                BlockInternal( (thread_desc*)this_thread );
226                LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ctx Switch IRH : Back\n\n");
227        }
228        else {
229                LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Ctx Switch IRH : Defering\n" );
230                defer_ctxSwitch();
231                signal_unblock( SIGUSR1 );
232        }
233}
234
235void sigHandler_alarm( __CFA_SIGPARMS__ ) {
236        LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "\nAlarm IRH %p running %p @ %p\n", this_processor, this_thread, (void *)(cxt->uc_mcontext.gregs[REG_RIP]) );
237
238        // if( ((intptr_t)cxt->uc_mcontext.gregs[REG_RIP]) > 0xFFFFFF ) __debug_break();
239
240        if( try_lock( &systemProcessor->alarm_lock, __PRETTY_FUNCTION__ ) ) {
241                tick_preemption();
242                unlock( &systemProcessor->alarm_lock );
243        }
244        else {
245                defer_alarm();
246        }
247
248        signal_unblock( SIGALRM );
249
250        if( preemption_ready() && this_processor->pending_preemption ) {
251                LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Alarm IRH : Blocking thread %p on %p\n", this_thread, this_processor );
252                this_processor->pending_preemption = false;
253                BlockInternal( (thread_desc*)this_thread );
254                LIB_DEBUG_PRINT_BUFFER_LOCAL( STDERR_FILENO, "Alarm Switch IRH : Back\n\n");
255        }
256}
257
258static void preempt( processor * this ) {
259        // LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO, "Processor : signalling %p\n", this );
260
261        if( this != systemProcessor ) {
262                pthread_kill( this->kernel_thread, SIGUSR1 );
263        }
264        else {
265                defer_ctxSwitch();
266        }
267}
268
269static void timeout( thread_desc * this ) {
270        //TODO : implement waking threads
271}
272
273static void __kernel_sigaction( int sig, void (*handler)(__CFA_SIGPARMS__), int flags ) {
274        struct sigaction act;
275
276        act.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
277        act.sa_flags = flags;
278
279        // disabled during signal handler
280        sigemptyset( &act.sa_mask );
281        sigaddset( &act.sa_mask, sig );
282
283        if ( sigaction( sig, &act, NULL ) == -1 ) {
284                LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO,
285                        " __kernel_sigaction( sig:%d, handler:%p, flags:%d ), problem installing signal handler, error(%d) %s.\n",
286                        sig, handler, flags, errno, strerror( errno )
287                );
288                _exit( EXIT_FAILURE );
289        }
290}
291
292typedef void (*sa_handler_t)(int);
293
294static void __kernel_sigdefault( int sig ) {
295        struct sigaction act;
296
297        // act.sa_handler = SIG_DFL;
298        act.sa_flags = 0;
299        sigemptyset( &act.sa_mask );
300
301        if ( sigaction( sig, &act, NULL ) == -1 ) {
302                LIB_DEBUG_PRINT_BUFFER_DECL( STDERR_FILENO,
303                        " __kernel_sigdefault( sig:%d ), problem reseting signal handler, error(%d) %s.\n",
304                        sig, errno, strerror( errno )
305                );
306                _exit( EXIT_FAILURE );
307        }
308}
309
310//=============================================================================================
311// Terminating Signals logic
312//=============================================================================================
313
314LIB_DEBUG_DO(
315        static void __kernel_backtrace( int start ) {
316                // skip first N stack frames
317
318                enum { Frames = 50 };
319                void * array[Frames];
320                int size = backtrace( array, Frames );
321                char ** messages = backtrace_symbols( array, size );
322
323                // find executable name
324                *index( messages[0], '(' ) = '\0';
325                #ifdef __USE_STREAM__
326                serr | "Stack back trace for:" | messages[0] | endl;
327                #else
328                fprintf( stderr, "Stack back trace for: %s\n", messages[0]);
329                #endif
330
331                // skip last 2 stack frames after main
332                for ( int i = start; i < size && messages != NULL; i += 1 ) {
333                        char * name = NULL;
334                        char * offset_begin = NULL;
335                        char * offset_end = NULL;
336
337                        for ( char *p = messages[i]; *p; ++p ) {
338                                // find parantheses and +offset
339                                if ( *p == '(' ) {
340                                        name = p;
341                                }
342                                else if ( *p == '+' ) {
343                                        offset_begin = p;
344                                }
345                                else if ( *p == ')' ) {
346                                        offset_end = p;
347                                        break;
348                                }
349                        }
350
351                        // if line contains symbol print it
352                        int frameNo = i - start;
353                        if ( name && offset_begin && offset_end && name < offset_begin ) {
354                                // delimit strings
355                                *name++ = '\0';
356                                *offset_begin++ = '\0';
357                                *offset_end++ = '\0';
358
359                                #ifdef __USE_STREAM__
360                                serr    | "("  | frameNo | ")" | messages[i] | ":"
361                                        | name | "+" | offset_begin | offset_end | endl;
362                                #else
363                                fprintf( stderr, "(%i) %s : %s + %s %s\n", frameNo, messages[i], name, offset_begin, offset_end);
364                                #endif
365                        }
366                        // otherwise, print the whole line
367                        else {
368                                #ifdef __USE_STREAM__
369                                serr | "(" | frameNo | ")" | messages[i] | endl;
370                                #else
371                                fprintf( stderr, "(%i) %s\n", frameNo, messages[i] );
372                                #endif
373                        }
374                }
375
376                free( messages );
377        }
378)
379
380void sigHandler_segv( __CFA_SIGPARMS__ ) {
381        LIB_DEBUG_DO(
382                #ifdef __USE_STREAM__
383                serr    | "*CFA runtime error* program cfa-cpp terminated with"
384                        | (sig == SIGSEGV ? "segment fault." : "bus error.")
385                        | endl;
386                #else
387                fprintf( stderr, "*CFA runtime error* program cfa-cpp terminated with %s\n", sig == SIGSEGV ? "segment fault." : "bus error." );
388                #endif
389
390                // skip first 2 stack frames
391                __kernel_backtrace( 1 );
392        )
393        exit( EXIT_FAILURE );
394}
395
396// void sigHandler_abort( __CFA_SIGPARMS__ ) {
397//      // skip first 6 stack frames
398//      LIB_DEBUG_DO( __kernel_backtrace( 6 ); )
399
400//      // reset default signal handler
401//      __kernel_sigdefault( SIGABRT );
402
403//      raise( SIGABRT );
404// }
Note: See TracBrowser for help on using the repository browser.