Re-entrant and multi-entrant ABCL calls

Blake McBride blake at mcbride.name
Fri Apr 20 11:03:19 UTC 2018


Thanks, Mark.  Here is a portion of what I am doing (written many years
ago):

public class ABCL {
   private static Interpreter interpreter;
   private static boolean invertCase = false;
   private static Function makeWebServiceArgs;
   private static int lispRelease = 0;
   private static boolean once = true;

   public static void init() {  // only called once
      interpreter = Interpreter.createInstance();
      invertCase();

      load("com/xxx/lisp/clos-utils");
      load("com/xxx/lisp/package-lru");
      load("com/xxx/lisp/utils");
      load("com/xxx/lisp/mappings");
      makeWebServiceArgs = findLispFunction("UTILS",
"make-web-service-args"); // this line is repeated in multiple places
   }

   private static void invertCase() {
      interpreter.eval("(setf (readtable-case *readtable*) :invert)");
 // make lisp case sensitive
      invertCase = true;
   }

   public static String fixCase(String symbol) {
      if (invertCase) {
         int ucl = 0, lcl = 0;
         char[] vec = symbol.toCharArray();
         for (int i=0 ; i < vec.length && (ucl == 0  ||  lcl == 0) ; i++)
            if (Character.isUpperCase(vec[i]))
               ucl++;
            else if (Character.isLowerCase(vec[i]))
               lcl++;
         if (ucl != 0  &&  lcl != 0  ||  ucl == 0  &&  lcl == 0)
            return symbol;
         else
            if (ucl != 0)
               return symbol.toLowerCase();
            else
               return symbol.toUpperCase();
      } else
         return symbol.toUpperCase();
   }

   public static void reset() {
//    if (interpreter == null)
//       return;
//    try {
//       interpreter.eval("(delete-package \"ARAHANT-UTILS\")");
//    } catch (Throwable t) {
//    }

      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"MAPPINGS\")");
      } catch (Throwable t) {
      }
      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"UTILS\")");
      } catch (Throwable t) {
      }
      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"PACKAGE-LRU\")");
      } catch (Throwable t) {
      }
      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"CLOS-UTILS\")");
      } catch (Throwable t) {
      }
      load("com/xxx/lisp/clos-utils");
      load("com/xxx/lisp/package-lru");
      load("com/xxx/lisp/utils");
      load("com/xxx/lisp/mappings");
      makeWebServiceArgs = findLispFunction("UTILS",
"make-web-service-args"); // this line is repeated in multiple places
   }

   public static LispObject load(String fileName) {
      return eval("(load \"" + FileSystemUtils.getSourcePath() +
fileName + "\")");
   }

   public static LispObject compileFile(String fileName) {
      return eval("(compile-file \"" + FileSystemUtils.getSourcePath()
+ fileName + "\")");
   }

   public static void loadPackage(String lispPackage, String fileName)
throws Exception {
      try {
         eval("(package-lru:load-package \"" + lispPackage + "\" \"" +
FileSystemUtils.getSourcePath() + fileName + "\")");
      } catch (Throwable t) {
         // Convert Throwable to Exception
         throw new Exception("Error loading lisp file " + fileName, t);
      }
   }

   public static void packageDone(String lispPackage) {
      if (FileSystemUtils.isUnderIDE())
         eval("(package-lru:package-done-unload \"" + lispPackage + "\")");
      else
         eval("(package-lru:package-done \"" + lispPackage + "\")");
   }

    public static LispObject eval(String str) {
        return interpreter.eval(str);
    }

    public static Function findLispFunction(String packageName, String
funName) {
        if (packageName == null  ||  packageName.isEmpty())
            packageName = "CL-USER";
//        else
//            packageName = fixCase(packageName);
        org.armedbear.lisp.Package lispPackage =
Packages.findPackage(packageName);
      if (lispPackage == null)
         throw new RuntimeException("Package " + packageName + " not found");
        Symbol symbol = lispPackage.findAccessibleSymbol(fixCase(funName));
      if (symbol == null)
         throw new RuntimeException("Symbol " + packageName + ":" +
fixCase(funName) + " not found");
        Function fun = (Function) symbol.getSymbolFunction();
        return fun;
    }

    public static LispObject executeLispFunction(Function fun, Object
... args) {
        LispObject [] jargs;
        jargs = new LispObject[args.length];
        for (int i=0 ; i < args.length ; i++)
            jargs[i] = JavaObject.getInstance(args[i], true);
        return fun.execute(jargs);
    }

    public static LispObject executeLisp(String packageName, String
funName, Object ... args) {
        Function fun = findLispFunction(packageName, funName);
      if (fun == null)
         return null;
        LispObject [] jargs;
        jargs = new LispObject[args.length];
        for (int i=0 ; i < args.length ; i++)
            jargs[i] = JavaObject.getInstance(args[i], true);
        return fun.execute(jargs);
    }

    public static LispObject executeLispArray(String packageName,
String funName, Object [] args) {
        Function fun = findLispFunction(packageName, funName);
      if (fun == null)
         return null;
        LispObject [] jargs;
        jargs = new LispObject[args.length];
        for (int i=0 ; i < args.length ; i++)
            jargs[i] = JavaObject.getInstance(args[i], true);
        return fun.execute(jargs);
    }

   public static Function getMakeWebServiceArgs() {
      return makeWebServiceArgs;
   }

