 /*
 * These used to be compile-time options to Browser.
 *
 * Obviously this makes no sense for public code, so I have
 *  stuck all of that here.
 *
 * Makes Browser less unintelligble :^/
 *
 * (C) Copyright 1991 David C. Navas
 */
#include <shadow/coreMeta.h>
#include <shadow/coreRoot.h>
#include <shadow/process.h>
#include <shadow/semaphore.h>

#include <ipc.h>
#include <ipc_proto.h>

#include <shadow/shadow_proto.h>
#include <shadow/shadow_pragmas.h>
#include <shadow/method.h>
#include <dos/dostags.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <proto/exec.h>
#include <math.h>

extern struct ExecBase * __far SysBase;
struct Library * __far IPCBase;
struct ShadowBase * __far ShadowBase;
struct DosLibrary * __far DOSBase;


/*
 * To test the binary tree speed.
 */
void BinSpeedTest(void);

/*
 * To test the memory speed.
 */
void MemorySpeedTest(void);
void MemorySpeedTest2(void);

/*
 * To test the semaphore speed.
 */
void SemaphoreSpeedTest(void);

/*
 * To test the FindString() speed.
 */
void StringSpeedTest(void);

/*
 * This is for the method testing and patching/attribute tests.
 */

#define DOS_PROCESS_CLASS  "dos process class"
#define NEW_SUPER_CLASS    "testing new superclass!"
#define FAKE_DOS_TASK      "Fake dos Task"
#define PROCESS_TASK       "testing subprocess..."
#define TEST_PORT_NAME     "My perftesting port"

#define METH_TEST_SPEED    "Method Tst5"
#define METH_TEST_PROC1    "Method test SHADOW_MSG_PREPROC"
#define METH_TEST_PROC2    "Method test SHADOW_MSG_NEVER_PREPROC"
#define METH_TEST_PORT     "Method test send to PORT type"
#define METH_TEST_SEM1     "Method test semaphore1"
#define METH_TEST_SEM2     "Method test semaphore2"

#define METH_TEST_SPEC1    "Method test Spec Process"
#define METH_TEST_SPEC2    "Method test Spec Class"
#define METH_TEST_SPEC3    "Method test Spec Port"

#define METH_TEST_SPEC4    "Method test Spec Process II"
#define METH_TEST_SPEC5    "Method test Spec Class II"
#define METH_TEST_SPEC6    "Method test Spec Port II"

#define METH_NEW_SUPER     "Method testing new superclass?"

struct MethodInvokeSpec spec1 = {
                           PROCESS_TASK,
                           PROCESS_CLASS,
                           META_CLASS,
                           SPEC_SAVE_BINDING
                        },
                        spec2 = {
                           NULL,
                           PROCESS_CLASS,
                           META_CLASS,
                           SPEC_SAVE_BINDING
                        },
                        spec3 = {
                           TEST_PORT_NAME,
                           NULL,
                           NULL,
                           SPEC_SAVE_BINDING
                        };
struct MethodInvokeSpec spec4 = {
                           FAKE_DOS_TASK,
                           DOS_PROCESS_CLASS,
                           META_CLASS,
                           NULL
                        },
                        spec5 = {
                           NULL,
                           DOS_PROCESS_CLASS,
                           META_CLASS,
                           NULL
                        },
                        spec6 = {
                           TEST_PORT_NAME,
                           NULL,
                           NULL,
                           NULL
                        };

extern ARGUMENT_TAG  REF_TestMethod[],
                     REF_SendTestMethod[],
                     REF_SendTest5Method[];

long TestMethod(METHOD_ARGS, char *), SendTestMethod(METHOD_ARGS,
                                                     OBJECT testObject);
long Test5Method(METHOD_ARGS), SendTest5Method(METHOD_ARGS, OBJECT);

void ProcTestMethod(METHOD_ARGS);
void TestSemMethod(METHOD_ARGS);
void __saveds TestSem2Method(METHOD_ARGS);

#define SPEEDTESTNUMBER  100000

/*
 * The Process shutdown test.
 */

METHOD_TAG procMethods[] = {
                              {
                                 METH_REMOVE,
                                 NULL, NULL,
                                 INVOKE_FORCE_SYNC,
                                 METH_FLAG_OBJECT_AS_DEST | METH_FLAG_NO_RTRN,
                                 0,
                                 (METHODFUNCTYPE)ProcTestMethod, NULL
                              },
                              TAG_END
                           };


/*
 * Class that is to be run by "testing subprocess..."
 */

/*
 * These methods are called by this test program in main()
 */
