Coroutines/MYCoroutine.m
author Jens Alfke <jens@mooseyard.com>
Wed Apr 30 14:18:49 2008 -0700 (2008-04-30)
changeset 1 2475f871c218
parent 0 deb0ee0c5b21
permissions -rw-r--r--
Rewrote CoroX. Simplified APIs. Improved stack size checks.
     1 //
     2 //  MYCoroutine.m
     3 //  Coroutines
     4 //
     5 //  Created by Jens Alfke on 4/29/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //  License is at the bottom of this file.
     8 //
     9 
    10 #import "MYCoroutine.h"
    11 #import "CoroX.h"
    12 
    13 
    14 #ifndef LogTo
    15 #define kEnableLog 1    /* 1 enables logging, 0 disables it */
    16 #define LogTo(DOMAIN,MSG,...) do{ if(kEnableLog) NSLog(@""#DOMAIN ": " MSG,__VA_ARGS__); }while(0)
    17 #endif
    18 
    19 #ifndef Warn
    20 #define Warn(MSG,...) NSLog(@"WARNING: " #MSG,__VA_ARGS__)
    21 #endif
    22 
    23 
    24 static void MYCoroutineStart( CoroX *coro );
    25 
    26 
    27 @implementation MYCoroutine
    28 
    29 
    30 static MYCoroutine *sMain, *sCurrent;
    31 
    32 static NSMutableArray *sCallers;
    33 static id sYieldResult;
    34 
    35 
    36 - (id) _initMain
    37 {
    38     self = [super init];
    39     if (self != nil) {
    40         NSAssert(!sMain,@"Already created a main coroutine");
    41         _coro = CoroX_new(NULL,NULL);
    42         self.name = @"MAIN";
    43         sMain = self;
    44         LogTo(Coroutine,@"INIT %@ : _coro=%p",self,_coro);
    45     }
    46     return self;
    47 }
    48 
    49 
    50 - (id) init
    51 {
    52     self = [super init];
    53     if (self != nil) {
    54         _coro = CoroX_new(&MYCoroutineStart,self);
    55         LogTo(Coroutine,@"INIT %@ : _coro=%p",self,_coro);
    56     }
    57     return self;
    58 }
    59 
    60 
    61 - (void) dealloc
    62 {
    63     LogTo(Coroutine,@"DEALLOC %@",self);
    64     CoroX_free(_coro);
    65     [super dealloc];
    66 }
    67 
    68 
    69 + (void) initialize
    70 {
    71     if( self == [MYCoroutine class] ) {
    72         sCurrent = [[self alloc] _initMain];
    73         sCallers = [[NSMutableArray alloc] init];
    74     }
    75 }
    76 
    77 
    78 - (NSString*) description
    79 {
    80     if( _name )
    81         return [NSString stringWithFormat: @"%@[%@]", [self class],_name];
    82     else
    83         return [NSString stringWithFormat: @"%@[%p]", [self class],self];
    84 }
    85 
    86 
    87 @synthesize name=_name, invocation=_invocation;
    88 
    89 
    90 + (MYCoroutine*) mainCoroutine          {return sMain;}
    91 + (MYCoroutine*) currentCoroutine       {return sCurrent;}
    92 
    93 - (BOOL) isCurrent                      {return self==sCurrent;}
    94 
    95 - (const void*) stack                   {return CoroX_stack(_coro);}
    96 - (size_t) stackSize                    {return CoroX_stackSize(_coro);}
    97 - (void) setStackSize: (size_t)size     {CoroX_setStackSize_(_coro,size);}
    98 
    99 + (void) setDefaultStackSize: (size_t)s {CoroX_setDefaultStackSize(s);}
   100 
   101 - (size_t) bytesLeftOnStack             {return CoroX_bytesLeftOnStack(_coro);}
   102 - (BOOL) stackSpaceAlmostGone           {return CoroX_stackSpaceAlmostGone(_coro);}
   103 
   104 - (BOOL) hasStarted                     {return CoroX_stack(_coro)!=NULL || self==sMain;}
   105 
   106 
   107 - (void) _start
   108 {
   109     if( _invocation )
   110         [_invocation invoke];
   111     else
   112         [self main];
   113     // If body returns, yield control to caller:
   114     LogTo(Coroutine,@"%@ finished",self);
   115     [MYCoroutine yieldToCaller: nil];
   116 }
   117 
   118 static void MYCoroutineStart( CoroX *coro )
   119 {
   120     MYCoroutine *self = CoroX_userData(coro);
   121     [self _start];
   122 }
   123 
   124 
   125 + (MYCoroutine*) startWithInvocation: (NSInvocation*)invocation
   126 {
   127     MYCoroutine *cr = [[self alloc] init];
   128     cr.invocation = invocation;
   129     [cr resume];
   130     return cr;
   131 }
   132 
   133 
   134 - (void) main
   135 {
   136     // subclasses should override this if they don't use an invocation.
   137 }
   138 
   139 
   140 #pragma mark -
   141 #pragma mark CALLING:
   142 
   143 
   144 - (void) resume
   145 {
   146     LogTo(Coroutine,@"Resuming %@ (currently in %@)",self,sCurrent);
   147     MYCoroutine *current = sCurrent;
   148     sCurrent = self;
   149     CoroX_switchTo_(current->_coro,_coro);
   150     sCurrent = current;
   151     LogTo(Coroutine,@"...resumed %@",sCurrent);
   152 }
   153 
   154 
   155 - (id) call
   156 {
   157     NSAssert(sCurrent!=self,@"Cannot call the current coroutine");
   158     [sCallers addObject: sCurrent];
   159     [self resume];
   160     id result = sYieldResult;
   161     sYieldResult = nil;
   162     return result;
   163 }
   164 
   165 
   166 + (void) yieldToCaller: (id)value
   167 {
   168     MYCoroutine *caller = sCallers.lastObject;
   169     NSAssert(caller, @"No caller to yield to");
   170     sYieldResult = value;
   171     [sCallers removeLastObject];
   172     [caller resume];
   173 }
   174 
   175 
   176 
   177 @end
   178 
   179 
   180 
   181 
   182 /*
   183  (This is a BSD License)
   184  
   185  Copyright (c) 2008 Jens Alfke
   186  All rights reserved.
   187  
   188  Redistribution and use in source and binary forms, with or without modification, are
   189  permitted provided that the following conditions are met:
   190  
   191  • Redistributions of source code must retain the above copyright notice, this list of
   192  conditions and the following disclaimer.
   193  • Redistributions in binary form must reproduce the above copyright notice, this list
   194  of conditions and the following disclaimer in the documentation and/or other materials
   195  provided with the distribution.
   196  • Neither the name of the author nor the names of other contributors may be used to
   197  endorse or promote products derived from this software without specific prior written
   198  permission.
   199  
   200  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
   201  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   202  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   203  THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   204  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   205  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   206  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
   207  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   208  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   209 */