[comp.sys.amiga] SetFunction

kkaempf@rmi.UUCP (Klaus Kaempf) (02/13/88)

The problem:

1. program A starting:
   oldFunctionA = SetFunction(LibBase, Offset, newFunctionA);

2. program B starting:
   oldFunctionB = SetFunction(LibBase, Offset, newFunctionB);
   /* oldFunctionB unfortunately happens to be newFunctionA */

3. program A exiting:
   (void)SetFunction(LibBase, Offset, oldFunctionA);

4. program B exiting:
   (void)SetFunction(LibBase, Offset, oldFunctionB);

5. boom!!!

The idea:

Developers should agree on a common method being used for
patching library jump tables so programs can exit in any
order.

Simple solution:

Check for the result returned in step 3, and notify the user
that s/he loaded another program that changed this specific
library offset. And do not close the library unless you've
undone your "SetFunction()".

Forbid();
if(flag = (temp = SetFunction(LibBase, Offset, oldFunctionA)) != newFunctionA)
 SetFunction(LibBase, Offset, temp);
Permit();
if(flag)
 printf("cannot exit yet!\n");

My suggestion:

struct FunctionNode *NewSetFunction(libbase, offset, function)
struct Library *libbase;
WORD offset;
APTR function;

This is a replacement for the old one (clever name, eh?). It
returns some kind of handle that must be used for calling
the predecessor (or any preceding predecessor if the one
active on startup has already been removed) and for
restoring the jump table entry.

VOID UndoSetFunction(fn)
struct FunctionNode *fn;

And this is it. It will do all the magic stuff.

Now there's a problem: What should I do, if someone wants to
remove his library patch, but it's still in use? I decided
to use semaphores. So if you want to call the function that
has been in use before patching the library, you should get
its address by calling ...

APTR LockPred(fn)
struct FunctionNode *fn;

If you've installed a new "Text()" function that will only
handle some special fonts ...

NewText:
    if(cannot_handle_this_one)
     {
     ((VOID (*)())LockPred(fn))(rp, text, length);
     UnlockPred(fn);
     }
    else
     ...

VOID UnlockPred(fn)
struct FunctionNode *fn;

You guessed it ...

"UndoSetFunction()" will not return until all "LockPred()"
have been unlocked.

So this should be the solution? Unfortunately not! I simply
do not have any idea on how to lock the first function
called, i. e. the one currently in the jump table. It is
still possible to remove this one via "UndoSetFunction()",
even if it is currently in use! So what should I do? Matt?
Bryce? Andy? HELP!!!

I could add a "LockFunction()" to be called on entry by the
new function itself, but this would lock the function too
late. And I considered some kind of supervisor for each
patched function entry, but this would probably be overkill
(and slow down performance considerably).

So any ideas? Maybe even simpler & better?

Thanks

!ralph

%%%%%%%%%%%%%%%%
/*
** SetFunction.h - suggested protocol
** 08-Feb-1988 by Guru Meditation Network
** Ralph Babel, Falkenweg 3, D-6204 Taunusstein, FRG
**
** UNCONDITIONALLY PLACED IN THE PUBLIC DOMAIN
*/

#ifndef SETFUNCTION_H
#define SETFUNCTION_H

#ifndef __ARGS
#ifdef AZTEC_C /* not tested */
#define __ARGS(a) ()
#else
#define __ARGS(a) a
#endif
#endif

#define MAXPORTNAME 28 /* even */

struct FunctionPort
 {
 struct MsgPort Port;
 struct Library *LibBase;
 WORD Offset;
 char Name[MAXPORTNAME];
 };

struct FunctionNode
 {
 struct MinNode Node;
 struct SignalSemaphore Semaphore;
 struct FunctionPort *Port;
 WORD NestCount;
 APTR Function;
 };

struct FunctionNode *NewSetFunction __ARGS((struct Library *, WORD, APTR));
VOID UndoSetFunction __ARGS((struct FunctionNode *));
APTR LockPred __ARGS((struct FunctionNode *));
VOID UnlockPred __ARGS((struct FunctionNode *));

#endif
%%%%%%%%%%%%%%%%
/*
** SetFunction.c - suggested protocol
** 08-Feb-1988 by Guru Meditation Network
** Ralph Babel, Falkenweg 3, D-6204 Taunusstein, FRG
**
** UNCONDITIONALLY PLACED IN THE PUBLIC DOMAIN
*/

#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/ports.h>
#include <exec/semaphores.h>
#include "setfunction.h"

APTR AllocMem __ARGS((ULONG, ULONG));
VOID FreeMem __ARGS((APTR, ULONG));
VOID Forbid __ARGS((VOID));
VOID Permit __ARGS((VOID));
VOID InitSemaphore __ARGS((struct SignalSemaphore *));
VOID ObtainSemaphore __ARGS((struct SignalSemaphore *));
VOID ReleaseSemaphore __ARGS((struct SignalSemaphore *));
struct MsgPort *FindPort __ARGS((char *));
VOID AddPort __ARGS((struct MsgPort *));
VOID RemPort __ARGS((struct MsgPort *));
VOID Remove __ARGS((struct Node *));
VOID AddTail __ARGS((struct List *, struct Node *));
APTR SetFunction __ARGS((struct Library *, LONG /*WORD*/, APTR));
VOID sprintf __ARGS((char *, char *, ));
char *strcpy __ARGS((char *, char *));
VOID NewList __ARGS((struct List *));