METHOD_TAG methods2[] =
                        {
                           {
                              "Method TEST",
                              NULL, NULL,
                              INVOKE_SYNC,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)SendTestMethod,
                                 REF_SendTestMethod
                           },
                           {
                              METH_TEST_SPEED,
                              NULL, NULL,
                              INVOKE_SYNC,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)SendTest5Method,
                                 REF_SendTest5Method
                           },
                           {
                              METH_TEST_PROC1,
                              NULL, NULL,
                              INVOKE_FORCE_SYNC,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           {
                              METH_TEST_PROC2,
                              NULL, NULL,
                              INVOKE_FORCE_SYNC|INVOKE_IGNORE_PROCESS,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
#define TEST_PORT_NUM 4
                           {
                              METH_TEST_PORT,
                              NULL, NULL,
                              INVOKE_FORCE_ASYNC,
                              METH_FLAG_PORT, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           {
                              METH_TEST_SEM1,
                              NULL, NULL,
                              INVOKE_CALL,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)TestSemMethod, NULL
                           },
                           {
                              METH_TEST_SEM2,
                              NULL, NULL,
                              INVOKE_FORCE_ASYNC,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)TestSem2Method, NULL
                           },
                           {
                              METH_TEST_SPEC1,
                              &spec1, NULL,
                              INVOKE_FORCE_SYNC,
                              METH_FLAG_OBJECT | METH_FLAG_SPEC, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           {
                              METH_TEST_SPEC2,
                              &spec2, NULL,
                              INVOKE_FORCE_SYNC,
                              METH_FLAG_CLASS | METH_FLAG_SPEC, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           {
                              METH_TEST_SPEC3,
                              &spec3, NULL,
                              INVOKE_FORCE_ASYNC,
                              METH_FLAG_PORT | METH_FLAG_SPEC, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           {
                              METH_TEST_SPEC4,
                              &spec4, NULL,
                              INVOKE_FORCE_SYNC,
                              METH_FLAG_OBJECT | METH_FLAG_SPEC, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           {
                              METH_TEST_SPEC5,
                              &spec5, NULL,
                              INVOKE_FORCE_SYNC,
                              METH_FLAG_CLASS | METH_FLAG_SPEC, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           {
                              METH_TEST_SPEC6,
                              &spec6, NULL,
                              INVOKE_FORCE_ASYNC,
                              METH_FLAG_PORT | METH_FLAG_SPEC, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           TAG_END
                        },
                  newMethods[] =
                        {
                           {
                              METH_NEW_SUPER,
                              NULL, NULL,
                              INVOKE_CALL,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)ProcTestMethod, NULL
                           },
                           TAG_END
                        };

struct TestDefaultAttribute
{
   long thing;
};

ATTRIBUTE_TAG attrs2[] =
                        {
                           {
                              "A default attribute",
                              sizeof(struct TestDefaultAttribute),
                              NULL
                           },
                           TAG_END
                        };


/*
 * Class that is to be run by the "Fake dos Process..."
 */

/*
 * These are the methods that are called in the inner loop by the
 *  methods defined above.
 */
METHOD_TAG methods[] =
                        {
                           {
                              "Method TEST",
                              NULL, NULL,
                              INVOKE_SYNC,
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)TestMethod, REF_TestMethod
                           },
                           {
                              METH_TEST_SPEED,
                              NULL, NULL,
                              INVOKE_CALL,         /* Edit this one! */
                              METH_FLAG_OBJECT, 0,
                              (METHODFUNCTYPE)Test5Method, NULL
                           },
                           TAG_END
                        };

/*
 * The patches
 */
extern double PreTestMethod(METHOD_ARGS);

METHOD_TAG preMethod =
                           {
                              "Method TEST",
                              NULL, NULL,
                              INVOKE_SYNC,
                              METH_FLAG_CHECK_CONTINUE |
                              METH_FLAG_CLASS | METH_FLAG_NO_RTRN,
                              1, /* The priority */
                              (METHODFUNCTYPE)PreTestMethod, NULL
                           };

extern long PostTestMethod(METHOD_ARGS);

METHOD_TAG postMethod =
                           {
                              "Method TEST",
                              NULL, NULL,
                              INVOKE_SYNC,
                              METH_FLAG_OBJECT | METH_FLAG_NO_RTRN, -1,
                              (METHODFUNCTYPE)PostTestMethod, NULL
                           };



/*
 * A default attribute, just to show you how they work.
 */
struct TestDefaultAttribute theDefAtt = {0xFEED};

/*
 * Here, we not only test the default attribute, but we show that you
 * can give a new class a default attribute for an attribute of the new
 * class' superclass.
 */
ATTRIBUTE_TAG attrs[] =
                        {
                           {
                              "A default attribute",
                              sizeof(struct TestDefaultAttribute),
                              &theDefAtt
                           },
                           TAG_END
                        };


/*
 * Lattice, go away.
 */
long CXBRK(void)
{
   return(0);
}

/*
 * really.
 */
chkabort(void)
{
   return(0);
}


/*
 * Some memory allocation routines.  Dunno why we have these here...
 */
/*
void * __regargs temporaryAlloc(struct MemoryList *list)
{
   return AllocMem(32 * list->memlst_size + sizeof(struct MemoryNode),
                   MEMF_PUBLIC);
}

void __regargs temporaryFree(struct MemoryList *list,
                           struct MemoryNode *node)
{
   FreeMem(node, 32 * list->memlst_size + sizeof(struct MemoryNode));
}
*/

/*
 * Possible test constants
 */
#define MEMORYTEST   "MEMORY"
#define SPEEDTEST    "METHOD"
#define FEATURETEST  "TEST"
#define AVLTREETEST  "AVL"
#define SEMTEST      "SEMAPHORE"
#define STRINGTEST   "STRING"

#define MEMORYTEST_VAL  1
#define SPEEDTEST_VAL   2
#define FEATURETEST_VAL 4
#define AVLTREETEST_VAL 8
#define SEMTEST_VAL     16
#define STRINGTEST_VAL  32

void main(long argc, char *argv[])
{
   CLASS root,
         dosTaskClass,
         testClass,
         dosClass,
         proc,
         newSuperClass;

   OBJECT procObject,
          testObject,
          dosTask,
          dosObject,
          preObject,
          postObject;
   ULONG args[10], test = 0, i;


   if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)))
   {
      DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);
      Write(Output(), "Sorry, use 2.0\n", 15);
      CloseLibrary(DOSBase);
      return;
   }

   if (argc == 0)
   {
      VPrintf("Run this from the Shell\n", NULL);
      CloseLibrary(DOSBase);
      return;
   }

   if ((argc == 1) || !stricmp(argv[1], "?"))
   {
      VPrintf("\nOkay, this is how you want to run this program:\n\t%s ",
              (ULONG *)&argv[0]);
      VPrintf(MEMORYTEST"/S,"SPEEDTEST"/S,"FEATURETEST"/S,"
              AVLTREETEST"/S,"SEMTEST"/S,"STRINGTEST"/S\n\n", NULL);
      CloseLibrary(DOSBase);
      return;
   }

   for(i = 1; i < argc; i++)
   {
      if (!stricmp(argv[i], MEMORYTEST))
         test |= MEMORYTEST_VAL;
      else
         if (!stricmp(argv[i], SPEEDTEST))
            test |= SPEEDTEST_VAL;
      else
         if (!stricmp(argv[i], FEATURETEST))
            test |= FEATURETEST_VAL;
      else
         if (!stricmp(argv[i], AVLTREETEST))
            test |= AVLTREETEST_VAL;
      else
         if (!stricmp(argv[i], SEMTEST))
            test |= SEMTEST_VAL;
      else
         if (!stricmp(argv[i], STRINGTEST))
            test |= STRINGTEST_VAL;
      else
         VPrintf("Disregarding Parameter <%s>\n", (ULONG *)&argv[i]);
   }

   if (!(IPCBase = OpenLibrary("ppipc.library", 0)))
   {
      VPrintf("requires ppipc.library in libs:\n", NULL);
      CloseLibrary(DOSBase);
      return;
   }

   if (!(ShadowBase = (struct ShadowBase *)
                       OpenLibrary("shadow.library", 5)))
   {
      VPrintf("requires shadow.library V5.x in libs:\n", NULL);
      CloseLibrary(IPCBase);
      CloseLibrary(DOSBase);
      return;
   }

   if (!InitOOProgram("Performance Tester Program"))
   {
      CloseLibrary(IPCBase);
      CloseLibrary(ShadowBase);
      CloseLibrary(DOSBase);
      return;
   }

   if (test & AVLTREETEST_VAL)
      BinSpeedTest();

   if (test & MEMORYTEST_VAL)
   {
      MemorySpeedTest();
      MemorySpeedTest2();
   }

   if (test & SEMTEST_VAL)
      SemaphoreSpeedTest();

   if (test & STRINGTEST_VAL)
      StringSpeedTest();

   /*
    * What follows below is a complete mess.
    * The author realizes it, but doesn't care enough about this
    *  particular stretch of code to do much about it.
    * Please, refer to the other examples provided for better use of
    *  the shadow.lib functions.
    * If you like, think of this as an example of more direct mucking
    *  about with creating classes and objects, without
    *  Create[Instance|SubClass]
    */
   proc = FindShadowClass(PROCESS_CLASS);
   root = FindShadowClass(ROOT_CLASS);

   if (test & (SPEEDTEST_VAL | FEATURETEST_VAL))
   {
      struct Meta *MHClass;

      SetupMethodTags(procMethods, (void *)-1, (void *)-1);
      dosTaskClass = DoShadow(proc, NULL, METH_SUB,
                                              DOS_PROCESS_CLASS,
                                              NULL,
                                              NULL,
                                              procMethods,
                                              METHOD_END);

      args[0] = (ULONG)proc;
      args[1] = proc->ccl_size;
      args[2] = (ULONG)dosTaskClass;
      VPrintf("Defined procClass:<%lx> : %ld bytes :: dosProcClass:<%lx>\n",
              args);

      args[0] = NP_Output;
      args[1] = (ULONG)Open("CONSOLE:", MODE_OLDFILE);
      args[2] = TAG_END;
      dosTask = DoShadow(dosTaskClass, NULL, METH_CREATE,
                                                 METHOD_END);
      dosTask = DoShadow(dosTask, NULL, METH_INIT,
                                            FAKE_DOS_TASK,
                                            NULL,
                                            NULL,
                                            args,
                                            METHOD_END);
      /*
       * if didn't Open, then free the resources.
       */
      if (args[0] != TAG_IGNORE)
      {
         VPrintf("Couldn't start the Fake dos Task\n", NULL);
         Close(args[1]);
      }

      args[0] = NP_Output;
      args[1] = (ULONG)Open("CONSOLE:", MODE_OLDFILE);
      args[2] = TAG_END;
      procObject = DoShadow(proc, NULL, METH_CREATE, METHOD_END);
      procObject = DoShadow(procObject, NULL, METH_INIT,
                                                  PROCESS_TASK,
                                                  NULL,
                                                  NULL,
                                                  args,
                                                  METHOD_END);
      /*
       * if didn't Open, then free the resources.
       */
      if (args[0] != TAG_IGNORE)
      {
         VPrintf("Couldn't start the testing subprocess...", NULL);
         Close(args[1]);
      }


      args[0] = (ULONG)procObject;
      args[1] = (ULONG)dosTask;
      VPrintf("Proc Objects %lx %lx\n\n", args);

      SetupMethodTags(methods2, procObject, (void *)-1);
      SetupMethodTags(methods, dosTask, (void *)-1);

      /*
       * Setup our one port test case.
       */
      {
         struct ShadowProcess *sp = FindAttribute(procObject,
                                                  ATTR_SHADOWPROCESS);

         methods2[TEST_PORT_NUM].mtag_procObject = sp->sp_port;
      }

      testClass = DoShadow(root, NULL, METH_SUB,
                                                      "Test Class",
                                                      NULL,
                                                      attrs2,
                                                      methods2,
                                                      METHOD_END);

      testObject = DoShadow( DoShadow(testClass,
                                              NULL,
                                              METH_CREATE,
                                              METHOD_END),
                                 NULL,
                                 METH_INIT,
                                 METHOD_END);

      dosClass = DoShadow(testClass, NULL, METH_SUB,
                                                        "Test Class Dest",
                                                        NULL,
                                                        attrs,
                                                        methods,
                                                        METHOD_END);

      dosObject = DoShadow(dosClass, NULL, METH_CREATE, METHOD_END);

      if (test & FEATURETEST_VAL)
      {
         /*
          * Well, did the attribute turn out okay?
          */
         VPrintf("Default Attribute Value :: %lx\n\n",
                 FindAttribute(dosObject, "A default attribute"));

      }
      dosObject = DoShadow(dosObject, NULL, METH_INIT, METHOD_END);


      AddAutoResource(NULL, UseObject(testClass), NULL);
      AddAutoResource(NULL, dosTaskClass, NULL);
      AddAutoResource(NULL, UseObject(dosObject), NULL);
      AddAutoResource(NULL, UseObject(dosClass), NULL);
      AddAutoResource(NULL, UseObject(testObject), NULL);
      AddAutoResource(NULL, procObject, NULL);
      AddAutoResource(NULL, UseObject(dosTask), NULL);

      if (test & FEATURETEST_VAL)
      {
         MHClass = FindShadowClass(PATCHER_CLASS);

         /*
          * Add pre and post patches
          */
         preMethod.mtag_procObject = dosTaskClass;
         preMethod.mtag_defnObject = CurrentProcess();

         preObject = DoShadow(MHClass, NULL, METH_CREATE,
                                                 METHOD_END);
         preObject = DoShadow(preObject, NULL, METH_INIT,
                                                   &preMethod,
                                                   dosClass,
                                                   METHOD_END);
         VPrintf("Added prePatch %lx\n", (ULONG *)&preObject);

         postMethod.mtag_procObject = dosTask;
         postMethod.mtag_defnObject = CurrentProcess();

         postObject = DoShadow(MHClass, NULL, METH_CREATE,
                                                  METHOD_END);
         postObject = DoShadow(postObject, NULL, METH_INIT,
                                                     &postMethod,
                                                     dosClass,
                                                     METHOD_END);
         VPrintf("Added postPatch %lx\n", (ULONG *)&postObject);

         DropObject(MHClass);

         /*
          * Method patching tests.
          */
         VPrintf("\nMETHOD TESTING\n", NULL);
         ClearShadowError();
         DoShadow(testObject, NULL, "Method doesn't exist", METHOD_END);
         {
            double myError = GetShadowError();
            VPrintf("\nMethod doesn't exist! %lx %lx\n", (ULONG *)&myError);
         }

         DoShadow(testObject, NULL, "Method TEST", dosObject,
                                                       METHOD_END);

         DoShadow(preObject, NULL, METH_REMOVE, METHOD_END);
         DropObject(preObject);
         VPrintf("Removed prePatch\n\n", NULL);

         DoShadow(testObject, NULL, "Method TEST", dosObject,
                                                       METHOD_END);

         DoShadow(postObject, NULL, METH_REMOVE, METHOD_END);
         DropObject(postObject);
         VPrintf("Removed postPatch\n\n", NULL);

         DoShadow(testObject, NULL, "Method TEST", dosObject,
                                                      METHOD_END);
         VPrintf("\nTesting DoShadowInProcess\n", NULL);

         DoShadowInProcess(dosTask, testObject, NULL, METH_TEST_PROC1,
                           METHOD_END);
         DoShadowInProcess(dosTask, testObject, NULL, METH_TEST_PROC2,
                           METHOD_END);

         VPrintf("\nTesting DoShadowAsync()", NULL);
         DoShadowAsync(testObject, NULL, METH_TEST_PROC2, METHOD_END);

         VPrintf("\nTesting PreParseShadow()\n", NULL);
         {
            struct IPCMessage *msg2;

            msg2 = PreParseShadow(testObject, NULL, METH_TEST_PROC2,
                                  METHOD_END);
            VPrintf("Message returned = %lx\n", (ULONG *)&msg2);
            ParseShadowMessage(msg2, SHADOW_RETURN_MSG_NEVER);
         }

         VPrintf("\nTesting METH_FLAG_PORT bit\n", NULL);

         DoShadow(testObject, NULL, METH_TEST_PORT, METHOD_END);

         VPrintf("\nTesting Specs\n", NULL);
         DoShadow(testObject, NULL, METH_TEST_SPEC1, METHOD_END);
         DoShadow(testObject, NULL, METH_TEST_SPEC1, METHOD_END);

         DoShadow(testObject, NULL, METH_TEST_SPEC2, METHOD_END);
         DoShadow(testObject, NULL, METH_TEST_SPEC2, METHOD_END);

         {
            struct IPCPort *port = ServeIPCPort(TEST_PORT_NAME);
            struct IPCMessage *msg;

            DoShadow(testObject, NULL, METH_TEST_SPEC3, METHOD_END);
            DoShadow(testObject, NULL, METH_TEST_SPEC3, METHOD_END);

            if (!port)
               VPrintf("Could not create port!\n", NULL);
            else
            {

               ShutIPCPort(port);
               while(msg = GetIPCMessage(port))
                  ParseShadowMessage(msg, SHADOW_RETURN_MSG_NEVER);
               LeaveIPCPort(port);
            }
         }

         DoShadow(testObject, NULL, METH_TEST_SPEC4, METHOD_END);
         DoShadow(testObject, NULL, METH_TEST_SPEC4, METHOD_END);

         DoShadow(testObject, NULL, METH_TEST_SPEC5, METHOD_END);
         DoShadow(testObject, NULL, METH_TEST_SPEC5, METHOD_END);

         {
            struct IPCPort *port = ServeIPCPort(TEST_PORT_NAME);
            struct IPCMessage *msg;

            DoShadow(testObject, NULL, METH_TEST_SPEC6, METHOD_END);
            DoShadow(testObject, NULL, METH_TEST_SPEC6, METHOD_END);

            if (!port)
               VPrintf("Could not create port!\n", NULL);
            else
            {
               ShutIPCPort(port);
               while(msg = GetIPCMessage(port))
                  ParseShadowMessage(msg, SHADOW_RETURN_MSG_NEVER);
               LeaveIPCPort(port);
            }
         }

         VPrintf("Trying to get to method not installed!\n", NULL);
         DoShadow(testObject, NULL, METH_NEW_SUPER, METHOD_END);
         SetupMethodTags(newMethods, (void *)-1, (void *)-1);
         newSuperClass = DoShadow(testClass, NULL, METH_SUPER,
                                                   NEW_SUPER_CLASS,
                                                   NULL,
                                                   NULL,
                                                   &newMethods,
                                                   METHOD_END);
         AddAutoResource(NULL, newSuperClass, NULL);
         VPrintf("Now ----installed----\n", NULL);
         DoShadow(testObject, NULL, METH_NEW_SUPER, METHOD_END);

         VPrintf("METHOD TESTING DONE\n\n", NULL);

         VPrintf("\n\t\t\tStarting semaphore testing\n", NULL);
         DoShadow(testObject, NULL, METH_TEST_SEM2, METHOD_END);
         DoShadow(testObject, NULL, METH_TEST_SEM1, METHOD_END);
      }

      if (test & SPEEDTEST_VAL)
      {
         /*
          * A3000 tests under moderate system usage (many apps Open, nothing
          *  running) shows 40000+ for methods/sec, and 240000 for funcs/sec.
          */
         DoShadow(testObject, NULL, METH_TEST_SPEED, dosObject, METHOD_END);
      }

      DropObject(dosObject);
      DropObject(dosClass);
      DropObject(testClass);
      DropObject(testObject);
      DropObject(dosTask);
   }
   DropObject(root);
   DropObject(proc);

   VPrintf("\n\nRemoving current program\n", NULL);

   RemoveCurrentProgram(NULL);

   VPrintf("Closing libraries\n", NULL);

   CloseLibrary(ShadowBase);
   CloseLibrary(IPCBase);

   VPrintf("Attempting to purge SHADOW and PPIPC libraries.\n", NULL);
   CloseLibrary(DOSBase);

   /*
    * Purge everything.
    */

   AllocMem(-1, MEMF_ANY);
}

