source: src/libcfa/concurrency/kernel.c @ 8def349

aaron-thesisarm-ehcleanup-dtorsdeferred_resndemanglerjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprnew-envno_listpersistent-indexerresolv-newwith_gc
Last change on this file since 8def349 was 8def349, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

cfa now supports processors which represent kernel threads, allowing basic parallelism

  • Property mode set to 100644
File size: 12.0 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// kernel.c --
9//
10// Author           : Thierry Delisle
11// Created On       : Tue Jan 17 12:27:26 2016
12// Last Modified By : Thierry Delisle
13// Last Modified On : --
14// Update Count     : 0
15//
16
17//Header
18#include "kernel"
19
20//C Includes
21#include <stddef.h>
22extern "C" {
23#include <sys/resource.h>
24}
25
26//CFA Includes
27#include "libhdr.h"
28#include "threads"
29
30//Private includes
31#define __CFA_INVOKE_PRIVATE__
32#include "invoke.h"
33
34//-----------------------------------------------------------------------------
35// Kernel storage
36struct processorCtx_t {
37        processor * proc;
38        coroutine c;
39};
40
41DECL_COROUTINE(processorCtx_t)
42
43#define KERNEL_STORAGE(T,X) static char X##_storage[sizeof(T)]
44
45KERNEL_STORAGE(processorCtx_t, systemProcessorCtx);
46KERNEL_STORAGE(cluster, systemCluster);
47KERNEL_STORAGE(processor, systemProcessor);
48KERNEL_STORAGE(thread_h, mainThread);
49KERNEL_STORAGE(machine_context_t, mainThread_context);
50
51cluster * systemCluster;
52processor * systemProcessor;
53thread_h * mainThread;
54
55void kernel_startup(void)  __attribute__((constructor(101)));
56void kernel_shutdown(void) __attribute__((destructor(101)));
57
58//-----------------------------------------------------------------------------
59// Global state
60
61thread_local processor * this_processor;
62
63processor * get_this_processor() {
64        return this_processor;
65}
66
67coroutine * this_coroutine(void) {
68        return this_processor->current_coroutine;
69}
70
71thread_h * this_thread(void) {
72        return this_processor->current_thread;
73}
74
75//-----------------------------------------------------------------------------
76// Main thread construction
77struct current_stack_info_t {
78        machine_context_t ctx; 
79        unsigned int size;              // size of stack
80        void *base;                             // base of stack
81        void *storage;                  // pointer to stack
82        void *limit;                    // stack grows towards stack limit
83        void *context;                  // address of cfa_context_t
84        void *top;                              // address of top of storage
85};
86
87void ?{}( current_stack_info_t * this ) {
88        CtxGet( &this->ctx );
89        this->base = this->ctx.FP;
90        this->storage = this->ctx.SP;
91
92        rlimit r;
93        int ret = getrlimit( RLIMIT_STACK, &r);
94        this->size = r.rlim_cur;
95
96        this->limit = (void *)(((intptr_t)this->base) - this->size);
97        this->context = &mainThread_context_storage;
98        this->top = this->base;
99}
100
101void ?{}( coStack_t * this, current_stack_info_t * info) {
102        this->size = info->size;
103        this->storage = info->storage;
104        this->limit = info->limit;
105        this->base = info->base;
106        this->context = info->context;
107        this->top = info->top;
108        this->userStack = true;
109}
110
111void ?{}( coroutine * this, current_stack_info_t * info) {
112        (&this->stack){ info }; 
113        this->name = "Main Thread";
114        this->errno_ = 0;
115        this->state = Inactive;
116        this->notHalted = true;
117}
118
119void ?{}( thread_h * this, current_stack_info_t * info) {
120        (&this->c){ info };
121}
122
123//-----------------------------------------------------------------------------
124// Processor coroutine
125void ?{}(processorCtx_t * this, processor * proc) {
126        (&this->c){};
127        this->proc = proc;
128        proc->ctx = this;
129}
130
131void ?{}(processorCtx_t * this, processor * proc, current_stack_info_t * info) {
132        (&this->c){ info };
133        this->proc = proc;
134        proc->ctx = this;
135}
136
137void start(processor * this);
138
139void ?{}(processor * this) {
140        this{ systemCluster };
141}
142
143void ?{}(processor * this, cluster * cltr) {
144        this->cltr = cltr;
145        this->current_coroutine = NULL;
146        this->current_thread = NULL;
147        (&this->lock){};
148        this->terminated = false;
149
150        start( this );
151}
152
153void ?{}(processor * this, cluster * cltr, processorCtx_t * ctx) {
154        this->cltr = cltr;
155        this->current_coroutine = NULL;
156        this->current_thread = NULL;
157        (&this->lock){};
158        this->terminated = false;
159
160        this->ctx = ctx;
161        LIB_DEBUG_PRINTF("Kernel : constructing processor context %p\n", ctx);
162        ctx{ this };
163}
164
165void ^?{}(processor * this) {
166        if( ! this->terminated ) {
167                LIB_DEBUG_PRINTF("Kernel : core %p signaling termination\n", this);
168                this->terminated = true;
169                lock( &this->lock );
170        }
171}
172
173void ?{}(cluster * this) {
174        ( &this->ready_queue ){};
175        pthread_spin_init( &this->lock, PTHREAD_PROCESS_PRIVATE );
176}
177
178void ^?{}(cluster * this) {
179        pthread_spin_destroy( &this->lock );
180}
181
182//-----------------------------------------------------------------------------
183// Processor running routines
184void main(processorCtx_t * ctx);
185thread_h * nextThread(cluster * this);
186void runThread(processor * this, thread_h * dst);
187void spin(processor * this, unsigned int * spin_count);
188
189void main(processorCtx_t * ctx) {
190        processor * this = ctx->proc;
191        LIB_DEBUG_PRINTF("Kernel : core %p starting\n", this);
192
193        thread_h * readyThread = NULL;
194        for( unsigned int spin_count = 0; ! this->terminated; spin_count++ ) {
195               
196                readyThread = nextThread( this->cltr );
197
198                if(readyThread) {
199                        runThread(this, readyThread);
200                        spin_count = 0;
201                } else {
202                        spin(this, &spin_count);
203                }               
204        }
205
206        LIB_DEBUG_PRINTF("Kernel : core %p unlocking thread\n", this);
207        unlock( &this->lock );
208        LIB_DEBUG_PRINTF("Kernel : core %p terminated\n", this);
209}
210
211void runThread(processor * this, thread_h * dst) {
212        coroutine * proc_ctx = get_coroutine(this->ctx);
213        coroutine * thrd_ctx = get_coroutine(dst);
214        thrd_ctx->last = proc_ctx;
215
216        // context switch to specified coroutine
217        // Which is now the current_coroutine
218        // LIB_DEBUG_PRINTF("Kernel : switching to ctx %p (from %p, current %p)\n", thrd_ctx, proc_ctx, current_coroutine);
219        this->current_thread = dst;
220        this->current_coroutine = thrd_ctx;
221        CtxSwitch( proc_ctx->stack.context, thrd_ctx->stack.context );
222        this->current_coroutine = proc_ctx;
223        // LIB_DEBUG_PRINTF("Kernel : returned from ctx %p (to %p, current %p)\n", thrd_ctx, proc_ctx, current_coroutine);
224
225        // when CtxSwitch returns we are back in the processor coroutine
226}
227
228void spin(processor * this, unsigned int * spin_count) {
229        (*spin_count)++;
230}
231
232void * CtxInvokeProcessor(void * arg) {
233        processor * proc = (processor *) arg;
234        this_processor = proc;
235        // SKULLDUGGERY: We want to create a context for the processor coroutine
236        // which is needed for the 2-step context switch. However, there is no reason
237        // to waste the perfectly valid stack create by pthread.
238        current_stack_info_t info;
239        machine_context_t ctx;
240        info.context = &ctx;
241        processorCtx_t proc_cor_storage = { proc, &info };
242
243        proc->current_coroutine = &proc->ctx->c;
244        proc->current_thread = NULL;
245
246        LIB_DEBUG_PRINTF("Kernel : core %p created (%p)\n", proc, proc->ctx);
247
248        // LIB_DEBUG_PRINTF("Kernel : core    base : %p \n", info.base );
249        // LIB_DEBUG_PRINTF("Kernel : core storage : %p \n", info.storage );
250        // LIB_DEBUG_PRINTF("Kernel : core    size : %x \n", info.size );
251        // LIB_DEBUG_PRINTF("Kernel : core   limit : %p \n", info.limit );
252        // LIB_DEBUG_PRINTF("Kernel : core context : %p \n", info.context );
253        // LIB_DEBUG_PRINTF("Kernel : core     top : %p \n", info.top );
254
255        //We now have a proper context from which to schedule threads
256
257        // SKULLDUGGERY: Since the coroutine doesn't have its own stack, we can't
258        // resume it to start it like it normally would, it will just context switch
259        // back to here. Instead directly call the main since we already are on the
260        // appropriate stack.
261        proc_cor_storage.c.state = Active;
262      main( &proc_cor_storage );
263      proc_cor_storage.c.state = Halt;
264      proc_cor_storage.c.notHalted = false;
265
266        LIB_DEBUG_PRINTF("Kernel : core %p main ended (%p)\n", proc, proc->ctx);       
267
268        return NULL;
269}
270
271void start(processor * this) {
272        LIB_DEBUG_PRINTF("Kernel : Starting core %p\n", this);
273       
274        pthread_attr_t attributes;
275        pthread_attr_init( &attributes );
276
277        pthread_create( &this->kernel_thread, &attributes, CtxInvokeProcessor, (void*)this );
278
279        pthread_attr_destroy( &attributes );
280
281        LIB_DEBUG_PRINTF("Kernel : core %p started\n", this);   
282}
283
284//-----------------------------------------------------------------------------
285// Scheduler routines
286void thread_schedule( thread_h * thrd ) {
287        assertf( thrd->next == NULL, "Expected null got %p", thrd->next );
288       
289        pthread_spinlock_guard guard = { &systemProcessor->cltr->lock };
290        append( &systemProcessor->cltr->ready_queue, thrd );
291}
292
293thread_h * nextThread(cluster * this) {
294        pthread_spinlock_guard guard = { &this->lock };
295        return pop_head( &this->ready_queue );
296}
297
298//-----------------------------------------------------------------------------
299// Kernel boot procedures
300void kernel_startup(void) {
301
302        // SKULLDUGGERY: the mainThread steals the process main thread
303        // which will then be scheduled by the systemProcessor normally
304        LIB_DEBUG_PRINTF("Kernel : Starting\n");       
305
306        current_stack_info_t info;
307
308        // LIB_DEBUG_PRINTF("Kernel : core    base : %p \n", info.base );
309        // LIB_DEBUG_PRINTF("Kernel : core storage : %p \n", info.storage );
310        // LIB_DEBUG_PRINTF("Kernel : core    size : %x \n", info.size );
311        // LIB_DEBUG_PRINTF("Kernel : core   limit : %p \n", info.limit );
312        // LIB_DEBUG_PRINTF("Kernel : core context : %p \n", info.context );
313        // LIB_DEBUG_PRINTF("Kernel : core     top : %p \n", info.top );
314
315        // Start by initializing the main thread
316        mainThread = (thread_h *)&mainThread_storage;
317        mainThread{ &info };
318
319        // Initialize the system cluster
320        systemCluster = (cluster *)&systemCluster_storage;
321        systemCluster{};
322
323        // Initialize the system processor and the system processor ctx
324        // (the coroutine that contains the processing control flow)
325        systemProcessor = (processor *)&systemProcessor_storage;
326        systemProcessor{ systemCluster, (processorCtx_t *)&systemProcessorCtx_storage };
327
328        // Add the main thread to the ready queue
329        // once resume is called on systemProcessor->ctx the mainThread needs to be scheduled like any normal thread
330        thread_schedule(mainThread);
331
332        //initialize the global state variables
333        this_processor = systemProcessor;
334        this_processor->current_thread = mainThread;
335        this_processor->current_coroutine = &mainThread->c;
336
337        // SKULLDUGGERY: Force a context switch to the system processor to set the main thread's context to the current UNIX
338        // context. Hence, the main thread does not begin through CtxInvokeThread, like all other threads. The trick here is that
339        // mainThread is on the ready queue when this call is made.
340        resume(systemProcessor->ctx);
341
342
343
344        // THE SYSTEM IS NOW COMPLETELY RUNNING
345
346
347
348        LIB_DEBUG_PRINTF("Kernel : Started\n--------------------------------------------------\n\n");
349}
350
351void kernel_shutdown(void) {
352        LIB_DEBUG_PRINTF("\n--------------------------------------------------\nKernel : Shutting down\n");
353
354        // SKULLDUGGERY: Notify the systemProcessor it needs to terminates.
355        // When its coroutine terminates, it return control to the mainThread
356        // which is currently here
357        systemProcessor->terminated = true;
358        suspend();
359
360        // THE SYSTEM IS NOW COMPLETELY STOPPED
361
362        // Destroy the system processor and its context in reverse order of construction
363        // These were manually constructed so we need manually destroy them
364        ^(systemProcessor->ctx){};
365        ^(systemProcessor){};
366
367        // Final step, destroy the main thread since it is no longer needed
368        // Since we provided a stack to this taxk it will not destroy anything
369        ^(mainThread){};
370
371        LIB_DEBUG_PRINTF("Kernel : Shutdown complete\n");       
372}
373
374//-----------------------------------------------------------------------------
375// Locks
376void ?{}( simple_lock * this ) {
377        ( &this->blocked ){};
378}
379
380void ^?{}( simple_lock * this ) {
381
382}
383
384void lock( simple_lock * this ) {
385        {
386                pthread_spinlock_guard guard = { &systemCluster->lock };        //HUGE TEMP HACK which only works if we have a single cluster and is stupid
387                append( &this->blocked, this_thread() );
388        }
389        suspend();
390}
391
392void unlock( simple_lock * this ) {
393        thread_h * it;
394        while( it = pop_head( &this->blocked) ) {
395                thread_schedule( it );
396        }
397}
398
399//-----------------------------------------------------------------------------
400// Queues
401void ?{}( simple_thread_list * this ) {
402        this->head = NULL;
403        this->tail = &this->head;
404}
405
406void append( simple_thread_list * this, thread_h * t ) {
407        assert( t->next == NULL );
408        *this->tail = t;
409        this->tail = &t->next;
410}
411
412thread_h * pop_head( simple_thread_list * this ) {
413        thread_h * head = this->head;
414        if( head ) {
415                this->head = head->next;
416                if( !head->next ) {
417                        this->tail = &this->head;
418                }
419                head->next = NULL;
420        }       
421       
422        return head;
423}
424// Local Variables: //
425// mode: c //
426// tab-width: 4 //
427// End: //
Note: See TracBrowser for help on using the repository browser.