Coroutines/CoroX.c
author Jens Alfke <jens@mooseyard.com>
Tue Apr 29 17:05:32 2008 -0700 (2008-04-29)
changeset 0 deb0ee0c5b21
child 1 2475f871c218
permissions -rw-r--r--
First checkin
jens@0
     1
/*
jens@0
     2
 *  CoroX.c
jens@0
     3
 *  Coroutines for Mac OS X
jens@0
     4
 *
jens@0
     5
 *  Created by Jens Alfke on 4/29/08.
jens@0
     6
 *  Adapted from Steve Dekorte's libCoroutine:
jens@0
     7
 *  <http://www.dekorte.com/projects/opensource/libCoroutine/>
jens@0
     8
 *  by putting it on a piece of wood and banging a few nails through it.
jens@0
     9
 *  No, actually I removed all the stuff for cross-platform support, leaving only the simple
jens@0
    10
 *  code that works on Mac OS X 10.5, and then cleaned things up a bit.
jens@0
    11
 *
jens@0
    12
 *  Copyright 2008 Jens Alfke. All rights reserved.
jens@0
    13
 *  Copyright (c) 2002, 2003 Steve Dekorte. All rights reserved.
jens@0
    14
 *  License is at the bottom of this file.
jens@0
    15
 *
jens@0
    16
 */
jens@0
    17
jens@0
    18
#include "CoroX.h"
jens@0
    19
#include <ucontext.h>
jens@0
    20
#include <stddef.h>
jens@0
    21
#include <stdio.h>
jens@0
    22
#include <assert.h>
jens@0
    23
jens@0
    24
jens@0
    25
#define CORO_DEFAULT_STACK_SIZE (65536*4)
jens@0
    26
#define CORO_STACK_SIZE_MIN     8192
jens@0
    27
jens@0
    28
jens@0
    29
struct Coro
jens@0
    30
{        
jens@0
    31
    size_t stackSize;
jens@0
    32
    void *stack;
jens@0
    33
    ucontext_t env;
jens@0
    34
    // The following field works around bug(?) in sys/_structs.h. This field should be part of ucontext_t:
jens@0
    35
    _STRUCT_MCONTEXT	env_registers; 
jens@0
    36
    unsigned char isMain;
jens@0
    37
};
jens@0
    38
jens@0
    39
jens@0
    40
typedef struct CallbackBlock
jens@0
    41
{
jens@0
    42
    void *context;
jens@0
    43
    CoroStartCallback *func;
jens@0
    44
} CallbackBlock;
jens@0
    45
jens@0
    46
jens@0
    47
Coro *Coro_new(void)
jens@0
    48
{
jens@0
    49
    Coro *self = (Coro *)calloc(1, sizeof(Coro));
jens@0
    50
    self->stackSize = CORO_DEFAULT_STACK_SIZE;
jens@0
    51
    self->stack = NULL;
jens@0
    52
    return self;
jens@0
    53
}
jens@0
    54
jens@0
    55
void Coro_free(Coro *self)
jens@0
    56
{
jens@0
    57
    if (self->stack)
jens@0
    58
        free(self->stack);
jens@0
    59
    free(self);
jens@0
    60
}
jens@0
    61
jens@0
    62
jens@0
    63
void *Coro_stack(Coro *self)
jens@0
    64
{
jens@0
    65
    return self->stack;
jens@0
    66
}
jens@0
    67
jens@0
    68
size_t Coro_stackSize(Coro *self)
jens@0
    69
{
jens@0
    70
    return self->stackSize;
jens@0
    71
}
jens@0
    72
jens@0
    73
void Coro_setStackSize_(Coro *self, size_t sizeInBytes)
jens@0
    74
{
jens@0
    75
    self->stackSize = sizeInBytes;
jens@0
    76
}
jens@0
    77
jens@0
    78
uint8_t *Coro_CurrentStackPointer(void) __attribute__ ((noinline));
jens@0
    79
jens@0
    80
uint8_t *Coro_CurrentStackPointer(void)
jens@0
    81
{
jens@0
    82
    uint8_t a;
jens@0
    83
    uint8_t *b = &a;
jens@0
    84
    return b;
jens@0
    85
}
jens@0
    86
jens@0
    87