void BinSpeedTest(void)
{
   AVLTree bt = NULL;
   long i, j;
   struct DateStamp ds1, ds2;
   struct ClasslessObject object[256];

   for(i = 0;i < 256; i++)
   {
      object[i].clb_class = NULL;
      object[i].clb_useCount = object[i].clb_size = 0;
   }


   for (i = 0; i < 256; i++)
   {
      AddTreeNode(&bt, &object[i], i);
   }

   VPrintf("Beginning SHADOW AVLTree speed test of FindTreeNode:\n", NULL);

   DateStamp((void *)&ds1);
   for(j = 0; j < 100; j++)
   {
      for (i = 0; i < 256; i++)
      {
         FindTreeNode(&bt, i);
         /*
          * We don't need to call DropObject(), so don't!
          * Realize, however, that you normally would want to resource
          *  track what it is that's returned.
          */
      }

   }
   DateStamp((void *)&ds2);

   for (i = 0; i < 256; i++)
   {
      RemoveTreeNode(&bt, &object[i], i);
   }

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW AVLTree speed test completed.\n", NULL);
   i = 50 * 25600;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld SHADOW AVLTree FindTreeNode()/second\n\n", &i);


   VPrintf("Beginning SHADOW AVLTree speed test Add/Remove:\n", NULL);

   DateStamp((void *)&ds1);
   for(j = 0; j < 50; j++)
   {
      for (i = 0; i < 256; i++)
      {
         AddTreeNode(&bt, &object[i], i);
      }
      for (i = 0; i < 256; i++)
      {
         RemoveTreeNode(&bt, &object[i], i);
      }

   }
   DateStamp((void *)&ds2);
   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW AVLTree speed test completed.\n", NULL);
   i = 50 * 12800;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld SHADOW AVLTree Adds-Removes/second\n\n", &i);

}