@SuppressWarnings("unchecked")
   public static Object LispObjectToJavaObject(LispObject obj) {
      if (obj.atom())
         if (obj.characterp())
            return obj.princToString().charAt(0);
         else if (obj.stringp())
            return obj.princToString();
         else if (obj.integerp())
            return obj.intValue();
         else if (obj.realp())
            return obj.doubleValue();
         else if (obj.listp())
            return null;
         else if (obj.constantp())
            return true;
         else
            return obj.princToString();
      else if (obj.listp()) {
         LinkedList ll = new LinkedList();
         while (!obj.endp()) {
            ll.addLast(LispObjectToJavaObject(obj.car()));
            obj = obj.cdr();
         }
         return ll;
      } else if (obj.vectorp()) {
         int len = obj.length();
         Object [] vec = new Object[len];
         for (int i=0 ; i < len ; i++)
            vec[i] = LispObjectToJavaObject(obj.AREF(i));
         return vec;
      } else
         return null;
   }

   public static LispObject JavaObjectToLispObject(Object jobj) {
      if (jobj instanceof Boolean)
         return ((Boolean)jobj) ? Lisp.T : Lisp.NIL;
      else if (jobj instanceof Character)
         return LispCharacter.getInstance((Character)jobj);
      else if (jobj instanceof Short)
         return LispInteger.getInstance((Short)jobj);
      else if (jobj instanceof Integer)
         return LispInteger.getInstance((Integer)jobj);
      else if (jobj instanceof Long)
         return LispInteger.getInstance((Long)jobj);
      else if (jobj instanceof Float)
         return SingleFloat.getInstance((Float)jobj);
      else if (jobj instanceof Double)
         return DoubleFloat.getInstance((Double)jobj);
      else if (jobj instanceof String)
         return new SimpleString((String)jobj);
      else if (jobj instanceof StringBuilder)
         return new SimpleString((StringBuilder)jobj);
      else if (jobj instanceof LinkedList) {
         LispObject lobj = Lisp.NIL;
         ListIterator it = ((LinkedList) jobj).listIterator();
         while (it.hasNext())
            lobj = new Cons(JavaObjectToLispObject(it.next()), lobj);
         return lobj;
      } else if (jobj instanceof Set) {
         LispObject lobj = Lisp.NIL;
         Iterator it = ((Set) jobj).iterator();
         while (it.hasNext())
            lobj = new Cons(JavaObjectToLispObject(it.next()), lobj);
         return lobj;
      } else if (jobj instanceof Array) {
         Array a = (Array) jobj;
         int len = Array.getLength(a);
         SimpleVector vec = new SimpleVector(len);
         for (int i=0 ; i < len ; i++)
            vec.setSlotValue(i, JavaObjectToLispObject(Array.get(a, i)));
         return null;
      }
      return null;
   }

   public static void printStackTrace(Throwable e) {
      try {
         Function fun = findLispFunction("UTILS", "print-stack-trace");
         if (fun != null) {
            LispObject stackTrace = LispThread.currentThread().backtrace(0);
            if (stackTrace != null && stackTrace != Lisp.NIL) {
               System.err.println("Lisp execution error");
               fun.execute(stackTrace);
            }
         }
      } catch (Throwable t) {
      }
      e.printStackTrace();
   }

   public static int getLispRelease() {
      return lispRelease;
   }

   public static void setLispRelease(int lispRelease) {
      ABCL.lispRelease = lispRelease;
   }

}





On Fri, Apr 20, 2018 at 5:36 AM, Mark Evenson <evenson at panix.com> wrote:

>
>
> > On Apr 20, 2018, at 11:10, Blake McBride <blake at mcbride.name> wrote:
> >
> > Greetings,
> >
> > Does ABCL safely support re-entrant and multi-entrant calls?  What I
> mean by that is the following:
> >
> > Re-entrant:  on a single OS thread - my Java program calls into ABCL,
> then ABCL calls into my Java application, and then the Java application
> calls back into ABCL.  So the stack has Java, ABCL, JAVA, and then ABCL
> again.
> >
> > Multi-entrant:  my Java application has many threads.  One of my threads
> calls into ABCL.  Then, while one thread is still in ABCL, another thread
> evokes ABCL.  So now we have two calls into ABCL by two independent Java/OS
> threads running at the same time.
> >
> > I understand the typical problems associated with application-level
> shared variables.  This is expected.  The question revolves around ABCL's
> internals.  I presume ABCL would have some shared data that is internal to
> ABCL.  That's what I am unclear about.  ABCL would have had to be designed
> for these scenarios from the ground up.
> >
> > This is a little hard to test because if it can't always correctly
> handle these situations, it may not become clear until certain scenarios
> arrive.  It may be hard for any "test" program I write to cause those
> scenarios, so I thought this may be a known answer.
>
>
> As long as one is referencing the org.armedbear.lisp.Interpreter singleton
> for
> the calls into ABCL, everything should work fine.  Most of the logic can be
> understood by studying what [LispThread.java][1] does to mark global/local
> special variables, and how each Java thread is associated with a LispThread
> call stack.
>
> [1]: https://gitlab.common-lisp.net/abcl/abcl/blob/master/src/
> org/armedbear/lisp/LispThread.java#L55
>
> How exactly are you calling into ABCL from Java?  A snippet of code would
> be
> useful.
>
>
>
>
> --
> "A screaming comes across the sky.  It has happened before but there is
> nothing
> to compare to it now."
>
>
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/armedbear-devel/attachments/20180420/08d4ed83/attachment-0001.html>


More information about the armedbear-devel mailing list