| [6a3d2e7] | 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 | //
 | 
|---|
| [75a17f1] | 7 | // coroutine.c --
 | 
|---|
| [6a3d2e7] | 8 | //
 | 
|---|
 | 9 | // Author           : Thierry Delisle
 | 
|---|
 | 10 | // Created On       : Mon Nov 28 12:27:26 2016
 | 
|---|
| [6b0b624] | 11 | // Last Modified By : Peter A. Buhr
 | 
|---|
 | 12 | // Last Modified On : Fri Jul 21 22:34:57 2017
 | 
|---|
 | 13 | // Update Count     : 1
 | 
|---|
| [6a3d2e7] | 14 | //
 | 
|---|
 | 15 | 
 | 
|---|
| [75a17f1] | 16 | #include "coroutine"
 | 
|---|
| [bd98b58] | 17 | 
 | 
|---|
| [6a3d2e7] | 18 | extern "C" {
 | 
|---|
 | 19 | #include <stddef.h>
 | 
|---|
 | 20 | #include <malloc.h>
 | 
|---|
 | 21 | #include <errno.h>
 | 
|---|
 | 22 | #include <string.h>
 | 
|---|
 | 23 | #include <unistd.h>
 | 
|---|
 | 24 | #include <sys/mman.h>
 | 
|---|
 | 25 | }
 | 
|---|
 | 26 | 
 | 
|---|
| [9cc0472] | 27 | #include "kernel_private.h"
 | 
|---|
| [6a3d2e7] | 28 | 
 | 
|---|
 | 29 | #define __CFA_INVOKE_PRIVATE__
 | 
|---|
 | 30 | #include "invoke.h"
 | 
|---|
 | 31 | 
 | 
|---|
| [bd98b58] | 32 | 
 | 
|---|
| [6a3d2e7] | 33 | //-----------------------------------------------------------------------------
 | 
|---|
 | 34 | // Global state variables
 | 
|---|
 | 35 | 
 | 
|---|
 | 36 | // minimum feasible stack size in bytes
 | 
|---|
 | 37 | #define MinStackSize 1000
 | 
|---|
 | 38 | static size_t pageSize = 0;                             // architecture pagesize HACK, should go in proper runtime singleton
 | 
|---|
 | 39 | 
 | 
|---|
 | 40 | //-----------------------------------------------------------------------------
 | 
|---|
 | 41 | // Coroutine ctors and dtors
 | 
|---|
| [242a902] | 42 | void ?{}(coStack_t& this) {
 | 
|---|
 | 43 |         this.size               = 65000;        // size of stack
 | 
|---|
 | 44 |         this.storage    = NULL; // pointer to stack
 | 
|---|
 | 45 |         this.limit              = NULL; // stack grows towards stack limit
 | 
|---|
 | 46 |         this.base               = NULL; // base of stack
 | 
|---|
 | 47 |         this.context    = NULL; // address of cfa_context_t
 | 
|---|
 | 48 |         this.top                = NULL; // address of top of storage
 | 
|---|
 | 49 |         this.userStack  = false;
 | 
|---|
| [6a3d2e7] | 50 | }
 | 
|---|
 | 51 | 
 | 
|---|
| [242a902] | 52 | void ?{}(coStack_t& this, size_t size) {
 | 
|---|
| [6a3d2e7] | 53 |         this{};
 | 
|---|
| [242a902] | 54 |         this.size = size;
 | 
|---|
| [6a3d2e7] | 55 | 
 | 
|---|
| [242a902] | 56 |         create_stack(&this, this.size);
 | 
|---|
| [6a3d2e7] | 57 | }
 | 
|---|
 | 58 | 
 | 
|---|
| [242a902] | 59 | void ?{}(coroutine_desc& this) {
 | 
|---|
| [db6f06a] | 60 |         this{ "Anonymous Coroutine" };
 | 
|---|
| [6a3d2e7] | 61 | }
 | 
|---|
 | 62 | 
 | 
|---|
| [242a902] | 63 | void ?{}(coroutine_desc& this, const char * name) {
 | 
|---|
 | 64 |         this.name = name;
 | 
|---|
 | 65 |         this.errno_ = 0;
 | 
|---|
 | 66 |         this.state = Start;
 | 
|---|
| [b462670] | 67 |         this.starter = NULL;
 | 
|---|
| [242a902] | 68 |         this.last = NULL;
 | 
|---|
| [84e2523] | 69 | }
 | 
|---|
 | 70 | 
 | 
|---|
| [242a902] | 71 | void ?{}(coroutine_desc& this, size_t size) {
 | 
|---|
| [6a3d2e7] | 72 |         this{};
 | 
|---|
| [242a902] | 73 |         (this.stack){size};
 | 
|---|
| [6a3d2e7] | 74 | }
 | 