void MemorySpeedTest(void)
{
   long i, j;
   void *table[128];
   struct DateStamp ds1, ds2;
   struct MemoryList globalMemList;

   VPrintf("Beginning SHADOW memory allocation speed test:\n", NULL);

   InitTable(&globalMemList, NULL, NULL, 16);

   DateStamp((void *)&ds1);

   for(j = 0; j < 500; j++)
   {
      for (i = 0; i < 128; i++)
      {
         table[i] = AllocateItem(&globalMemList);
      }
      for (i = 0; i < 128; i++)
      {
         FreeItem(&globalMemList, table[i]);
      }
   }
   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW memory test completed.\n", NULL);
   i = 50 * 64000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld SHADOW memory allocs-frees/second\n\n", &i);

   /*
    * A3000 tests after decent memory usage by other programs
    * shows a bit over 12700 a second. (13502)
    */

   VPrintf("Beginning Exec allocation speed test:\n", NULL);

   DateStamp((void *)&ds1);

   for(j = 0; j < 500; j++)
   {
      for (i = 0; i < 128; i++)
      {
         table[i] = AllocMem(16, MEMF_PUBLIC);
      }
      for (i = 0; i < 128; i++)
      {
         FreeMem(table[i], 16);
      }
   }
   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("Exec memory test completed.\n", NULL);
   i = 50 * 64000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld system memory allocs-frees/second\n\n", &i);

   /*
    * A3000 tests after decent memory usage by other programs
    *  shows a bit under 8000 a second. (7673)
    *
    * The comparisons are even more disparate for slightly larger
    *  memory block sizes.  After all, very few fragments need be
    *  searched for blocks of 16bytes -- smallest frag. size is 8!
    */
   FreeTable(&globalMemList);
}