struct FunctionNode *NewSetFunction(libbase, offset, function)
struct Library *libbase;
WORD offset;
APTR function;
 {
 struct FunctionPort *fp;
 struct FunctionNode *fn;
 char buffer[MAXPORTNAME];

 if((fn = (struct FunctionNode *)
  AllocMem(sizeof(struct FunctionNode), MEMF_PUBLIC)) != NULL)
  {
  sprintf(buffer, "SetFunction(%lx,%lx)", (ULONG)libbase, (ULONG)-offset);
  Forbid();
  if((fp = (struct FunctionPort *)FindPort(buffer)) == NULL)
   if((fp = (struct FunctionPort *)
    AllocMem(sizeof(struct FunctionPort), MEMF_PUBLIC)) != NULL)
    {
    strcpy(fp->Name, buffer);
    fp->Port.mp_Node.ln_Name = fp->Name;
    fp->LibBase = libbase;
    fp->Offset = offset;
    NewList(&fp->Port.mp_MsgList);
    AddPort((struct MsgPort *)fp);
    }
   else
    {
    FreeMem((APTR)fn, sizeof(struct FunctionNode));
    fn = NULL;
    goto quit; /* goto??? aarrrgghhh!!! */
    }

  InitSemaphore(&fn->Semaphore);
  fn->NestCount = -1; /* I _could_ have used the one in the SignalSemaphore */
  fn->Port = fp;
  fn->Function = SetFunction(libbase, offset, function);
  AddTail(&fp->Port.mp_MsgList, (struct Node *)fn);

  quit:
  Permit();
  }

 return fn;
 }

VOID UndoSetFunction(fn)
struct FunctionNode *fn;
 {
 Forbid();
 ObtainSemaphore(&fn->Semaphore); /* wait until not in use */

 if(fn->Node.mln_Succ->mln_Succ != NULL)
  ((struct FunctionNode *)fn->Node.mln_Succ)->Function = fn->Function;
 else
  (VOID)SetFunction(fn->Port->LibBase, fn->Port->Offset, fn->Function);

 Remove((struct Node *)fn);

 if(fn->Port->Port.mp_MsgList.lh_Head->ln_Succ == NULL)
  RemPort((struct MsgPort *)fn->Port);

 FreeMem((APTR)fn, sizeof(struct FunctionNode));
 Permit();
 }

APTR LockPred(fn)
struct FunctionNode *fn;
 {
 struct FunctionNode *pred;

 Forbid();
 if((pred = (struct FunctionNode *)fn->Node.mln_Pred)->Node.mln_Pred != NULL)
  if(++pred->NestCount == 0)
   ObtainSemaphore(&pred->Semaphore);
 Permit();

 return fn->Function;
 }

VOID UnlockPred(fn)
struct FunctionNode *fn;
 {
 struct FunctionNode *pred;

 Forbid();
 if((pred = (struct FunctionNode *)fn->Node.mln_Pred)->Node.mln_Pred != NULL)
  if(pred->NestCount-- == 0)
   ReleaseSemaphore(&pred->Semaphore);
 Permit();
 }
%%%%%%%%%%%%%%%%

kkaempf@rmi.UUCP (Klaus Kaempf) (02/13/88)

Maybe we should reserve task priority -128 to solve this
problem: If a task of prio -128 gains control, the system is
probably not very busy, i. e. all other tasks must be
waiting.

So we should include something like

    pri = SetTaskPri(FindTask(NULL), -128);

after "Forbid()", and, of course, add

    (void)SetTaskPri(FindTask(NULL), pri);

before "Permit()" in UndoSetFunction().

What should we do, if a task is waiting inside our
library-patch-code? Surround all Wait()'s (or WaitBlit(),
DoIO(), whatever) by some kind of "LockFunction()" and
"UnlockFunction()".

The "ObtainSemaphore()" would only wake up, if no task is
waiting inside our patch code. Maybe it's still inside our
code, but we won't wake up until everything is quiet (prio =
-128, remember).

Now, there's another catch: What if we finally obtained the
semaphore and gained control of the system, because someone
else is waiting for this semaphore to be released? Darn,
again someone waiting inside our code. I'd suggest ...

    Forbid();

    for(;;)
     {
     ObtainSemaphore(&fn->Semaphore);
     if(fn->Semaphore.ss_QueueCount == -1)
      break;
     ReleaseSemaphore(&fn->Semaphore);
     }

    /* ... */

Oh yeah: "LockFunction()" and "UnlockFunction()" can be
derived from "LockPred()" and "UnlockPred() and the latter
should then call the former.

!ralph

Ralph Babel, Falkenweg 3, D-6204 Taunusstein, FRGermany

FATQW@USU.BITNET (02/21/88)

>Maybe we should reserve task priority -128 to solve this
>problem: If a talk of prio -128 gains control, the system is
>probably not very busy, i. e. all other tasks must be waiting.

Yes, HOWEVER, this is no excuse for busy waiting, if that's what you're
thinking of.  (Ignore this message if not.)  Remember that HALTING the
processor completely will speed up certain things like the disk drives.
Even using a priority of -128 will still slow down the disk drives etc.

Advice: Don't busy-wait - PERIOD!!!

                                Bryan

        Bryan Ford                    //// A computer does what \\\\
Snail:  1790 East 1400 North         //// you tell it to do, not \\\\
        Logan, UT 84321         \\\XX///  what you want it to do. \\\XX///
Email:  FATQW@USU.BITNET         \XXXX/ Murphy's Law Calendar 1986 \XXXX/

marco@alessia.dei.unipd.it (Capitan Harlock) (10/17/90)

How can I use SetFunction() with Lattice C 5.05 ?
I should want change OpenWindow() ! But I don't know how made it.

Thank for your help ...
							...marco