|---|
 | 75 | 
 | 
|---|
| [ed235b6] | 76 | void ^?{}(coStack_t & this) {
 | 
|---|
 | 77 |         if ( ! this.userStack && this.storage ) {
 | 
|---|
| [6a3d2e7] | 78 |                 LIB_DEBUG_DO(
 | 
|---|
| [242a902] | 79 |                         if ( mprotect( this.storage, pageSize, PROT_READ | PROT_WRITE ) == -1 ) {
 | 
|---|
 | 80 |                                 abortf( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );
 | 
|---|
| [6a3d2e7] | 81 |                         }
 | 
|---|
 | 82 |                 );
 | 
|---|
| [242a902] | 83 |                 free( this.storage );
 | 
|---|
| [6a3d2e7] | 84 |         }
 | 
|---|
 | 85 | }
 | 
|---|
 | 86 | 
 | 
|---|
| [242a902] | 87 | void ^?{}(coroutine_desc& this) {}
 | 
|---|
| [6a3d2e7] | 88 | 
 | 
|---|
 | 89 | // Part of the Public API
 | 
|---|
 | 90 | // Not inline since only ever called once per coroutine
 | 
|---|
 | 91 | forall(dtype T | is_coroutine(T))
 | 
|---|
| [83a071f9] | 92 | void prime(T& cor) {
 | 
|---|
| [c3acb841] | 93 |         coroutine_desc* this = get_coroutine(cor);
 | 
|---|
| [6a3d2e7] | 94 |         assert(this->state == Start);
 | 
|---|
 | 95 | 
 | 
|---|
 | 96 |         this->state = Primed;
 | 
|---|
 | 97 |         resume(cor);
 | 
|---|
 | 98 | }
 | 
|---|
 | 99 | 
 | 
|---|
| [0c92c9f] | 100 | // Wrapper for co
 | 
|---|
| [c3acb841] | 101 | void CoroutineCtxSwitch(coroutine_desc* src, coroutine_desc* dst) {
 | 
|---|
| [6a3d2e7] | 102 |         // THREAD_GETMEM( This )->disableInterrupts();
 | 
|---|
 | 103 | 
 | 
|---|
 | 104 |         // set state of current coroutine to inactive
 | 
|---|
| [1c273d0] | 105 |         src->state = src->state == Halted ? Halted : Inactive;
 | 
|---|
| [6a3d2e7] | 106 | 
 | 
|---|
 | 107 |         // set new coroutine that task is executing
 | 
|---|
| [1c273d0] | 108 |         this_coroutine = dst;
 | 
|---|
| [6a3d2e7] | 109 | 
 | 
|---|
 | 110 |         // context switch to specified coroutine
 | 
|---|
| [1c273d0] | 111 |         assert( src->stack.context );
 | 
|---|
| [6a3d2e7] | 112 |         CtxSwitch( src->stack.context, dst->stack.context );
 | 
|---|
| [47ecf2b] | 113 |         // when CtxSwitch returns we are back in the src coroutine
 | 
|---|
| [6a3d2e7] | 114 | 
 | 
|---|
 | 115 |         // set state of new coroutine to active
 | 
|---|
 | 116 |         src->state = Active;
 | 
|---|
 | 117 | 
 | 
|---|
 | 118 |         // THREAD_GETMEM( This )->enableInterrupts();
 | 
|---|
 | 119 | } //ctxSwitchDirect
 | 
|---|
 | 120 | 
 | 