struct MyList {
   struct MyList *next;
};

void MemorySpeedTest2(void)
{
   struct MyList *list1 = NULL;
   struct MyList *list2;
   struct MyList *temp;
   struct MemoryList globalMemList;
   ULONG bytes = sizeof(struct MyList);
   long   nodes = 10000;
   long   i;

   VPrintf("Allocating single list...\n", NULL);
   for (i = 0; i < nodes; i++) {
      temp = (struct MyList *)AllocMem(bytes,NULL);
      temp->next = list1;
      list1 = temp;
   };
   VPrintf("Freeing single list...\n", NULL);
   while (list1 != NULL) {
      temp = list1;
      list1 = list1->next;
      FreeMem(temp,bytes);
   };
   VPrintf("Done with single list.\n", NULL);
   list1 = NULL;
   list2 = NULL;
   VPrintf("Allocating dual lists...\n", NULL);
   for (i = 0; i < nodes; i += 2) {
      temp = (struct MyList *)AllocMem(bytes,NULL);
      temp->next = list1;
      list1 = temp;
      temp = (struct MyList *)AllocMem(bytes,NULL);
      temp->next = list2;
      list2 = temp;
   };
   VPrintf("Freeing dual lists...\n", NULL);
   while (list1 != NULL) {
      temp = list1;
      list1 = list1->next;
      FreeMem(temp,bytes);
   };
   while (list2 != NULL) {
      temp = list2;
      list2 = list2->next;
      FreeMem(temp,bytes);
   };
   VPrintf("Done with dual lists.\n", NULL);
   list1 = NULL;
   list2 = NULL;

   InitTable(&globalMemList, NULL, NULL, bytes);
   VPrintf("Allocating dual lists SHADOW...\n", NULL);
   for (i = 0; i < nodes; i += 2) {
      temp = (struct MyList *)AllocateItem(&globalMemList);
      temp->next = list1;
      list1 = temp;
      temp = (struct MyList *)AllocateItem(&globalMemList);
      temp->next = list2;
      list2 = temp;
   };
   VPrintf("Freeing dual lists...\n", NULL);
   while (list1 != NULL) {
      temp = list1;
      list1 = list1->next;
      FreeItem(&globalMemList, temp);
   };
   while (list2 != NULL) {
      temp = list2;
      list2 = list2->next;
      FreeItem(&globalMemList,temp);
   };
   VPrintf("Done with dual lists.\n", NULL);
   list1 = NULL;
   list2 = NULL;
   FreeTable(&globalMemList);
}

