Ignore:
File:
1 edited

Legend:

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

    r63364d8 r604d77b  
    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         }
    42 
    43         extern void CtxRet( struct __stack_context_t * to ) asm ("CtxRet") __attribute__ ((__noreturn__));
     37      void _CtxCoroutine_Unwind(struct _Unwind_Exception * storage) __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      }
    4442}
    4543
     
    4947// minimum feasible stack size in bytes
    5048#define MinStackSize 1000
    51 extern size_t __page_size;                              // architecture pagesize HACK, should go in proper runtime singleton
    52 
    53 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
    5450
    5551//-----------------------------------------------------------------------------
    5652// Coroutine ctors and dtors
    57 void ?{}( __stack_info_t & this, void * storage, size_t storageSize ) {
    58         this.storage   = (__stack_t *)storage;
    59 
    60         // Did we get a piece of storage ?
    61         if (this.storage || storageSize != 0) {
    62                 // We either got a piece of storage or the user asked for a specific size
    63                 // Immediately create the stack
    64                 // (This is slightly unintuitive that non-default sized coroutines create are eagerly created
    65                 // but it avoids that all coroutines carry an unnecessary size)
    66                 verify( storageSize != 0 );
    67                 __stack_prepare( &this, storageSize );
    68         }
    69 }
    70 
    71 void ^?{}(__stack_info_t & this) {
    72         bool userStack = ((intptr_t)this.storage & 0x1) != 0;
    73         if ( ! userStack && this.storage ) {
    74                 __attribute__((may_alias)) intptr_t * istorage = (intptr_t *)&this.storage;
    75                 *istorage &= (intptr_t)-1;
    76 
    77                 void * storage = this.storage->limit;
    78                 __cfaabi_dbg_debug_do(
    79                         storage = (char*)(storage) - __page_size;
    80                         if ( mprotect( storage, __page_size, PROT_READ | PROT_WRITE ) == -1 ) {
    81                                 abort( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );
    82                         }
    83                 );
    84                 __cfaabi_dbg_print_safe("Kernel : Deleting stack %p\n", storage);
    85                 free( storage );
    86         }
     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      }
    8772}
    8873
    8974void ?{}( coroutine_desc & this, const char * name, void * storage, size_t storageSize ) with( this ) {
    90         (this.context){NULL, NULL};
    91         (this.stack){storage, storageSize};
    92         this.name = name;
    93         state = Start;
    94         starter = NULL;
    95         last = NULL;
    96         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;
    9782}
    9883
    9984void ^?{}(coroutine_desc& this) {
    100         if(this.state != Halted && this.state != Start) {
    101                 coroutine_desc * src = TL_GET( this_thread )->curr_cor;
    102                 coroutine_desc * dst = &this;
    103 
    104                 struct _Unwind_Exception storage;
    105                 storage.exception_class = -1;
    106                 storage.exception_cleanup = _CtxCoroutine_UnwindCleanup;
    107                 this.cancellation = &storage;
    108                 this.last = src;
    109 
    110                 // not resuming self ?
    111                 if ( src == dst ) {
    112                         abort( "Attempt by coroutine %.256s (%p) to terminate itself.\n", src->name, src );
    113                 }
    114 
    115                 CoroutineCtxSwitch( src, dst );
    116         }
     85      if(this.state != Halted && this.state != Start) {
     86            coroutine_desc * src = TL_GET( this_coroutine );
     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      }
    117102}
    118103
     
    121106forall(dtype T | is_coroutine(T))
    122107void prime(T& cor) {
    123         coroutine_desc* this = get_coroutine(cor);
    124         assert(this->state == Start);
    125 
    126         this->state = Primed;
    127         resume(cor);
    128 }
    129 
    130 [void *, size_t] __stack_alloc( size_t storageSize ) {
    131         static const size_t stack_data_size = libCeiling( sizeof(__stack_t), 16 ); // minimum alignment
    132         assert(__page_size != 0l);
    133         size_t size = libCeiling( storageSize, 16 ) + stack_data_size;
    134 
    135         // If we are running debug, we also need to allocate a guardpage to catch stack overflows.
    136         void * storage;
    137         __cfaabi_dbg_debug_do(
    138                 storage = memalign( __page_size, size + __page_size );
    139         );
    140         __cfaabi_dbg_no_debug_do(
    141                 storage = (void*)malloc(size);
    142         );
    143 
    144         __cfaabi_dbg_print_safe("Kernel : Created stack %p of size %zu\n", storage, size);
    145         __cfaabi_dbg_debug_do(
    146                 if ( mprotect( storage, __page_size, PROT_NONE ) == -1 ) {
    147                         abort( "__stack_alloc : internal error, mprotect failure, error(%d) %s.", (int)errno, strerror( (int)errno ) );
    148                 }
    149                 storage = (void *)(((intptr_t)storage) + __page_size);
    150         );
    151 
    152         verify( ((intptr_t)storage & (libAlign() - 1)) == 0ul );
    153         return [storage, size];
    154 }
    155 
    156 void __stack_prepare( __stack_info_t * this, size_t create_size ) {
    157         static const size_t stack_data_size = libCeiling( sizeof(__stack_t), 16 ); // minimum alignment
    158         bool userStack;
    159         void * storage;
    160         size_t size;
    161         if ( !this->storage ) {
    162                 userStack = false;
    163                 [storage, size] = __stack_alloc( create_size );
    164         } else {
    165                 userStack = true;
    166                 __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);
    167 
    168                 // The stack must be aligned, advance the pointer to the next align data
    169                 storage = (void*)libCeiling( (intptr_t)this->storage, libAlign());
    170 
    171                 // The size needs to be shrinked to fit all the extra data structure and be aligned
    172                 ptrdiff_t diff = (intptr_t)storage - (intptr_t)this->storage;
    173                 size = libFloor(create_size - stack_data_size - diff, libAlign());
    174         } // if
    175         assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %d bytes for a stack.", size, MinStackSize );
    176 
    177         this->storage = (__stack_t *)((intptr_t)storage + size);
    178         this->storage->limit = storage;
    179         this->storage->base  = (void*)((intptr_t)storage + size);
    180         __attribute__((may_alias)) intptr_t * istorage = (intptr_t*)&this->storage;
    181         *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 : This could cause some false positives due to preemption
     118      verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate );
     119      disable_interrupts();
     120
     121      // set state of current coroutine to inactive
     122      src->state = src->state == Halted ? Halted : Inactive;
     123
     124      // set new coroutine that task is executing
     125      kernelTLS.this_coroutine = dst;
     126
     127      // context switch to specified coroutine
     128      assert( src->stack.context );
     129      CtxSwitch( src->stack.context, dst->stack.context );
     130      // when CtxSwitch returns we are back in the src coroutine
     131
     132      // set state of new coroutine to active
     133      src->state = Active;
     134
     135      enable_interrupts( __cfaabi_dbg_ctx );
     136      // Safety note : This could cause some false positives due to preemption
     137      verify( TL_GET( preemption_state.enabled ) || TL_GET( this_processor )->do_terminate );
     138
     139      if( unlikely(src->cancellation != NULL) ) {
     140            _CtxCoroutine_Unwind(src->cancellation);
     141      }
     142} //ctxSwitchDirect
     143
     144void create_stack( coStack_t* this, unsigned int storageSize ) with( *this ) {
     145      //TEMP HACK do this on proper kernel startup
     146      if(pageSize == 0ul) pageSize = sysconf( _SC_PAGESIZE );
     147
     148      size_t cxtSize = libCeiling( sizeof(machine_context_t), 8 ); // minimum alignment
     149
     150      if ( !storage ) {
     151            __cfaabi_dbg_print_safe("Kernel : Creating stack of size %zu for stack obj %p\n", cxtSize + size + 8, this);
     152
     153            userStack = false;
     154            size = libCeiling( storageSize, 16 );
     155            // use malloc/memalign because "new" raises an exception for out-of-memory
     156
     157            // assume malloc has 8 byte alignment so add 8 to allow rounding up to 16 byte alignment
     158            __cfaabi_dbg_debug_do( storage = memalign( pageSize, cxtSize + size + pageSize ) );
     159            __cfaabi_dbg_no_debug_do( storage = malloc( cxtSize + size + 8 ) );
     160
     161            __cfaabi_dbg_debug_do(
     162                  if ( mprotect( storage, pageSize, PROT_NONE ) == -1 ) {
     163                        abort( "(uMachContext &)%p.createContext() : internal error, mprotect failure, error(%d) %s.", this, (int)errno, strerror( (int)errno ) );
     164                  } // if
     165            );
     166
     167            if ( (intptr_t)storage == 0 ) {
     168                  abort( "Attempt to allocate %zd bytes of storage for coroutine or task execution-state but insufficient memory available.", size );
     169            } // if
     170
     171            __cfaabi_dbg_debug_do( limit = (char *)storage + pageSize );
     172            __cfaabi_dbg_no_debug_do( limit = (char *)libCeiling( (unsigned long)storage, 16 ) ); // minimum alignment
     173
     174      } else {
     175            __cfaabi_dbg_print_safe("Kernel : stack obj %p using user stack %p(%u bytes)\n", this, storage, storageSize);
     176
     177            assertf( ((size_t)storage & (libAlign() - 1)) == 0ul, "Stack storage %p for task/coroutine must be aligned on %d byte boundary.", storage, (int)libAlign() );
     178            userStack = true;
     179            size = storageSize - cxtSize;
     180
     181            if ( size % 16 != 0u ) size -= 8;
     182
     183            limit = (char *)libCeiling( (unsigned long)storage, 16 ); // minimum alignment
     184      } // if
     185      assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %d bytes for a stack.", size, MinStackSize );
     186
     187      base = (char *)limit + size;
     188      context = base;
     189      top = (char *)context + cxtSize;
    182190}
    183191
     
    185193// is not inline (We can't inline Cforall in C)
    186194extern "C" {
    187         void __suspend_internal(void) {
    188                 suspend();
    189         }
    190 
    191         void __leave_coroutine( coroutine_desc * src ) {
    192                 coroutine_desc * starter = src->cancellation != 0 ? src->last : src->starter;
    193 
    194                 src->state = Halted;
    195 
    196                 assertf( starter != 0,
    197                         "Attempt to suspend/leave coroutine \"%.256s\" (%p) that has never been resumed.\n"
    198                         "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",
    199                         src->name, src );
    200                 assertf( starter->state != Halted,
    201                         "Attempt by coroutine \"%.256s\" (%p) to suspend/leave back to terminated coroutine \"%.256s\" (%p).\n"
    202                         "Possible cause is terminated coroutine's main routine has already returned.",
    203                         src->name, src, starter->name, starter );
    204 
    205                 CoroutineCtxSwitch( src, starter );
    206         }
     195      void __suspend_internal(void) {
     196            suspend();
     197      }
     198
     199      void __leave_coroutine() {
     200            coroutine_desc * src = TL_GET( this_coroutine ); // optimization
     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      }
    207216}
    208217
Note: See TracChangeset for help on using the changeset viewer.