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