void SemaphoreSpeedTest(void)
{
   long i, j;
   void *table[12];
   char bufferNames[40][12];
   struct SignalSemaphore tableSem[12];
   struct DateStamp ds1, ds2;

   VPrintf("Beginning SHADOW semaphore speed test:\n", NULL);

   for(i = 0; i < 12; i++)
   {
      table[i] = (void *)lrand48();
   }

   DateStamp((void *)&ds1);

   for(j = 0; j < 1000; j++)
   {
      for (i = 0; i < 12; i++)
      {
         PSem(table[i], SSEM_LOCK);
      }
      for (i = 0; i < 12; i++)
      {
         VSem(table[i]);
      }
/*
      PSem(NULL, SSEM_LOCK);
      VSem(NULL);
*/
   }
   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW semaphore test completed.\n", NULL);
   i = 50 * 12000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld SHADOW semaphore Psem()-VSem()/second\n\n", &i);

   /*
    * A3000 tests shows a bit over 8500 a second.
    */


   VPrintf("Beginning SHADOW semaphore pre-allocated speed test:\n", NULL);
   for (i = 0; i < 12; i++)
   {
      table[i] = PSem((void *)-1, SSEM_READ);
   }

   DateStamp((void *)&ds1);

   for(j = 0; j < 1000; j++)
   {
      for (i = 0; i < 12; i++)
      {
         PSem(table[i], SSEM_READ);
      }
      for (i = 0; i < 12; i++)
      {
         VSem(table[i]);
      }
   }
   DateStamp((void *)&ds2);

   for (i = 0; i < 12; i++)
   {
      PSem(table[i], SSEM_DESTROY);
   }

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW semaphore test completed.\n", NULL);
   i = 50 * 12000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld SHADOW semaphore Pre-allocated Psem()-VSem()/second\n\n", &i);


   VPrintf("Beginning Exec semaphore speed test:\n", NULL);

   for(i = 0; i < 12; i++)
   {
      InitSemaphore(&tableSem[i]);
   }

   DateStamp((void *)&ds1);

   for(j = 0; j < 1000; j++)
   {
      for (i = 0; i < 12; i++)
      {
         ObtainSemaphore(&tableSem[i]);
      }
      for (i = 0; i < 12; i++)
      {
         ReleaseSemaphore(&tableSem[i]);
      }
   }
   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("Exec semaphore test completed.\n", NULL);
   i = 50 * 12000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld System semaphore ObtainSemaphore()-"
           "ReleaseSemaphore()/second\n\n", &i);

   /*
    * A3000 tests shows around 33,200 a second.
    */

   VPrintf("Beginning Exec semaphore w/FindSemaphore() speed test:\n", NULL);

   for(i = 0; i < 12; i++)
   {
      RawDoFmt("SHADOW  #%lx", &i, SprintfCallback, bufferNames[i]);

      tableSem[i].ss_Link.ln_Name = bufferNames[i];
      tableSem[i].ss_Link.ln_Pri = 0;
      AddSemaphore(&tableSem[i]);
   }

   DateStamp((void *)&ds1);

   for(j = 0; j < 1000; j++)
   {
      for (i = 0; i < 12; i++)
      {
         FindSemaphore(bufferNames[i]);
         ObtainSemaphore(&tableSem[i]);
      }
      for (i = 0; i < 12; i++)
      {
         ReleaseSemaphore(&tableSem[i]);
      }
   }
   DateStamp((void *)&ds2);

   for(i = 0; i < 12; i++)
   {
      RemSemaphore(&tableSem[i]);
   }

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("Exec semaphore test w/FindSsemaphore() completed.\n", NULL);
   i = 50 * 12000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld System semaphore ObtainSemaphore()-"
           "ReleaseSemaphore()/second\n\n", &i);

   /*
    * A3000 tests shows around 7800 a second.
    */

   VPrintf("Beginning Forbid()/Permit() speed test:\n", NULL);

   DateStamp((void *)&ds1);

   for(j = 0; j < 120000; j++)
   {
      Forbid();
      Permit();
   }
   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("Forbid()/Permit() test completed.\n", NULL);
   i = 50 * 120000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld System Forbid()-Permit()/second\n\n", &i);

   /*
    * A3000 tests shows a bit over 8500 a second.
    */
}

#define STRINGTESTER "Find this string in Table"

void StringSpeedTest(void)
{
   long i;
   struct DateStamp ds1, ds2;

   VPrintf("Beginning SHADOW word-aligned FindString() speed test:\n", NULL);

   UseString(STRINGTESTER);

   DateStamp((void *)&ds1);

   for(i = 0; i < 50000; i++)
   {
      FindString(STRINGTESTER);
   }
   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW FindString() test completed.\n", NULL);
   i = 50 * 50000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld SHADOW FindString()/second\n\n", &i);

   DropString(STRINGTESTER);

   /*
    * A3000 tests shows a bit under 21000 a second.
    */

   VPrintf("Beginning SHADOW unaligned FindString() speed test:\n", NULL);

   UseString(STRINGTESTER + 1);

   DateStamp((void *)&ds1);

   for(i = 0; i < 50000; i++)
   {
      FindString(STRINGTESTER + 1);
   }
   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW FindString() test completed.\n", NULL);
   i = 50 * 50000;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld SHADOW FindString()/second\n\n", &i);

   DropString(STRINGTESTER + 1);

   /*
    * A3000 tests shows around 17000 a second -- 15%-20% slower!.
    */
}

/*
 * The speed and testing test methods.
 */
double PreTestMethod(METHOD_ARGS)
{
   static long __far myLocalVar = 0; /* YES -- __far is required! */
   union {
      double tempD;
      ULONG  tempV[2];
   } retval;
   BPTR oldoutput;

   oldoutput = SelectOutput(Open("CONSOLE:", MODE_OLDFILE));

   VPrintf("Pretest called in <%s> task.\n",
           (ULONG *)&(FindTask(NULL)->tc_Node.ln_Name));

   if (!(retval.tempV[1] = (myLocalVar++ & 1)))
      VPrintf("PreTest will block this call:\n", NULL);

   oldoutput = SelectOutput(oldoutput);
   Close(oldoutput);
   /*
    * Try to fool it into returning 100.  It won't.
    */
   retval.tempV[0] = 100;
   return retval.tempD;
}

long PostTestMethod(METHOD_ARGS)
{
   VPrintf("Post test called\n", NULL);
   return 200;
}

ARGUMENT_TAG REF_TestMethod[] = {
                                 {'JSTR', sizeof(char *), 0},
                                 {TAG_DONE, SHADOW_RETURN_BLANK, 0}
                              };

long TestMethod(METHOD_ARGS, char *string)
{
   long args[10];

   if (!string)
      string = "NULL";

   args[0] = (long)MethodID;
   if (!msg)
      args[1] = (ULONG)"SMET";
   else
      args[1] = (long)&msg->ipc_Id;
   args[2] = (long)FindTask(NULL)->tc_Node.ln_Name;
   VPrintf("Method <%s> called via <%s> Message recv'd by task <%s>.\n", args);

   args[0] = (long)class->meta_name;
   args[1] = (long)object;
   VPrintf("\tClass = '%s', OBJECT = '%lx'\n", args);

   args[0] = (long)string;
   args[1] = *(long *)(((ULONG)&string) + 4);
   VPrintf("\tArgument STRING sent <%s> :: %lx\n", args);

   return TRUE;

}

long Test5Method(METHOD_ARGS)
{
   return TRUE;
}