size_t Coro_bytesLeftOnStack(Coro *self)
jens@0
    88
{
jens@0
    89
    uint8_t dummy;
jens@0
    90
    ptrdiff_t p1 = (ptrdiff_t)(&dummy);
jens@0
    91
    ptrdiff_t p2 = (ptrdiff_t)Coro_CurrentStackPointer();
jens@0
    92
    int stackMovesUp = p2 > p1;
jens@0
    93
    ptrdiff_t start = ((ptrdiff_t)self->stack);
jens@0
    94
    ptrdiff_t end = start + self->stackSize;
jens@0
    95
    
jens@0
    96
    if (stackMovesUp)
jens@0
    97
    {
jens@0
    98
        return end - p1;
jens@0
    99
    }
jens@0
   100
    else
jens@0
   101
    {
jens@0
   102
        return p1 - start;
jens@0
   103
    }
jens@0
   104
}
jens@0
   105
jens@0
   106
int Coro_stackSpaceAlmostGone(Coro *self)
jens@0
   107
{
jens@0
   108
    return Coro_bytesLeftOnStack(self) < CORO_STACK_SIZE_MIN;
jens@0
   109
}
jens@0
   110
jens@0
   111
jens@0
   112
void Coro_initializeMainCoro(Coro *self)
jens@0
   113
{
jens@0
   114
    self->isMain = 1;
jens@0
   115
}
jens@0
   116
jens@0
   117
jens@0
   118
static void Coro_StartWithArg(CallbackBlock *block)
jens@0
   119
{
jens@0
   120
    (block->func)(block->context);
jens@0
   121
    fprintf(stderr,"CoroX error: returned from coro start function\n");
jens@0
   122
    abort();
jens@0
   123
}
jens@0
   124
jens@0
   125
typedef void (*makecontext_func)(void);
jens@0
   126
jens@0
   127
static void Coro_setup(Coro *self, void *arg)
jens@0
   128
{
jens@0
   129
    ucontext_t *ucp = (ucontext_t *) &self->env;
jens@0
   130
    getcontext(ucp);
jens@0
   131
    
jens@0
   132
    ucp->uc_stack.ss_sp = Coro_stack(self);
jens@0
   133
    ucp->uc_stack.ss_size = Coro_stackSize(self);
jens@0
   134
    ucp->uc_stack.ss_flags = 0;
jens@0
   135
    ucp->uc_link = NULL;
jens@0
   136
    
jens@0
   137
    makecontext(ucp, (makecontext_func)Coro_StartWithArg, 1, arg);
jens@0
   138
}
jens@0
   139
jens@0
   140
jens@0
   141
void Coro_startCoro_(Coro *self, Coro *other, void *context, CoroStartCallback *callback)
jens@0
   142
{
jens@0
   143
    assert(other->stack==NULL);
jens@0
   144
    other->stack = calloc(1, other->stackSize + 16);
jens@0
   145
jens@0
   146
    CallbackBlock callbackBlock = {context,callback};
jens@0
   147
    Coro_setup(other, &callbackBlock);
jens@0
   148
    
jens@0
   149
    Coro_switchTo_(self, other);
jens@0
   150
}
jens@0
   151
jens@0
   152
void Coro_switchTo_(Coro *self, Coro *next)
jens@0
   153
{
jens@0
   154
    swapcontext(&self->env, &next->env);
jens@0
   155
}
jens@0
   156
jens@0
   157
jens@0
   158
jens@0
   159
jens@0
   160
/*
jens@0
   161
 (This is a BSD License)
jens@0
   162
 
jens@0
   163
 Copyright (c) 2002, 2003 Steve Dekorte
jens@0
   164
 Copyright (c) 2008 Jens Alfke
jens@0
   165
 All rights reserved.
jens@0
   166
 
jens@0
   167
 Redistribution and use in source and binary forms, with or without modification, are
jens@0
   168
 permitted provided that the following conditions are met:
jens@0
   169
 
jens@0
   170
 • Redistributions of source code must retain the above copyright notice, this list of
jens@0
   171
 conditions and the following disclaimer.
jens@0
   172
 • Redistributions in binary form must reproduce the above copyright notice, this list
jens@0
   173
 of conditions and the following disclaimer in the documentation and/or other materials
jens@0
   174
 provided with the distribution.
jens@0
   175
 • Neither the name of the author nor the names of other contributors may be used to
jens@0
   176
 endorse or promote products derived from this software without specific prior written
jens@0
   177
 permission.
jens@0
   178
 
jens@0
   179
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
jens@0
   180
 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
jens@0
   181
 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
jens@0
   182
 THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
jens@0
   183
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
jens@0
   184
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
jens@0
   185
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
jens@0
   186
 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
jens@0
   187
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
   188
*/