/*
 * sThreadsDynamicEnvironment.c -- stubbed out PCR, based on
 * Mike Agostino's pseudoPCR
 */

#include <xr/ThreadsDynamicEnvironment.h>
#include <xr/sMisc.h>

XR←DynFrame SPCR←dynEnv = 0;

static int XR←unwindFrameKeyValueIsMyAddress;

XR←Pointer XR←UnwindFrameKey =
        (XR←Pointer) (&XR←unwindFrameKeyValueIsMyAddress);

XR←DynFrame
XR←GetDynamicEnvironment()
{
    return (SPCR←dynEnv);
}

XR←DynFrame
XR←GetDynamicEnvironmentOf(thread)
XR←Thread thread;
{
    if (thread != XR←currThread) {
        SPCR←error("XR←GetDynamicEnvironmentOf(bad thread)");
    }
    return (SPCR←dynEnv);
}

void
XR←SetDynamicEnvironment(f)
    XR←DynFrame f;
{
    SPCR←dynEnv = f;
}

XR←DynFrame
XR←LookupInDynamicEnvironment(key, de)
    XR←Pointer key;
    XR←DynFrame de;
{
    XR←DynFrame f;
  
    for (f = de; f != NIL; f = f->df←link) {
        if (((unsigned) f) & 0x3) {
            SPCR←error("link on dynamic chain was not 4-byte aligned ptr");
	}
        if (f->df←key == key)
            return f;
    }
  
    return NIL;
}


static unsigned
EnvironmentLength(de)
    XR←DynFrame de;
{
    unsigned len = 0;
  
    while (de != NIL) {
        len++;
        de = de->df←link;
    }
    return len;
}


XR←DynFrame
XR←FirstCommonParent(de1, de2)
    XR←DynFrame de1, de2;
{
    unsigned len1 = EnvironmentLength(de1);
    unsigned len2 = EnvironmentLength(de2);

    while (len1 > len2) {
        de1 = de1->df←link;
        len1--;
    };
  
    while (len2 > len1) {
        de2 = de2->df←link;
        len2--;
    };
   
    /* de1 and de2 are now the same length */
    while (de1 != de2) {
        de1 = de1->df←link;
        de2 = de2->df←link;
    };
  
    return de1;
}


int
XR←UnwindTo(target)
XR←DynFrame target;
{
    XR←Pointer myKey = XR←UnwindFrameKey;
    XR←DynFrame de = XR←GetDynamicEnvironment();
    XR←DynFrame f;
  
    for (f = de; (f != NIL) && (f != target); f = f->df←link)
        ;
    if (f == NIL)
        return -1;
  
    for (f = de; f != target; f = f->df←link) {
        if (f->df←key == myKey) {
            XR←UnwindFrame uf = (XR←UnwindFrame) f;
            XR←MesaProc unwinder = uf->uf←unwinder;
      
            if (unwinder != NIL) {
                XR←SetDynamicEnvironment(uf->uf←link);
                (*(unwinder->mp←proc))(unwinder);
            }
        }
    }
    XR←SetDynamicEnvironment(target);
    return 0;
}


int
XR←RewindTo(target)
XR←DynFrame target;
{
  extern void DoRewind(/* end, f, key */);
  XR←DynFrame de = XR←GetDynamicEnvironment();
  XR←DynFrame f;
  
    for (f = target; (f != NIL) && (f != de); f = f->df←link)
        ;
    if (f == NIL)
        return -1;
  
    DoRewind(de, target, XR←UnwindFrameKey);
    XR←SetDynamicEnvironment(target);
    return 0;
}


#define REWIND←BATCH	32

static void
DoRewind(end, f, key) /* call all rewinders in (end..f] in that order */
    XR←DynFrame end, f;
    XR←Pointer key;
{
    XR←DynFrame stack[REWIND←BATCH];
    int i = 0;

    for(;;) {
        if( f == NIL ) XR←Panic("DoRewind 0");
        if( f == end ) break;
        if( f->df←key == key ) {
            if( i == REWIND←BATCH ) {
                DoRewind(end, f, key);
                break;
            } else {
                stack[i] = f;
                i++;
            }
        }
        f = f->df←link;
    }
    while( i > 0 ) {
        XR←UnwindFrame uf;
        XR←MesaProc rewinder;
        i -= 1;
        uf = (XR←UnwindFrame)(stack[i]);
        if( (rewinder = uf->uf←rewinder) != NIL ) {
            XR←SetDynamicEnvironment(uf->uf←link);
            (*(rewinder->mp←proc))(rewinder);
        }
    }
}