ARGUMENT_TAG REF_SendTestMethod[] = {
                                     {'JOBJ', sizeof(char *), SHADOW_OBJECT},
                                     {TAG_DONE, SHADOW_RETURN_BLANK, 0}
                                  };

long SendTestMethod(METHOD_ARGS, OBJECT testObject)
{
   long test, args[3];

   args[0] = (long)MethodID;
   if (!msg)
      args[1] = (ULONG)"SMET";
   else
      args[1] = (long)&msg->ipc_Id;
   args[2] = (long)FindTask(NULL)->tc_Node.ln_Name;
   VPrintf("Method <%s> called via <%s> Message recv'd by task <%s>.\n", args);

   args[0] = (long)class->meta_name;
   args[1] = (long)object;
   VPrintf("\tClass = '%s', OBJECT = '%lx'\n", args);

   args[0] = (long)testObject;
   VPrintf("\tArgument OBJECT sent <%lx>\n\n", args);

   test = (long)DoShadow(testObject, NULL, MethodID,
                                                   "Testing", METHOD_END);

   VPrintf("Method Call first Returned %ld\n\n", &test);

   test = (long)DoShadow(testObject, NULL, MethodID, METHOD_END);

   VPrintf("Method Call second Returned %ld\n\n", &test);

   test = (long)DoShadowAsync(testObject, NULL, MethodID, METHOD_END);

   VPrintf("Method Call First Async Returned %ld\n\n", &test);

   test = (long)DoShadowAsync(testObject, NULL, MethodID, METHOD_END);

   VPrintf("Method Call Second Async Returned %ld\n\n", &test);

   return TRUE;

}

ARGUMENT_TAG REF_SendTest5Method[] = {
                                      {'JOBJ', sizeof(char *),SHADOW_OBJECT},
                                      {TAG_DONE, SHADOW_RETURN_BLANK, 0}
                                   };

long SendTest5Method(METHOD_ARGS, OBJECT testObject)
{
   long i;
   struct DateStamp ds1, ds2;
/*
   Use this for testing the DSM() call, which is a bit faster than
    DoShadow() [maybe 20% faster >50k/sec!].  Funny thing is, it makes
    the function call faster too....
   void *args[4];

   args[0] = testObject;
   args[1] = testObject->cob_class;
   args[2] = METH_TEST_SPEED;
   args[3] = METHOD_END;
 */


   VPrintf("Beginning SHADOW method speed test:\n", NULL);

   Delay(30);  /* let things settle a bit */

   DateStamp((void *)&ds1);
   for (i = 0; i< SPEEDTESTNUMBER; i++)
   {
      /*
       * The following would be a little bit faster than the DoShadow
       *  call.
       * DSM(INVOKE_FIND_SELECTOR, args);
       */

      DoShadow(testObject, NULL, METH_TEST_SPEED, METHOD_END);
   }

   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW method test completed.\n", NULL);
   i = 50 * SPEEDTESTNUMBER;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld methods/second\n\n", &i);

   /*
    * A3000 tests under moderate system usage (many apps Open, nothing
    *  running) shows 40000+.
    */



   VPrintf("Beginning SHADOW method synchronous message speed test:\n", NULL);

   {
      OBJECT procTask = FindNamedObject(PROCESS_TASK, PROCESS_CLASS,
                                        META_CLASS);
      Delay(30);  /* let things settle a bit */

      DateStamp((void *)&ds1);
      for (i = 0; i< SPEEDTESTNUMBER/10; i++)
      {
         DoShadowInProcess(procTask, testObject, NULL, METH_TEST_SPEED,
                           METHOD_END);
      }

      DateStamp((void *)&ds2);

      DropObject(procTask);
   }

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW method test completed.\n", NULL);
   i = 5 * SPEEDTESTNUMBER;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld synchronous-message methods/second\n\n", &i);

   /*
    * A3000 tests under moderate system usage (many apps Open, nothing
    *  running) shows 2500/sec
    */

   VPrintf("Beginning SHADOW asynchronous-message method speed test:\n", NULL);

   {
      OBJECT procTask = FindNamedObject(PROCESS_TASK, PROCESS_CLASS,
                                        META_CLASS);
      Delay(30);  /* let things settle a bit */

      DateStamp((void *)&ds1);
      for (i = 0; i< SPEEDTESTNUMBER/10; i++)
      {
         DoShadowInProcessAsync(procTask, testObject, NULL, METH_TEST_SPEED,
                                METHOD_END);
      }

      DateStamp((void *)&ds2);

      DropObject(procTask);
   }

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("SHADOW method test completed.\n", NULL);
   i = 5 * SPEEDTESTNUMBER;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld asynchronous-message methods/second\n\n", &i);

   /*
    * A3000 tests under moderate system usage (many apps Open, nothing
    *  running) shows 2600/sec
    */



   VPrintf("Beginning function call speed test:\n", NULL);
   DateStamp((void *)&ds1);
   for (i = 0; i< SPEEDTESTNUMBER; i++)
      Test5Method(NULL, testObject, testObject->cob_class, METH_TEST_SPEED);

   DateStamp((void *)&ds2);

   ds2.ds_Tick -= ds1.ds_Tick;
   ds2.ds_Minute -= ds1.ds_Minute;
   if (ds2.ds_Tick < 0)
   {
      --ds2.ds_Minute;
      ds2.ds_Tick += (60 * 50);
   }
   ds2.ds_Days = ds2.ds_Minute;
   ds2.ds_Minute = ds2.ds_Tick / 50;
   ds2.ds_Tick -= (ds2.ds_Minute * 50);

   VPrintf("Function call test completed.\n", NULL);
   i = 50 * SPEEDTESTNUMBER;
   i = i / ((ds2.ds_Days * 60 + ds2.ds_Minute) * 50 + ds2.ds_Tick);
   VPrintf("\t%ld functio calls/second\n\n", &i);

   /*
    * A3000 tests under moderate system usage (many apps Open, nothing
    *  running) shows 230000 for funcs/sec.
    */

   return TRUE;
}

void ProcTestMethod(METHOD_ARGS)
{
   BPTR oldoutput;

   oldoutput = SelectOutput(Open("CONSOLE:", MODE_OLDFILE));

   VPrintf("Called method: <%s> ", (ULONG *)&MethodID);
   VPrintf("in process <%s>\n", (ULONG *)&FindTask(NULL)->tc_Node.ln_Name);
   VPrintf("\tin Class <%s> ", (ULONG *)&(CurrentProcess()->cob_class->meta_name));
   VPrintf(((msg)?"Asynchronously\n":"Synchronously\n"), NULL);

   oldoutput = SelectOutput(oldoutput);
   Close(oldoutput);

   CallSuper();   /* This test is for a Remove method as well! */
}

