Ignore:
File:
1 edited

Legend:

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

    rffe2fad r212c2187  
    3535
    3636extern "C" {
    37         void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc *) __attribute__ ((__noreturn__));
    38         static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) __attribute__ ((__noreturn__));
    39         static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) {
    40                 abort();
    41         }
     37      void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage, struct coroutine_desc *) __attribute__ ((__noreturn__));
     38      static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) __attribute__ ((__noreturn__));
     39      static void _CtxCoroutine_UnwindCleanup(_Unwind_Reason_Code, struct _Unwind_Exception *) {
     40            abort();
     41      }
    4242}
    4343
     
    4747// minimum feasible stack size in bytes
    4848#define MinStackSize 1000
    49 extern size_t __page_size;                              // architecture pagesize HACK, should go in proper runtime singleton
    50 
    51 void __stack_prepare( __stack_info_t * this, size_t create_size );
     49static size_t pageSize = 0;                             // architecture pagesize HACK, should go in proper runtime singleton
    5250
    5351//-----------------------------------------------------------------------------
    5452// Coroutine ctors and dtors
    55 void ?{}( __stack_info_t & this, void * storage, size_t storageSize ) {
    56         this.storage   = (__stack_t *)storage;
    57 
    58         // Did we get a piece of storage ?
    59         if (this.storage || storageSize != 0) {
    60                 // We either got a piece of storage or the user asked for a specific size
    61                 // Immediately create the stack
    62                 // (This is slightly unintuitive that non-default sized coroutines create are eagerly created
    63                 // but it avoids that all coroutines carry an unnecessary size)
    64                 verify( storageSize != 0 );
    65                 __stack_prepare( &this, storageSize );
    66         }
    67 }
    68 
    69 void ^?{}(__stack_info_t & this) {
    70         bool userStack = ((intptr_t)this.storage & 0x1) != 0;
    71         if ( ! userStack && this.storage ) {
    72                 __attribute__((may_alias)) intptr_t * istorage = (intptr_t *)&this.storage;
    73                 *istorage &= (intptr_t)-1;
    74 
    75                 void * storage = this.storage->limit;
    76                 __cfaabi_dbg_debug_do(
    77                         storage = (char*)(storage) - __page_size;
    78                         if ( mprotect( storage, __page_size, PROT_READ | PROT_WRITE ) == -1 ) {
    79                                 abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );
    80                         }
    81                 );
    82                 __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage);
    83                 free( storage );
    84         }
     53void ?{}( coStack_t & this, void * storage, size_t storageSize ) with( this ) {
     54      size               = storageSize == 0 ? 65000 : storageSize; // size of stack
     55      this.storage = storage;                                // pointer to stack
     56      limit              = NULL;                                   // stack grows towards stack limit
     57      base               = NULL;                                   // base of stack
     58      context    = NULL;                                   // address of cfa_context_t
     59      top                = NULL;                                   // address of top of storage
     60      userStack  = storage != NULL;
     61}
     62
     63void ^?{}(coStack_t & this) {
     64      if ( ! this.userStack && this.storage ) {
     65            __cfaabi_dbg_debug_do(
     66                  if ( mprotect( this.storage, pageSize, PROT_READ | PROT_WRITE ) == -1 ) {
     67                        abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );
     68                  }
     69            );
     70            free( this.storage );
     71      }
    8572}
    8673
    8774void ?{}( coroutine_desc & this, const char * name, void * storage, size_t storageSize ) with( this ) {
    88         (this.context){NULL, NULL};
    89         (this.stack){storage, storageSize};
    90         this.name = name;
    91         state = Start;
    92         starter = NULL;
    93         last = NULL;
    94         cancellation = NULL;
     75      (this.stack){storage, storageSize};
     76      this.name = name;
     77      errno_ = 0;
     78      state = Start;
     79      starter = NULL;
     80      last = NULL;
     81      cancellation = NULL;
    9582}
    9683
    9784void ^?{}(coroutine_desc& this) {
    98         if(this.state != Halted && this.state != Start) {
    99                 coroutine_desc * src = TL_GET( this_thread )->curr_cor;
    100                 coroutine_desc * dst = &this;
    101 
    102                 struct _Unwind_Exception storage;
    103                 storage.exception_class = -1;
    104                 storage.exception_cleanup = _CtxCoroutine_UnwindCleanup;
    105                 this.cancellation = &storage;
    106                 this.last = src;
    107 
    108                 // not resuming self ?
    109                 if ( src == dst ) {
    110                         abort( "Attempt by coroutine %.256s (%p) to terminate itself.\n", src->name, src );
    111                 }
    112 
    113                 CoroutineCtxSwitch( src, dst );
    114         }
     85      if(this.state != Halted && this.state != Start) {
     86            coroutine_desc * src = TL_GET( this_thread )->curr_cor;
     87            coroutine_desc * dst = &this;
     88
     89            struct _Unwind_Exception storage;
     90            storage.exception_class = -1;
     91            storage.exception_cleanup = _CtxCoroutine_UnwindCleanup;
     92            this.cancellation = &storage;
     93            this.last = src;
     94
     95              // not resuming self ?
     96              if ( src == dst ) {
     97                      abort( "Attempt by coroutine %.256s (%p) to terminate itself.\n", src->name, src );
     98            }
     99
     100              CoroutineCtxSwitch( src, dst );
     101      }
    115102}
    116103
     
    119106forall(dtype T | is_coroutine(T))
    120107void prime(T& cor) {
    121         coroutine_desc* this = get_coroutine(cor);
    122         assert(this->state == Start);
    123 
    124         this->state = Primed;
    125         resume(cor);
    126 }
    127 
    128 [void *, size_t] __stack_alloc( size_t storageSize ) {
    129         static const size_t stack_data_size = libCeiling( sizeof(__stack_t), 16 ); // minimum alignment
    130         assert(__page_size != 0l);
    131         size_t size = libCeiling( storageSize, 16 ) + stack_data_size;
    132 
    133         // If we are running debug, we also need to allocate a guardpage to catch stack overflows.
    134         void * storage;
    135         __cfaabi_dbg_debug_do(
    136                 storage = memalign( __page_size, size + __page_size );
    137         );
    138         __cfaabi_dbg_no_debug_do(
    139                 storage = (void*)malloc(size);
    140         );
    141 
    142         __cfaabi_dbg_print_safe("Kernel : Created stack %p of size %zu\n", storage, size);
    143         __cfaabi_dbg_debug_do(
    144                 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) {
    145                         abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    146                 }
    147                 storage = (void *)(((intptr_t)storage) + __page_size);
    148         );
    149 
    150         verify( ((intptr_t)storage & (libAlign() - 1)) == 0ul );
    151         return [storage, size];
    152 }
    153 
    154 void __stack_prepare( __stack_info_t * this, size_t create_size ) {
    155         static const size_t stack_data_size = libCeiling( sizeof(__stack_t), 16 ); // minimum alignment
    156         bool userStack;
    157         void * storage;
    158         size_t size;
    159         if ( !this->storage ) {
    160                 userStack = false;
    161                 [storage, size] = __stack_alloc( create_size );
    162         } else {
    163                 userStack = true;
    164                 __cfaabi_dbg_print_safe("Kernel : stack obj %p using user stack %p(%zd bytes)\n", this, this->storage, (intptr_t)this->storage->limit - (intptr_t)this->storage->base);
    165 
    166                 // The stack must be aligned, advance the pointer to the next align data
    167                 storage = (void*)libCeiling( (intptr_t)this->storage, libAlign());
    168 
    169                 // The size needs to be shrinked to fit all the extra data structure and be aligned
    170                 ptrdiff_t diff = (intptr_t)storage - (intptr_t)this->storage;
    171                 size = libFloor(create_size - stack_data_size - diff, libAlign());
    172         } // if
    173         assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %d bytes for a stack.", size, MinStackSize );
    174 
    175         this->storage = (__stack_t *)((intptr_t)storage + size);
    176         this->storage->limit = storage;
    177         this->storage->base  = (void*)((intptr_t)storage + size);
    178         __attribute__((may_alias)) intptr_t * istorage = (intptr_t*)&this->storage;
    179         *istorage |= userStack ? 0x1 : 0x0;
     108      coroutine_desc* this = get_coroutine(cor);
     109      assert(this->state == Start);
     110
     111      this->state = Primed;
     112      resume(cor);
     113}
     114
     115// Wrapper for co
     116void CoroutineCtxSwitch(coroutine_desc* src, coroutine_desc* dst) {
     117      // Safety note : Preemption must be disabled since there is a race condition
     118      // kernelTLS.this_thread->curr_cor and $rsp/$rbp must agree at all times
     119      verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate );
     120      disable_interrupts();
     121
     122      // set state of current coroutine to inactive
     123      src->state = src->state == Halted ? Halted : Inactive;
     124
     125      // set new coroutine that task is executing
     126      TL_GET( this_thread )->curr_cor = dst;
     127
     128      // context switch to specified coroutine
     129      assert( src->stack.context );
     130      CtxSwitch( src->stack.context, dst->stack.context );
     131      // when CtxSwitch returns we are back in the src coroutine
     132
     133      // set state of new coroutine to active
     134      src->state = Active;
     135
     136      enable_interrupts( __cfaabi_dbg_ctx );
     137      verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate );
     138
     139
     140      if( unlikely(src->cancellation != NULL) ) {
     141            _CtxCoroutine_Unwind(src->cancellation, src);
     142      }
     143} //ctxSwitchDirect
     144
     145void create_stack( coStack_t* this, unsigned int storageSize ) with( *this ) {
     146      //TEMP HACK do this on proper kernel startup
     147      if(pageSize == 0ul) pageSize = sysconf( _SC_PAGESIZE );
     148
     149      size_t cxtSize = libCeiling( sizeof(machine_context_t), 8 ); // minimum alignment
     150
     151      if ( !storage ) {
     152            __cfaabi_dbg_print_safe("Kernel : Creating stack of size %zu for stack obj %p\n", cxtSize + size + 8, this);
     153
     154            userStack = false;
     155            size = libCeiling( storageSize, 16 );
     156            // use malloc/memalign because "new" raises an exception for out-of-memory
     157
     158            // assume malloc has 8 byte alignment so add 8 to allow rounding up to 16 byte alignment
     159            __cfaabi_dbg_debug_do( storage = memalign( pageSize, cxtSize + size + pageSize ) );
     160            __cfaabi_dbg_no_debug_do( storage = malloc( cxtSize + size + 8 ) );
     161
     162            __cfaabi_dbg_debug_do(
     163                  if ( mprotect( storage, pageSize, PROT_NONE ) == -1 ) {
     164                        abort( "(uMachContext &)%p.createContext() : internal error, mprotect failure, error(%d) %s.", this, (int)errno, strerror( (int)errno ) );
     165                  } // if
     166            );
     167
     168            if ( (intptr_t)storage == 0 ) {
     169                  abort( "Attempt to allocate %zd bytes of storage for coroutine or task execution-state but insufficient memory available.", size );
     170            } // if
     171
     172            __cfaabi_dbg_debug_do( limit = (char *)storage + pageSize );
     173            __cfaabi_dbg_no_debug_do( limit = (char *)libCeiling( (unsigned long)storage, 16 ) ); // minimum alignment
     174
     175      } else {
     176            __cfaabi_dbg_print_safe("Kernel : stack obj %p using user stack %p(%u bytes)\n", this, storage, storageSize);
     177
     178            assertf( ((size_t)storage & (libAlign() - 1)) == 0ul, "Stack storage %p for task/coroutine must be aligned on %d byte boundary.", storage, (int)libAlign() );
     179            userStack = true;
     180            size = storageSize - cxtSize;
     181
     182            if ( size % 16 != 0u ) size -= 8;
     183
     184            limit = (char *)libCeiling( (unsigned long)storage, 16 ); // minimum alignment
     185      } // if
     186      assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %d bytes for a stack.", size, MinStackSize );
     187
     188      base = (char *)limit + size;
     189      context = base;
     190      top = (char *)context + cxtSize;
    180191}
    181192
     
    183194// is not inline (We can't inline Cforall in C)
    184195extern "C" {
    185         void __suspend_internal(void) {
    186                 suspend();
    187         }
    188 
    189         void __leave_coroutine( coroutine_desc * src ) {
    190                 coroutine_desc * starter = src->cancellation != 0 ? src->last : src->starter;
    191 
    192                 src->state = Halted;
    193 
    194                 assertf( starter != 0,
    195                         "Attempt to suspend/leave coroutine \"%.256s\" (%p) that has never been resumed.\n"
    196                         "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",
    197                         src->name, src );
    198                 assertf( starter->state != Halted,
    199                         "Attempt by coroutine \"%.256s\" (%p) to suspend/leave back to terminated coroutine \"%.256s\" (%p).\n"
    200                         "Possible cause is terminated coroutine's main routine has already returned.",
    201                         src->name, src, starter->name, starter );
    202 
    203                 CoroutineCtxSwitch( src, starter );
    204         }
     196      void __suspend_internal(void) {
     197            suspend();
     198      }
     199
     200      void __leave_coroutine( coroutine_desc * src ) {
     201            coroutine_desc * starter = src->cancellation != 0 ? src->last : src->starter;
     202
     203            src->state = Halted;
     204
     205            assertf( starter != 0,
     206                  "Attempt to suspend/leave coroutine \"%.256s\" (%p) that has never been resumed.\n"
     207                  "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",
     208                  src->name, src );
     209            assertf( starter->state != Halted,
     210                  "Attempt by coroutine \"%.256s\" (%p) to suspend/leave back to terminated coroutine \"%.256s\" (%p).\n"
     211                  "Possible cause is terminated coroutine's main routine has already returned.",
     212                  src->name, src, starter->name, starter );
     213
     214            CoroutineCtxSwitch( src, starter );
     215      }
    205216}
    206217
Note: See TracChangeset for help on using the changeset viewer.