|---|
 | 121 | void create_stack( coStack_t* this, unsigned int storageSize ) {
 | 
|---|
 | 122 |         //TEMP HACK do this on proper kernel startup
 | 
|---|
 | 123 |         if(pageSize == 0ul) pageSize = sysconf( _SC_PAGESIZE );
 | 
|---|
 | 124 | 
 | 
|---|
 | 125 |         size_t cxtSize = libCeiling( sizeof(machine_context_t), 8 ); // minimum alignment
 | 
|---|
 | 126 | 
 | 
|---|
 | 127 |         if ( (intptr_t)this->storage == 0 ) {
 | 
|---|
 | 128 |                 this->userStack = false;
 | 
|---|
 | 129 |                 this->size = libCeiling( storageSize, 16 );
 | 
|---|
 | 130 |                 // use malloc/memalign because "new" raises an exception for out-of-memory
 | 
|---|
| [47ecf2b] | 131 | 
 | 
|---|
| [6a3d2e7] | 132 |                 // assume malloc has 8 byte alignment so add 8 to allow rounding up to 16 byte alignment
 | 
|---|
 | 133 |                 LIB_DEBUG_DO( this->storage = memalign( pageSize, cxtSize + this->size + pageSize ) );
 | 
|---|
 | 134 |                 LIB_NO_DEBUG_DO( this->storage = malloc( cxtSize + this->size + 8 ) );
 | 
|---|
 | 135 | 
 | 
|---|
 | 136 |                 LIB_DEBUG_DO(
 | 
|---|
 | 137 |                         if ( mprotect( this->storage, pageSize, PROT_NONE ) == -1 ) {
 | 
|---|
 | 138 |                                 abortf( "(uMachContext &)%p.createContext() : internal error, mprotect failure, error(%d) %s.", this, (int)errno, strerror( (int)errno ) );
 | 
|---|
 | 139 |                         } // if
 | 
|---|
 | 140 |                 );
 | 
|---|
 | 141 | 
 | 
|---|
 | 142 |                 if ( (intptr_t)this->storage == 0 ) {
 | 
|---|
 | 143 |                         abortf( "Attempt to allocate %d bytes of storage for coroutine or task execution-state but insufficient memory available.", this->size );
 | 
|---|
 | 144 |                 } // if
 | 
|---|
 | 145 | 
 | 
|---|
 | 146 |                 LIB_DEBUG_DO( this->limit = (char *)this->storage + pageSize );
 | 
|---|
 | 147 |                 LIB_NO_DEBUG_DO( this->limit = (char *)libCeiling( (unsigned long)this->storage, 16 ) ); // minimum alignment
 | 
|---|
 | 148 | 
 | 
|---|
 | 149 |         } else {
 | 
|---|
 | 150 |                 assertf( ((size_t)this->storage & (libAlign() - 1)) != 0ul, "Stack storage %p for task/coroutine must be aligned on %d byte boundary.", this->storage, (int)libAlign() );
 | 
|---|
 | 151 |                 this->userStack = true;
 | 
|---|
 | 152 |                 this->size = storageSize - cxtSize;
 | 
|---|
 | 153 | 
 | 
|---|
 | 154 |                 if ( this->size % 16 != 0u ) this->size -= 8;
 | 
|---|
 | 155 | 
 | 
|---|
 | 156 |                 this->limit = (char *)libCeiling( (unsigned long)this->storage, 16 ); // minimum alignment
 | 
|---|
 | 157 |         } // if
 | 
|---|
 | 158 |         assertf( this->size >= MinStackSize, "Stack size %d provides less than minimum of %d bytes for a stack.", this->size, MinStackSize );
 | 
|---|
 | 159 | 
 | 
|---|
 | 160 |         this->base = (char *)this->limit + this->size;
 | 
|---|
 | 161 |         this->context = this->base;
 | 
|---|
 | 162 |         this->top = (char *)this->context + cxtSize;
 | 
|---|
 | 163 | }
 | 
|---|
 | 164 | 
 | 
|---|
| [0c92c9f] | 165 | // We need to call suspend from invoke.c, so we expose this wrapper that
 | 
|---|
 | 166 | // is not inline (We can't inline Cforall in C)
 | 
|---|
 | 167 | extern "C" {
 | 
|---|
 | 168 |         void __suspend_internal(void) {
 | 
|---|
 | 169 |                 suspend();
 | 
|---|
 | 170 |         }
 | 
|---|
| [39fea2f] | 171 | 
 | 
|---|
 | 172 |         void __leave_coroutine(void) {
 | 
|---|
 | 173 |                 coroutine_desc * src = this_coroutine;          // optimization
 | 
|---|
 | 174 | 
 | 
|---|
 | 175 |                 assertf( src->starter != 0,
 | 
|---|
| [b462670] | 176 |                         "Attempt to suspend/leave coroutine \"%.256s\" (%p) that has never been resumed.\n"
 | 
|---|
 | 177 |                         "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",
 | 
|---|
| [39fea2f] | 178 |                         src->name, src );
 | 
|---|
 | 179 |                 assertf( src->starter->state != Halted,
 | 
|---|
| [b462670] | 180 |                         "Attempt by coroutine \"%.256s\" (%p) to suspend/leave back to terminated coroutine \"%.256s\" (%p).\n"
 | 
|---|
| [39fea2f] | 181 |                         "Possible cause is terminated coroutine's main routine has already returned.",
 | 
|---|
 | 182 |                         src->name, src, src->starter->name, src->starter );
 | 
|---|
 | 183 | 
 | 
|---|
 | 184 |                 CoroutineCtxSwitch( src, src->starter );
 | 
|---|
 | 185 |         }
 | 
|---|
| [0c92c9f] | 186 | }
 | 
|---|
 | 187 | 
 | 
|---|
| [6a3d2e7] | 188 | // Local Variables: //
 | 
|---|
 | 189 | // mode: c //
 | 
|---|
 | 190 | // tab-width: 4 //
 | 
|---|
| [4aa2fb2] | 191 | // End: //
 | 
|---|