long bang;
long cross, back;

void TestSemMethod(METHOD_ARGS)
{
   VPrintf("\nStarting semaphore read tests\n", NULL);
   PSem(&bang, SSEM_READ);

   SetCondition(&cross, 1);
   WaitCondition(&back, 1);

   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("Read-Read nesting correct\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("ERROR:Could not own a nested, single-process read semaphore\n",
              NULL);
   }

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Write lock while I owned a Read lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Read-Write correctly does not nest\n", NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read-Write lock while I owned a"
              " Read lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Read-RW correctly does not nest\n", NULL);
   }

   VSem(&bang);
   VPrintf("\nStarting semaphore Write tests\n", NULL);
   PSem(&bang, SSEM_WRITE);

   SetCondition(&cross, 1);
   WaitCondition(&back, 1);

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:owned single-process, nested, Write-Write\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Correctly rejects Write-Write nesting\n",
              NULL);
   }

   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read lock while I owned a Write lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Write-Read correctly does not nest\n", NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read-Write lock while I owned a"
              " Write lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Write-RW correctly does not nest\n", NULL);
   }

   VSem(&bang);

   VPrintf("\nStarting semaphore ReadWrite tests\n", NULL);
   PSem(&bang, SSEM_LOCK);

   SetCondition(&cross, 1);
   WaitCondition(&back, 1);

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("\nRW-Write nesting correct\n", NULL);

      SetCondition(&cross, 1);
      WaitCondition(&back, 1);

      if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
      {
         VPrintf("ERROR:Got a nested Write on a RW owned semaphore\n", NULL);
         VSem(&bang);
      } else
      {
         VPrintf("Correctly could not get nested Write on RW owned\n", NULL);
      }

      if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
      {
         VPrintf("ERROR:Got a Read on a Write on a RW owned semaphore\n", NULL);
         VSem(&bang);
      } else
      {
         VPrintf("Correctly could not get Read on Write on RW owned\n", NULL);
      }

      if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
      {
         VPrintf("ERROR:Got a RW on Write on a RW owned semaphore\n", NULL);
         VSem(&bang);
      } else
      {
         VPrintf("Correctly could not get RW on Write on RW owned\n", NULL);
      }

      VSem(&bang);
   } else
   {
      VPrintf("ERROR:Could not own a single-process write semaphore\n"
              "\twhen already owned RW\n",
              NULL);
   }

   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("\nRW-Read nesting correct\n", NULL);

      SetCondition(&cross, 1);
      WaitCondition(&back, 1);

      if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
      {
         VPrintf("ERROR:Got a Write on a read on a RW owned semaphore\n", NULL);
         VSem(&bang);
      } else
      {
         VPrintf("Correctly could not get Write on Read on RW owned\n", NULL);
      }

      if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
      {
         VPrintf("Correctly got a nested Read on a RW owned semaphore\n", NULL);
         VSem(&bang);
      } else
      {
         VPrintf("ERROR: could not get a nested Read on RW owned\n", NULL);
      }

      if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
      {
         VPrintf("ERROR:Got a RW on Read on a RW owned semaphore\n", NULL);
         VSem(&bang);
      } else
      {
         VPrintf("Correctly could not get RW on Read on RW owned\n", NULL);
      }

      VSem(&bang);
   } else
   {
      VPrintf("ERROR:Could not own a single-process read semaphore\n"
              "\twhen already owned RW\n",
              NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("RW-RW nesting correct\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("ERROR:RW nesting on single-process does not nest!\n", NULL);
   }

   VSem(&bang);
}

void __saveds TestSem2Method(METHOD_ARGS)
{
   WaitCondition(&cross, 1);

   /*
    * Read locked by other process.
    */
   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("Read-Read correct across processes\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("ERROR:Could not own a multi-process read semaphore\n",
              NULL);
   }

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Write lock while "
              "someone else owned a Read lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Read-Write correctly is not shared across processes\n", NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read-Write lock while "
              "someone else owned a Read lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Read-RW correctly is not shared across processes\n", NULL);
   }


   SetCondition(&back, 1);

   WaitCondition(&cross, 1);

   /*
    * Write locked by other process.
    */

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Write-Write owned across processes\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Correctly rejects multiproc-WRITE ownership.\n",
              NULL);
   }

   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read lock while "
              "someone else owned a Write lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Write-Read correctly is not shared across processes\n", NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a RW lock while "
              "someone else owned a Write lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Write-RW correctly is not shared across processes\n", NULL);
   }


   SetCondition(&back, 1);

   WaitCondition(&cross, 1);

   /*
    * Read-Write locked by other process.
    */

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:RW-Write owned across processes\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Correctly rejects Write on RW ownership by someone else.\n",
              NULL);
   }

   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read lock while "
              "someone else owned a RW lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("RW-Read correctly is not shared across processes\n", NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a RW lock while "
              "someone else owned a RW lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Shared RW is correctly rejected.\n", NULL);
   }

   SetCondition(&back, 1);

   WaitCondition(&cross, 1);

   /*
    * W/Read-Write locked by other process.
    */

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:RWW-Write owned across processes\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Correctly rejects Write on RWW ownership by someone else.\n",
              NULL);
   }

   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read lock while "
              "someone else owned a RWW lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("RWW-Read correctly is not shared across processes\n", NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a RW lock while "
              "someone else owned a RWW lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Shared RW and RW-Write is correctly rejected.\n", NULL);
   }

   SetCondition(&back, 1);

   WaitCondition(&cross, 1);

   /*
    * WR/Read-Write locked by other process.
    */

   if (PSem(&bang, SSEM_WRITE|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:RRW-Write owned across processes\n", NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Correctly rejects Write on RRW ownership by someone else.\n",
              NULL);
   }

   if (PSem(&bang, SSEM_READ|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a Read lock while "
              "someone else owned a RRW lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("RRW-Read correctly is not shared across processes\n", NULL);
   }

   if (PSem(&bang, SSEM_LOCK|SSEM_ATTEMPT))
   {
      VPrintf("ERROR:Was able to own a RW lock while "
              "someone else owned a RRW lock!\n",
              NULL);
      VSem(&bang);
   } else
   {
      VPrintf("Shared RW and RW-Read is correctly rejected.\n", NULL);
   }

   SetCondition(&back, 1);
}
