> I'd be interested in helping on the webOS port which would share a lot of
> code.
Since WebOS is based on Linux, it should already work (in theory),
assuming you have some way of building, installing and running native
executables on the device. Have you tried it?
Good news regarding the iOS port: I've got all but one of the tests
passing on an jailbroken iPhone 3G I'm borrowing from a colleague. The
one test that isn't passing is the OutOfMemory test, which causes the
phone to lock up and eventually reboot itself every time I run it, even
when I reduce the VM heap size from the default 128MB to 8MB. That makes
it hard to debug, but I'll try narrow down the problem when I have time.
The code is in the "ios" branch of the Git repository. Bootimage (i.e.
ahead-of-time-compiled) builds are not yet supported because Avian can't
currently build a bootimage targeting a different architecture than the
build system, and I haven't tried installing GCC on the iPhone to do
native builds. The lack of cross-architecture build support for
bootimages is something I've wanted to address for a while, and this may
be a good time to finally do it.
localhost:avian joshmarinacci$ rm -rf build; make platform=linux arch=arm cc=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-gcc
compiling build/linux-arm/type-generator-build.o
compiling build/linux-arm/posix-build.o
compiling build/linux-arm/finder-build.o
linking build/linux-arm/generator
Undefined symbols:
"_CFStringGetMaximumSizeOfFileSystemRepresentation", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFBundleGetMainBundle", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_kCFAllocatorDefault", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFURLCopyPath", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"___CFConstantStringClassReference", referenced from:
cfstring= in posix-build.o
"_CFBundleCopyExecutableURL", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFStringGetFileSystemRepresentation", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFURLCreateStringByReplacingPercentEscapes", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make: *** [build/linux-arm/generator] Error 1
localhost:avian joshmarinacci$
thanks,
Josh
> --
> You received this message because you are subscribed to the Google Groups "Avian" group.
> To post to this group, send email to av...@googlegroups.com.
> To unsubscribe from this group, send email to avian+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/avian?hl=en.
>
Blasting forth in three part harmony!
I haven't tried cross compiling for Linux/ARM on OS X myself, but it
should be doable. If you look in the makefile, you'll find a section that
looks like this:
ifeq ($(arch),arm)
asm = arm
pointer-size = 4
cflags += -marm -Wno-psabi
ifneq ($(arch),$(build-arch))
cxx = arm-linux-gnueabi-g++
cc = arm-linux-gnueabi-gcc
ar = arm-linux-gnueabi-ar
ranlib = arm-linux-gnueabi-ranlib
strip = arm-linux-gnueabi-strip
endif
endif
If you change the arm-linux-gnueabi-* commands to e.g.
/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-*, that should do the
trick. You can also override cxx, cc, ar, ranlib, and strip on the
command line if you want to avoid editing the makefile. You might also
want to set MAKEFLAGS= (i.e. set it to nothing) in order to see what
commands are actually being run:
make platform=linux arch=arm MAKEFLAGS= \
cxx=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-g++ \
cc=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-gcc \
ar=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-ar \
ranlib=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-ranlib \
strip=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-strip
Please let me know if you still have trouble.
I'm still getting undefined symbols. Forgive me for my newbie questions. I'm a Java expert, not a C guy. If it can't find symbols then there must be a missing path for the libraries, right? Where to -lz -lpthread, etc. map to?
- J
linking build/linux-arm/generator
gcc build/linux-arm/type-generator-build.o build/linux-arm/posix-build.o build/linux-arm/finder-build.o -lz -lpthread -ldl -o build/linux-arm/generator
Undefined symbols:
"_CFStringGetMaximumSizeOfFileSystemRepresentation", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFBundleGetMainBundle", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_kCFAllocatorDefault", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFURLCopyPath", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"___CFConstantStringClassReference", referenced from:
cfstring= in posix-build.o
"_CFBundleCopyExecutableURL", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFStringGetFileSystemRepresentation", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
"_CFURLCreateStringByReplacingPercentEscapes", referenced from:
(anonymous namespace)::pathOfExecutable(vm::System*, char const**, unsigned int*)in posix-build.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make: *** [build/linux-arm/generator] Error 1
> Thanks!
>
> I'm still getting undefined symbols. Forgive me for my newbie questions.
> I'm a Java expert, not a C guy. If it can't find symbols then there must
> be a missing path for the libraries, right? Where to -lz -lpthread,
> etc. map to?
No problem about the questions. Building cross-platform C/C++ code is
messy and difficult even for those with lots of experience.
I should have noticed in your first email that it's breaking when building
the the type generator utility, which is supposed to be a native (OS X, in
this case) executable, so we haven't even reached the cross compilation
stage. It's breaking because the makefile currently assumes the build
platform will only be OS X if the target platform is too, which obviously
doesn't hold in your case.
Anyway, I just tried it myself and got it building with a few changes to
the makefile, which I've committed to the "ios" branch of the Git
repository. Please check out that branch and use this command:
make platform=linux arch=arm MAKEFLAGS= \
cxx=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-g++ \
cc=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-gcc \
ar=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-ar \
ranlib=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-ranlib \
strip=/opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-strip \
extra-cflags=-I/opt/PalmPDK/include/ \
extra-lflags=-L/opt/PalmPDK/device/lib/
> The code is in the "ios" branch of the Git repository. Bootimage (i.e.
> ahead-of-time-compiled) builds are not yet supported because Avian can't
> currently build a bootimage targeting a different architecture than the build
> system, and I haven't tried installing GCC on the iPhone to do native builds.
> The lack of cross-architecture build support for bootimages is something I've
> wanted to address for a while, and this may be a good time to finally do it.
I went ahead and implemented this, so you can now do AOT bootimage builds
for iOS on OS X.
> Thanks for the pointer, i added that linker flag with no success. I
> checked the resulting mach-o executable, the segment is indeed marked
> as read/write/executeable (using this handz tool http://sourceforge.net/projects/machoview/,
> couldn't get the info from nm). I don't see any differences to other
> code segments, e.g. TEXT, so i'm puzzled.
__TEXT should be readable and executable, but not writable, which is why
we can't use it for the bootimage. You can use "otool -lV <executable>"
to see the permissions for each segment.
> Any other ideas? If it's not too much to ask, could you maybe provide
> a makefile for a simple bootimage app (e.g. Hello.java) that runs on
> iOS i could dissect?
I don't have a makefile handy, but here are the steps I took to build such
an app, start to finish:
git clone git://oss.readytalk.com/avian.git
git checkout ios
make bootimage=true arch=arm
mkdir hello
cd hello
ar x ../build/darwin-arm-bootimage/libavian.a
mkdir stage1
cd stage1/
jar xf ../../build/darwin-x86_64-bootimage/classpath.jar
cd ..
cat >Hello.java <<EOF
public class Hello {
public static void main(String[] args) {
System.out.println("hello, world!");
}
}
EOF
javac -bootclasspath stage1 -d stage1 Hello.java
cat >hello.pro <<EOF
-keep class Hello {
public static void main(java.lang.String[]);
}
EOF
java -jar ../../proguard4.6beta1/lib/proguard.jar -injars stage1 \
-outjars stage2 @../vm.pro -dontusemixedcaseclassnames @hello.pro
../build/darwin-arm-bootimage/bootimage-generator stage2 bootimage.bin
../build/darwin-arm-bootimage/binaryToObject bootimage.bin \
bootimage-bin.o _binary_bootimage_bin_start _binary_bootimage_bin_end \
darwin arm 8 writable executable
cat >main.cpp <<EOF
#include "stdint.h"
#include "jni.h"
#if (defined __MINGW32__) || (defined _MSC_VER)
# define EXPORT __declspec(dllexport)
#else
# define EXPORT __attribute__ ((visibility("default")))
#endif
#if (! defined __x86_64__) && ((defined __MINGW32__) || (defined
_MSC_VER))
# define BOOTIMAGE_BIN(x) binary_bootimage_bin_##x
#else
# define BOOTIMAGE_BIN(x) _binary_bootimage_bin_##x
#endif
extern "C" {
extern const uint8_t BOOTIMAGE_BIN(start)[];
extern const uint8_t BOOTIMAGE_BIN(end)[];
EXPORT const uint8_t*
bootimageBin(unsigned* size)
{
*size = BOOTIMAGE_BIN(end) - BOOTIMAGE_BIN(start);
return BOOTIMAGE_BIN(start);
}
} // extern "C"
int
main(int ac, const char** av)
{
JavaVMInitArgs vmArgs;
vmArgs.version = JNI_VERSION_1_2;
vmArgs.nOptions = 1;
vmArgs.ignoreUnrecognized = JNI_TRUE;
JavaVMOption options[vmArgs.nOptions];
vmArgs.options = options;
options[0].optionString
= const_cast<char*>("-Davian.bootimage=bootimageBin");
JavaVM* vm;
void* env;
JNI_CreateJavaVM(&vm, &env, &vmArgs);
JNIEnv* e = static_cast<JNIEnv*>(env);
jclass c = e->FindClass("Hello");
if (not e->ExceptionCheck()) {
jmethodID m = e->GetStaticMethodID(c, "main",
"([Ljava/lang/String;)V");
if (not e->ExceptionCheck()) {
jclass stringClass = e->FindClass("java/lang/String");
if (not e->ExceptionCheck()) {
jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0);
if (not e->ExceptionCheck()) {
for (int i = 1; i < ac; ++i) {
e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i]));
}
e->CallStaticVoidMethod(c, m, a);
}
}
}
}
int exitCode = 0;
if (e->ExceptionCheck()) {
exitCode = -1;
e->ExceptionDescribe();
}
vm->DestroyJavaVM();
return exitCode;
}
EOF
/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/g++ \
-I/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/JavaVM.framework/Headers/ \
-D_JNI_IMPLEMENTATION_ -arch armv6 -isysroot \
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/ \
-c main.cpp -o main.o
/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/g++ \
-arch armv6 -isysroot \
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/ \
-rdynamic *.o -ldl -lpthread -lz -framework CoreFoundation \
-Wl,-segprot,__RWX,rwx,rwx -o hello
The result runs on a jailbroken iPhone. I don't have a non-jailbroken
device handy to test, but I'll give it a try if I can get access to one.
It's possible that a stock iPhone won't accept a Mach-O section that's
both writable and executable. If that's the case, it will be a challenge
to support, although it should be possible. I'd prefer not to head down
that road until that theory is confirmed, though. Does anyone happen to
know of any official documentation on this subject?
> It's possible that a stock iPhone won't accept a Mach-O section that's both
> writable and executable. If that's the case, it will be a challenge to
> support, although it should be possible. I'd prefer not to head down that
> road until that theory is confirmed, though. Does anyone happen to know of
> any official documentation on this subject?
Now that I think about it, this totally makes sense. Since Apple
apparently wants to disallow JIT compilation, they naturally must disallow
writable, executable code segments. Otherwise, I could just make a big,
empty section with those permissions and fill it with JIT-compiled code at
runtime. A bit of googling seems to support this, although I still
haven't seen anything official.
> I couldn't see anything official either, but the setiment on the net
> seems to be that this is in fact not possible. The reasons you gave
> make sense. Is there a way to work-around the need for writeable code
> segments? I assume addresses get patched?
Yes, there are a couple of ways around it that I can think of; I'm working
on what I think will be the easiest. I hope to have something to show for
it by the end of the week.
>
> On 13 Sep., 08:48, Joel Dice <joel.d...@gmail.com> wrote:
>> On Tue, 13 Sep 2011, Joel Dice wrote:
>>> It's possible that a stock iPhone won't accept a Mach-O section that's both
>>> writable and executable. �If that's the case, it will be a challenge to
>>> support, although it should be possible. �I'd prefer not to head down that
>>> road until that theory is confirmed, though. �Does anyone happen to know of
>>> any official documentation on this subject?
>>
>> Now that I think about it, this totally makes sense. �Since Apple
>> apparently wants to disallow JIT compilation, they naturally must disallow
>> writable, executable code segments. �Otherwise, I could just make a big,
>> empty section with those permissions and fill it with JIT-compiled code at
>> runtime. �A bit of googling seems to support this, although I still
>> haven't seen anything official.
>
> On Mon, 19 Sep 2011, mzechner wrote:
>
>> I couldn't see anything official either, but the setiment on the net
>> seems to be that this is in fact not possible. The reasons you gave
>> make sense. Is there a way to work-around the need for writeable code
>> segments? I assume addresses get patched?
>
> Yes, there are a couple of ways around it that I can think of; I'm working on
> what I think will be the easiest. I hope to have something to show for it by
> the end of the week.
I finished this work and created a simple iOS app that displays "Hello,
World!" using Java:
git clone git://oss.readytalk.com/hello-ios.git
There's a readme.txt in that project which explains how to build it. The
build process currently requires a command-line step and a step to finish
the build from within the IDE. Someone with better XCode skills than mine
may be able to streamline this so you can build entirely from within the
IDE or entirely from the command line.
I only recently got access to a non-jailbroken device, which is why I
didn't catch this until now. I kind of wish the iOS Simulator was as
restrictive about writable, executable memory as real, non-jailbroken
hardware is; then I would have noticed this sooner.
On Sat, 7 Jan 2012, Anton Shevchenko wrote:
> I have some difficulties trying to build hello-ios to run on the
> iPhone.
> I have successfully built the hello-ios project for the simulator
> (needed to exclude objective c stuff from the hello.m file as Xcode3
> told about undefined symbols on the linker stage - anyway, this issue
> is not critical).
> So, after getting to run 'Hello world' in the simulator I decided to
> try out to build this sample for the iPhone (not jailbraked).
> When I build avian for the arm architecture I am getting the following
> error
>
> Undefined symbols for architecture armv6:
> "_JNI_CreateJavaVM", referenced from:
> -[helloAppDelegate application:didFinishLaunchingWithOptions:]
> in helloAppDelegate.o
> ld: symbol(s) not found for architecture armv6
>
> But when I am trying to build for the armv6 architecture the tool
> binaryToObject fails with 'unsupported platform'.
Did you follow the directions in readme.txt? Note that you must build
from the command line using the makefile. If you build from within XCode
first without running make, it will fail with the error you encountered.
If you still get that error even after running make from the command line,
please post the complete output of "make clean && make". Thanks.
By the way, if you're using the iOS 5.0 SDK, you'll need to change "4.3"
to "5.0" in the makefile.
>
> I am really wondering I got so far with Avian - thank you a lot, Joel.
Also, it is necessary to run "make clean" each time you switch from a
simulator build to a device (i.e. ARM) build and vice-versa.
On Sun, 26 Feb 2012, Daniel Jaeger wrote:
> Hi Joel,
>
> great work on Avian.
> I'm considering Avian as scripting-backend for a game. I've already
> played around with it and it seems to work great.
> As I understand it's not possible to allocate memory that's both
> writeable and executable on iOS devices.
> So the current solution is to compile the boot image into an object
> file and mark this file executable.
> For this process to work we have to use the "bootimage-generator" so
> the java byte code can be directly executed without JITing it.
The bootimage-generator does two things: (1) it preparses all the class
files in the application and creates a heap image which contains those
classes in a VM-specific format, minus any bytecode, and (2) it
ahead-of-time compiles all the bytecode to machine code to create a
read-only code image.
> So here are my questions:
>
> - why is a code image even necessary in those cases? Wouldn't the boot
> image be enough?
What I've been calling the boot image is composed of two things: the heap
image and the code image. If you don't want or need AOT compilation and
are willing to accept the performance penalty of interpreted bytecode,
you're better off not doing a boot image build at all and just embedding a
jar file instead.
> - I've tried to build the hello-world sample with (make run-
> proguard=false) but it failed. Why is pro guard required?
Sorry, that was a bug in the makefile. Please pull the latest from the
Git repo and try again.
> - would it be possible to avoid the JIT compilation and revert to a
> slow interpreter, similar to Lua, that's safe on the iOS devices?
Yes, if you need to be able to load bytecode at runtime and you don't care
much about performance, that will work. I'll update the hello-ios example
makefile to provide this option when I have some time.
>>>> �On Oct 4 2011, 1:23�am, Joel Dice <joel.d...@gmail.com> wrote:
>>>>> �FYI, I've merged the ios branch into master and plan to delete the ios
>>>>> �branch soon. �Any further iOS work will go straight into master unless
>>>>> �it's destabilizing enough to warrant its own branch.
>>
>>>>> �On Fri, 30 Sep 2011, Joel Dice wrote:
>>>>>> �OK, my last email was premature (again). �There was one more thing
>>>>>> �that
>>>>>> �needed changing to guarantee we don't use any runtime-generated code,
>>>>>> �which
>>>>>> �I've just committed.
>>
>>>>>> �I only recently got access to a non-jailbroken device, which is why I
>>>>>> �didn't
>>>>>> �catch this until now. �I kind of wish the iOS Simulator was as
>>>>>> �restrictive
>>>>>> �about writable, executable memory as real, non-jailbroken hardware is;
>>>>>> �then I
>>>>>> �would have noticed this sooner.
>>
>>>>>> �On Wed, 28 Sep 2011, mzechner wrote:
>>
>>>>>>> ��This is amazing. I'll put this to work on my end and report back
>>>>>>> �how
>>>>>>> ��things go. Maybe i can at least help streamline the process.
>>
>>>>>>> ��I can't state often enough how much i appreciate the work you put
>>>>>>> �into
>>>>>>> ��this. Keep it up and thank you!
>>
>>>>>>> ��On 22 Sep., 20:54, Joel Dice <joel.d...@gmail.com> wrote:
>>>>>>>> ��On Mon, 19 Sep 2011, Joel Dice wrote:
>>>>>>>>> ��On Mon, 19 Sep 2011, mzechner wrote:
>>
>>>>>>>>>> ���I couldn't see anything official either, but the setiment
>>>>>>>>>> �on the
>>>>>>>>>> ��net
>>>>>>>>>> ���seems to be that this is in fact not possible. The reasons
>>>>>>>>>> �you gave
>>>>>>>>>> ���make sense. Is there a way to work-around the need for
>>>>>>>>>> �writeable
>>>>>>>>>> ��code
>>>>>>>>>> ���segments? I assume addresses get patched?
>>
>>>>>>>>> ��Yes, there are a couple of ways around it that I can think of;
>>>>>>>>> �I'm
>>>>>>>>> ��working on
>>>>>>>>> ��what I think will be the easiest. �I hope to have something
>>>>>>>>> �to show
>>>>>>>>> ��for it by
>>>>>>>>> ��the end of the week.
>>
>>>>>>>> ��I finished this work and created a simple iOS app that displays
>>>>>>>> �"Hello,
>>>>>>>> ��World!" using Java:
>>
>>>>>>>> ��� �git clone git://oss.readytalk.com/hello-ios.git
>>
>>>>>>>> ��There's a readme.txt in that project which explains how to build
>>>>>>>> �it.
>>>>>>>> ���The
>>>>>>>> ��build process currently requires a command-line step and a step
>>>>>>>> �to
>>>>>>>> ��finish
>>>>>>>> ��the build from within the IDE. �Someone with better XCode
>>>>>>>> �skills than
>>>>>>>> ��mine
>>>>>>>> ��may be able to streamline this so you can build entirely from
>>>>>>>> �within the
>>>>>>>> ��IDE or entirely from the command line.
>>
>>>>>>> ��--
>>>>>>> ��You received this message because you are subscribed to the Google
>>>>>>> �Groups
>>>>>>> ��"Avian" group.
>>>>>>> ��To post to this group, send email to av...@googlegroups.com.
>>>>>>> ��To unsubscribe from this group, send email to
>>>>>>> ��avian+un...@googlegroups.com.
>>>>>>> ��For more options, visit this group at
>>>>>>> ��http://groups.google.com/group/avian?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups "Avian" group.
> To post to this group, send email to av...@googlegroups.com.
> To unsubscribe from this group, send email to avian+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/avian?hl=en.
>
>
> Hi Joel,
>
> thanks for the reply. I'll try the updated hello-iOS example in a few
> minutes.
>
>> Yes, if you need to be able to load bytecode at runtime and you don't care
>> much about performance, that will work. I'll update the hello-ios example
>> makefile to provide this option when I have some time.
>
> That would be amazing. It would be incredibly helpful to be able to
> test on the device without having to
> precompile to machine code. Changed code could be pulled from a
> network server and recompiled on the fly.
If you mean recompiled to machine code, that won't be possible. Or
rather, compiling to machine code at runtime is certainly possible on iOS,
but actually executing it is not, since we aren't able to allocate memory
which is executable. However, if you're talking about recompiling from
source code to byte code on a build server and then loading and running
the bytecode on the fly in interpreted mode, yes, that should work.
As of this change, you can build an interpreted version of hello-ios using
"make process=interpret":
I'll leave runtime classloading using ClassLoader.defineClass as an
excercise for the reader.
>>> On Jan 7, 6:33�pm, Joel Dice <joel.d...@gmail.com> wrote:
>>>> On Sat, 7 Jan 2012, Joel Dice wrote:
>>>>> Hi Anton,
>>
>>>>> On Sat, 7 Jan 2012, Anton Shevchenko wrote:
>>
>>>>>> �I have some difficulties trying to build hello-ios to run on the
>>>>>> �iPhone.
>>>>>> �I have successfully built the hello-ios project for the simulator
>>>>>> �(needed to exclude objective c stuff from the hello.m file as Xcode3
>>>>>> �told about undefined symbols on the linker stage - anyway, this issue
>>>>>> �is not critical).
>>>>>> �So, after getting to run 'Hello world' in the simulator I decided to
>>>>>> �try out to build this sample for the iPhone (not jailbraked).
>>>>>> �When I build avian for the arm architecture I am getting the following
>>>>>> �error
>>
>>>>>> �Undefined symbols for architecture armv6:
>>>>>> � "_JNI_CreateJavaVM", referenced from:
>>>>>> � � � -[helloAppDelegate application:didFinishLaunchingWithOptions:]
>>>>>> �in helloAppDelegate.o
>>>>>> �ld: symbol(s) not found for architecture armv6
>>
>>>>>> �But when I am trying to build for the armv6 architecture the tool
>>>>>> �binaryToObject fails with 'unsupported platform'.
>>
>>>>> Did you follow the directions in readme.txt? �Note that you must build from
>>>>> the command line using the makefile. �If you build from within XCode first
>>>>> without running make, it will fail with the error you encountered.
>>
>>>>> If you still get that error even after running make from the command line,
>>>>> please post the complete output of "make clean && make". �Thanks.
>>
>>>>> By the way, if you're using the iOS 5.0 SDK, you'll need to change "4.3" to
>>>>> "5.0" in the makefile.
>>
>>>> Also, it is necessary to run "make clean" each time you switch from a
>>>> simulator build to a device (i.e. ARM) build and vice-versa.
>>
>>>>>>>>>> ��may be able to streamline this so you can build entirely from
>>>>>>>>>> �within the
>>>>>>>>>> ��IDE or entirely from the command line.
>>
>>>>>>>>> ��--
>>>>>>>>> ��You received this message because you are subscribed to the Google
>>>>>>>>> �Groups
>>>>>>>>> ��"Avian" group.
>>>>>>>>> ��To post to this group, send email to av...@googlegroups.com.
>>>>>>>>> ��To unsubscribe from this group, send email to
>>>>>>>>> ��avian+un...@googlegroups.com.
>>>>>>>>> ��For more options, visit this group at
>>>>>>>>> ��http://groups.google.com/group/avian?hl=en.
Thanks for the report. It should be fixed as of this commit:
http://oss.readytalk.com/gitweb?p=avian.git;a=commitdiff;h=5a5d2a8dd2c69432a4c70e3821931416e53a6b4e
Please let me know if you still have problems.
>>>> On Jan 7, 6:33�pm, Joel Dice <joel.d...@gmail.com> wrote:
>>>>> On Sat, 7 Jan 2012, Joel Dice wrote:
>>>>>> Hi Anton,
>>
>>>>>> On Sat, 7 Jan 2012, Anton Shevchenko wrote:
>>
>>>>>>> �I have some difficulties trying to build hello-ios to run on the
>>>>>>> �iPhone.
>>>>>>> �I have successfully built the hello-ios project for the simulator
>>>>>>> �(needed to exclude objective c stuff from the hello.m file as Xcode3
>>>>>>> �told about undefined symbols on the linker stage - anyway, this issue
>>>>>>> �is not critical).
>>>>>>> �So, after getting to run 'Hello world' in the simulator I decided to
>>>>>>> �try out to build this sample for the iPhone (not jailbraked).
>>>>>>> �When I build avian for the arm architecture I am getting the following
>>>>>>> �error
>>
>>>>>>> �Undefined symbols for architecture armv6:
>>>>>>> � "_JNI_CreateJavaVM", referenced from:
>>>>>>> � � � -[helloAppDelegate application:didFinishLaunchingWithOptions:]
>>>>>>> �in helloAppDelegate.o
>>>>>>> �ld: symbol(s) not found for architecture armv6
>>
>>>>>>> �But when I am trying to build for the armv6 architecture the tool
>>>>>>> �binaryToObject fails with 'unsupported platform'.
>>
>>>>>> Did you follow the directions in readme.txt? �Note that you must build from
>>>>>> the command line using the makefile. �If you build from within XCode first
>>>>>> without running make, it will fail with the error you encountered.
>>
>>>>>> If you still get that error even after running make from the command line,
>>>>>> please post the complete output of "make clean && make". �Thanks.
>>
>>>>>> By the way, if you're using the iOS 5.0 SDK, you'll need to change "4.3" to
>>>>>> "5.0" in the makefile.
>>
>>>>> Also, it is necessary to run "make clean" each time you switch from a
>>>>> simulator build to a device (i.e. ARM) build and vice-versa.
>>
>>>>>>>>>>> ��may be able to streamline this so you can build entirely from
>>>>>>>>>>> �within the
>>>>>>>>>>> ��IDE or entirely from the command line.
>>
>>>>>>>>>> ��--
>>>>>>>>>> ��You received this message because you are subscribed to the Google
>>>>>>>>>> �Groups
>>>>>>>>>> ��"Avian" group.
>>>>>>>>>> ��To post to this group, send email to av...@googlegroups.com.
>>>>>>>>>> ��To unsubscribe from this group, send email to
>>>>>>>>>> ��avian+un...@googlegroups.com.
>>>>>>>>>> ��For more options, visit this group at
>>>>>>>>>> ��http://groups.google.com/group/avian?hl=en.
> Joel,
>
> super! That fixed the problem.
>
>> However, if you're talking about recompiling from
>> source code to byte code on a build server and then loading and running
>> the bytecode on the fly in interpreted mode, yes, that should work.
>
> Yes, that's exactly what I was talking about. I'll try the updated
> hello-project in a minute.
>
> I came across another bug. envTable->DeleteWeakGlobalRef is not
> initialized in jnienv.cpp.
> I also noticed that NewWeakGlobalRef is simply pointing to
> NewGlobalRef.
> Is there any reason you did not implement the WeakRef methods. I'm not
> a Java expert, so I don't know
> if it's OK to not implement these methods. Maybe the official SDK
> actually implements them as stubs.
I doubt it. If the VM claims to support a JNI version greater than or
equal to whichever one introduced {New|Delete}WeakGlobalRef, it should
implement them properly. However, our process thus far has been to only
implement stuff as necessary to get a given app working. That way we have
a real-world test case to help make sure we get it right (or at least as
right as that particular use of the feature needs it to be). I guess
we've just never needed DeleteWeakGlobalRef, and I don't remember why
NewWeakGlobalRef was set to the NewGlobalRef.
Anyway, I'll look at what it takes to implement them properly.
>>> On Feb 28, 9:06�pm, Daniel Jaeger <dwckongreg...@googlemail.com>
>>> wrote:
>>>> Hi Joel,
>>
>>>> thanks for the reply. I'll try the updated hello-iOS example in a few
>>>> minutes.
>>
>>>>> Yes, if you need to be able to load bytecode at runtime and you don't care
>>>>> much about performance, that will work. �I'll update the hello-ios example
>>>>> makefile to provide this option when I have some time.
>>
>>>> That would be amazing. It would be incredibly helpful to be able to
>>>> test on the device without having to
>>>> precompile to machine code. Changed code could be pulled from a
>>>> network server and recompiled on the fly.
>>>> Building the boot/code image only for release mode would be the best
>>>> possible solution I guess.
>>
>>>> Best,
>>
>>>> Daniel
>>
>>>> On Feb 27, 5:29�pm, Joel Dice <joel.d...@gmail.com> wrote:
>>
>>>>> Hi Daniel,
>>
>>>>> On Sun, 26 Feb 2012, Daniel Jaeger wrote:
>>>>>> Hi Joel,
>>
>>>>>> great work on Avian.
>>>>>> I'm considering Avian as scripting-backend for a game. I've already
>>>>>> played around with it and it seems to work great.
>>>>>> As I understand it's not possible to allocate memory that's both
>>>>>> writeable and executable on iOS devices.
>>>>>> So the current solution is to compile the boot image into an object
>>>>>> file and mark this file executable.
>>>>>> For this process to work we have to use the "bootimage-generator" so
>>>>>> the java byte code can be directly executed without JITing it.
>>
>>>>> The bootimage-generator does two things: (1) it preparses all the class
>>>>> files in the application and creates a heap image which contains those
>>>>> classes in a VM-specific format, minus any bytecode, and (2) it
>>>>> ahead-of-time compiles all the bytecode to machine code to create a
>>>>> read-only code image.
>>
>>>>>> So here are my questions:
>>
>>>>>> - why is a code image even necessary in those cases? Wouldn't the boot
>>>>>> image be enough?
>>
>>>>> What I've been calling the boot image is composed of two things: the heap
>>>>> image and the code image. �If you don't want or need AOT compilation and
>>>>> are willing to accept the performance penalty of interpreted bytecode,
>>>>> you're better off not doing a boot image build at all and just embedding a
>>>>> jar file instead.
>>
>>>>>> - I've tried to build the hello-world sample with (make run-
>>>>>> proguard=false) but it failed. Why is pro guard required?
>>
>>>>> Sorry, that was a bug in the makefile. �Please pull the latest from the
>>>>> Git repo and try again.
>>
>>>>>> - would it be possible to avoid the JIT compilation and revert to a
>>>>>> slow interpreter, similar to Lua, that's safe on the iOS devices?
>>
>>>>> Yes, if you need to be able to load bytecode at runtime and you don't care
>>>>> much about performance, that will work. �I'll update the hello-ios example
>>>>> makefile to provide this option when I have some time.
>>
>>>>>> Best,
>>
>>>>>> Daniel
>>
>>>>>> On Jan 7, 6:33�pm, Joel Dice <joel.d...@gmail.com> wrote:
>>>>>>> On Sat, 7 Jan 2012, Joel Dice wrote:
>>>>>>>> Hi Anton,
>>
>>>>>>>> On Sat, 7 Jan 2012, Anton Shevchenko wrote:
>>
>>>>>>>>> �I have some difficulties trying to build hello-ios to run on the
>>>>>>>>> �iPhone.
>>>>>>>>> �I have successfully built the hello-ios project for the simulator
>>>>>>>>> �(needed to exclude objective c stuff from the hello.m file as Xcode3
>>>>>>>>> �told about undefined symbols on the linker stage - anyway, this issue
>>>>>>>>> �is not critical).
>>>>>>>>> �So, after getting to run 'Hello world' in the simulator I decided to
>>>>>>>>> �try out to build this sample for the iPhone (not jailbraked).
>>>>>>>>> �When I build avian for the arm architecture I am getting the following
>>>>>>>>> �error
>>
>>>>>>>>> �Undefined symbols for architecture armv6:
>>>>>>>>> � "_JNI_CreateJavaVM", referenced from:
>>>>>>>>> � � � -[helloAppDelegate application:didFinishLaunchingWithOptions:]
>>>>>>>>> �in helloAppDelegate.o
>>>>>>>>> �ld: symbol(s) not found for architecture armv6
>>
>>>>>>>>> �But when I am trying to build for the armv6 architecture the tool
>>>>>>>>> �binaryToObject fails with 'unsupported platform'.
>>
>>>>>>>> Did you follow the directions in readme.txt? �Note that you must build from
>>>>>>>> the command line using the makefile. �If you build from within XCode first
>>>>>>>> without running make, it will fail with the error you encountered.
>>
>>>>>>>> If you still get that error even after running make from the command line,
>>>>>>>> please post the complete output of "make clean && make". �Thanks.
>>
>>>>>>>> By the way, if you're using the iOS 5.0 SDK, you'll need to change "4.3" to
>>>>>>>> "5.0" in the makefile.
>>
>>>>>>> Also, it is necessary to run "make clean" each time you switch from a
>>>>>>> simulator build to a device (i.e. ARM) build and vice-versa.
>>
>>>>>>>>>>>>> ��may be able to streamline this so you can build entirely from
>>>>>>>>>>>>> �within the
>>>>>>>>>>>>> ��IDE or entirely from the command line.
>>
>>>>>>>>>>>> ��--
>>>>>>>>>>>> ��You received this message because you are subscribed to the Google
>>>>>>>>>>>> �Groups
>>>>>>>>>>>> ��"Avian" group.
>>>>>>>>>>>> ��To post to this group, send email to av...@googlegroups.com.
>>>>>>>>>>>> ��To unsubscribe from this group, send email to
>>>>>>>>>>>> ��avian+un...@googlegroups.com.
>>>>>>>>>>>> ��For more options, visit this group at
>>
>> ...
>>
>> read more �
>
> --
> You received this message because you are subscribed to the Google Groups "Avian" group.
> To post to this group, send email to av...@googlegroups.com.
> To unsubscribe from this group, send email to avian+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/avian?hl=en.
>
>
> On Tue, 28 Feb 2012, Daniel Jaeger wrote:
>
>> Joel,
>>
>> super! That fixed the problem.
>>
>> > However, if you're talking about recompiling from
>> > source code to byte code on a build server and then loading and running
>> > the bytecode on the fly in interpreted mode, yes, that should work.
>>
>> Yes, that's exactly what I was talking about. I'll try the updated
>> hello-project in a minute.
>>
>> I came across another bug. envTable->DeleteWeakGlobalRef is not
>> initialized in jnienv.cpp.
>> I also noticed that NewWeakGlobalRef is simply pointing to
>> NewGlobalRef.
>> Is there any reason you did not implement the WeakRef methods. I'm not
>> a Java expert, so I don't know
>> if it's OK to not implement these methods. Maybe the official SDK
>> actually implements them as stubs.
>
> I doubt it. If the VM claims to support a JNI version greater than or equal
> to whichever one introduced {New|Delete}WeakGlobalRef, it should implement
> them properly. However, our process thus far has been to only implement
> stuff as necessary to get a given app working. That way we have a real-world
> test case to help make sure we get it right (or at least as right as that
> particular use of the feature needs it to be). I guess we've just never
> needed DeleteWeakGlobalRef, and I don't remember why NewWeakGlobalRef was set
> to the NewGlobalRef.
>
> Anyway, I'll look at what it takes to implement them properly.
I've attached a patch which implements them. Please let me know if it
works for you and I'll commit it.
Reading up on the semantics of these methods at
http://java.sun.com/docs/books/jni/, I feel like the design is inherently
dangerous. For example, consider:
jobject weak = NewWeakGlobalRef(env, foo);
...
if (! (*env)->IsSameObject(env, weak, NULL)) {
// DANGER: if a GC occurs after the call to IsSameObject above and
// before the call to CallVoidMethod below, "weak" may be nulled
// out! Since JNI makes no guarantee about what happens when null
// references are dereferenced
// (http://java.sun.com/docs/books/jni/html/pitfalls.html#11223),
// this could lead to a crash or something similarly unpleasant.
(*env)->CallVoidMethod(env, weak, someMethodID);
}
Is there something I'm missing here, or is this really just a
fundamentally unsafe API?
Replying to my reply to myself: yes, the above example is dangerous and
can potentially crash any VM, as explained here:
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html#weak
The only way to make the above example safe is something like this:
jobject weak = NewWeakGlobalRef(env, foo);
...
jobject strong = NewGlobalRef(env, weak);
if (strong) {
(*env)->CallVoidMethod(env, strong, someMethodID);
(*env)->DeleteGlobalRef(env, strong);
> Joel,
>
> that was fast again. Excellent.
>
> Yes, this is dangerous. But consider the case where the VM creates a
> jobject.
> The object's constructor invokes a native method that creates the
> native class-instance that belongs to the jobject.
> The native class also wants to hold a permanently valid reference to
> the jobject in the VM and vice versa.
> But the reference should not stop the VM from garbage collecting the
> object instance. Thus we need a weak global reference and not a strong
> global reference.
> If the object is being deleted by the VM, the VM will also invoke the
> finalizer and kill the native object (if the user didn't do it
> manually earlier).
>
> ...
>
> I guess even in the example you have posted above it could lead to a
> crash. If the object dies in-between the weak and strong reference
> creations.
If that happens, the NewGlobalRef call to create the strong reference will
return null, in which case we won't try to dereference it. Unless I'm
missing something, there's no possibility of a crash at any point (unless
there's a bug in the VM, of course).
> The only solid situation is where the jobject owns the object that
> holds the weak reference : and both die together.
>
> I haven't had time to see if the interpreter run works on the iOS
> devices. Do you pass special preprocessor flags to GCC when building
> an interpreter build or is this something that can be changed during
> runtime? Your related commit didn't seem to change much at all,
> besides specifying the bootclasspath jar.
If you specify a process=interpret option to make, the -DBOOT_IMAGE
preprocessor flag will *not* be passed to GCC, which means boot.c will
define a bootJar function instead of the bootimageBin and codeimageBin
functions defined for the bootimage build. When the VM is initialized, it
will first try to resolve the bootimageBin and codeimageBin functions, but
that will fail in the interpreted build, so it will fall back to a normal
initialization process, using the bootJar function to get access to the
embedded JAR file containing both the application classes and the standard
library classes.
If you're asking whether it's possible to build Avian such that a boot
image is used for all the classes known at build time, but still allow the
interpreter to load classes at runtime, no, that's not currently possible.
We can do it on platforms where JIT compilation is available (i.e.
everywhere but iOS), but the non-JIT interpreter is not currently
compatible with boot image builds. Making it compatible would require
some refactoring, which I'd be willing to pursue if there's enough
interest in it.
>>>>> � On Feb 28, 11:39�pm, Joel Dice <joel.d...@gmail.com> wrote:
>>>>>> � On Tue, 28 Feb 2012, Daniel Jaeger wrote:
>>>>>>> � Joel,
>>
>>>>>>> � building for iOS with the new makefile without pro-guard works just
>>>>>>> � fine!
>>>>>>> � However I can't seem to be able to pass float values to Java.
>>>>>>> � I've added the following method to the Hello.java file:
>>
>>>>>>> � � �public static void FloatTest(float one, float two, float three)
>>>>>>> � � �{
>>>>>>> � � � � � � �System.out.println("one=" + one + " two=" + two + "
>>>>>>> � three=" +
>>>>>>> � three);
>>>>>>> � � �}
>>
>>>>>>> � EOF
>>
>>>>>>> � To invoke the method I added the following code to
>>>>>>> � "helloAppDelegate.m" line=88
>>
>>>>>>> � � � �jmethodID floatMethod = (*e)->GetMethodID(e, hello,
>>>>>>> � "FloatTest",
>>>>>>> � "(FFF)V");
>>
>>>>>>> � � � �if (! (*e)->ExceptionCheck(e)) {
>>>>>>> � � � � � � � �float one = 123;
>>>>>>> � � � � � � � �float two = 456;
>>>>>>> � � � � � � � �float three = 789;
>>>>>>> � � � � � � � �(*e)->CallStaticVoidMethod(e, hello, floatMethod, one,
>>>>>>> � two ,
>>>>>>> � three);
>>>>>>> � � � �}
>>
>>>>>>> � EOF
>>
>>>>>>> � However this results in the following output:
>>
>>>>>>> � one=0 two=3.48047 three=0
>>
>>>>>>> � The identical code works if doubles are used instead of floats in
>>>>>>> � both
>>>>>>> � Java and C.
>>
>>>>>> � Thanks for the report. �It should be fixed as of this commit:
>>
>>>>>> �http://oss.readytalk.com/gitweb?p=avian.git;a=commitdiff;h=5a5d2a8dd2...
>>
>>>>>> � Please let me know if you still have problems.
>>
>>>>>>> � Best,
>>
>>>>>>> � Daniel
>>
>>>>>>> � On Feb 28, 9:06�pm, Daniel Jaeger <dwckongreg...@googlemail.com>
>>>>>>> � wrote:
>>>>>>>> � Hi Joel,
>>
>>>>>>>> � thanks for the reply. I'll try the updated hello-iOS example in a
>>>>>>>> � few
>>>>>>>> � minutes.
>>
>>>>>>>>> � Yes, if you need to be able to load bytecode at runtime and you
>>>>>>>>> � don't care
>>>>>>>>> � much about performance, that will work. �I'll update the
>>>>>>>>> � hello-ios example
>>>>>>>>> � makefile to provide this option when I have some time.
>>
>>>>>>>> � That would be amazing. It would be incredibly helpful to be able
>>>>>>>> � to
>>>>>>>> � test on the device without having to
>>>>>>>> � precompile to machine code. Changed code could be pulled from a
>>>>>>>> � network server and recompiled on the fly.
>>>>>>>> � Building the boot/code image only for release mode would be the
>>>>>>>> � best
>>>>>>>> � possible solution I guess.
>>
>>>>>>>> � Best,
>>
>>>>>>>> � Daniel
>>
>>>>>>>>> � image and the code image. �If you don't want or need AOT
>>>>>>>>> � compilation and
>>>>>>>>> � are willing to accept the performance penalty of interpreted
>>>>>>>>> � bytecode,
>>>>>>>>> � you're better off not doing a boot image build at all and just
>>>>>>>>> � embedding a
>>>>>>>>> � jar file instead.
>>
>>>>>>>>>> � - I've tried to build the hello-world sample with (make run-
>>>>>>>>>> � proguard=false) but it failed. Why is pro guard required?
>>
>>>>>>>>> � Sorry, that was a bug in the makefile. �Please pull the
>>>>>>>>> � latest from the
>>>>>>>>> � Git repo and try again.
>>
>>>>>>>>>> � - would it be possible to avoid the JIT compilation and
>>>>>>>>>> � revert to a
>>>>>>>>>> � slow interpreter, similar to Lua, that's safe on the iOS
>>>>>>>>>> � devices?
>>
>>>>>>>>> � Yes, if you need to be able to load bytecode at runtime and you
>>>>>>>>> � don't care
>>>>>>>>> � much about performance, that will work. �I'll update the
>>>>>>>>> � hello-ios example
>>>>>>>>> � makefile to provide this option when I have some time.
>>
>>>>>>>>>> � Best,
>>
>>>>>>>>>> � Daniel
>>
>>>>>>>>>> � On Jan 7, 6:33�pm, Joel Dice <joel.d...@gmail.com> wrote:
>>>>>>>>>>> � On Sat, 7 Jan 2012, Joel Dice wrote:
>>>>>>>>>>>> � Hi Anton,
>>
>>>>>>>>>>>> � On Sat, 7 Jan 2012, Anton Shevchenko wrote:
>>
>>>>>>>>>>>>> � �I have some difficulties trying to build hello-ios
>>>>>>>>>>>>> � to run on the
>>>>>>>>>>>>> � �iPhone.
>>>>>>>>>>>>> � �I have successfully built the hello-ios project for
>>>>>>>>>>>>> � the simulator
>>
>> ...
>>
> Joel,
>
> ah. I didn't notice that the interpreter was already available, don't
> know how I could miss that.
> I've directly setup my own project files to build the VM with the
> compiler included.
> I just added interpret.cpp and removed architecture specific files and
> it works perfectly.
>
>> If you're asking whether it's possible to build Avian such that a boot
>> image is used for all the classes known at build time, but still allow the
>> interpreter to load classes at runtime, no, that's not currently possible.
> The boot image compiled as machine code, and additional classes in the
> class path executed with the interpreter?
> While that would be great, it's not necessary we're going to use the
> interpreter for debug builds on the device and when
> building for release mode we're going to build the boot image and
> deploy it to the device.
That makes sense.
> However it would be great to switch between interpreted mode and
> compiled mode during VM construction. So we can use a single library
> to link.
> But from looking at the code architecture it's seems that this is not
> possible right now as most methods related to code execution are not
> bound to an interface.
It's actually abstracted pretty nicely. The interface in question is in
processor.h, and the two implementations of that interface are in
interpret.cpp (pure interpreter) and compile.cpp (AOT and JIT
compilation). It would take very little work to allow both
implementations to exist in the same build, as long as you don't expect to
use them both in the same VM instance.
> But even more important than this would be a thorough documentation of
> the methods in the code. And maybe an improved structure of the
> codebase, less use of namespace{} , refactoring C methods into C++
> classes. Defines like TARGET_BYTES_PER_WORD should be moved into a
> config file that configures the build without having to fall back to
> user specified preprocessor macros. Best case is if these things are
> based on std. compiler defines i.e:
>
> #ifdef __LP64__
> # define TARGET_BYTES_PER_WORD 8
> #else
> # define TARGET_BYTES_PER_WORD 4
> #endif
I definitely agree that the documentation could be improved. The need
just hasn't been urgent because there hasn't been a lot of interest in
working on the internals of the VM beyond myself and a few colleagues. I
guess it's a chicken-and-egg problem: the VM needs better documentation to
make it accessible to new developers, but there's little incentive to
write it until those developers dive in and start complaining about the
lack of docs :)
The problem with defining TARGET_BYTES_PER_WORD based on __LP64__, by the
way, is that the target system we're building a boot image for may not be
the same as the one we're building the boot image on. For example, we may
be building a 32-bit Windows boot image on a 64-bit Linux machine, or
vice-versa. You can't rely on built-in macros to figure that out -- it
has to be specified explicitly.
As for reducing the use of anonymous namespaces and moving functions into
classes, I don't see how they would improve the code. There are only two
ways in C++ to indicate to the compiler that a declaration is local to the
compilation unit it appears in: the "static" keyword and anonymous
namespaces. The former has been deprecated for over a decade, so the
latter is the idiomatic way to do it. Also, you can't use the static
keyword on a class definition, but you can put it in an anonymous
namespace.
Whether a non-virtual function resides inside a class or outside seems
like a matter of style to me. It generally comes down to whether you
prefer calls that look like "foo->doSomething()" or "doSomething(foo)".
I tend to prefer the latter for non-virtual functions because you can tell
at a glance that the latter is not a polymorphic call, whereas the former
may or may not be -- you'll need to find out what the type of "foo" is and
then inspect that class to find out. Obviously, there are also good
reasons to put methods inside classes, such as encapsulation and virtual
dispatch, and the VM already makes good use of those features where
appropriate.
>>> On Feb 29, 2:02�am, Joel Dice <joel.d...@gmail.com> wrote:
>>>> On Tue, 28 Feb 2012, Joel Dice wrote:
>>>>> On Tue, 28 Feb 2012, Joel Dice wrote:
>>
>>>>>> �On Tue, 28 Feb 2012, Daniel Jaeger wrote:
>>
>>>>>>> � Joel,
>>
>>>>>>> � super! That fixed the problem.
>>
>>>>>>>> � However, if you're talking about recompiling from
>>>>>>>> � source code to byte code on a build server and then loading and
>>>>>>>> � running
>>>>>>>> � the bytecode on the fly in interpreted mode, yes, that should work.
>>
>>>>>>> � Yes, that's exactly what I was talking about. I'll try the updated
>>>>>>> � hello-project in a minute.
>>
>>>>>>> � I came across another bug. envTable->DeleteWeakGlobalRef is not
>>>>>>> � initialized in jnienv.cpp.
>>>>>>> � I also noticed that NewWeakGlobalRef is simply pointing to
>>>>>>> � NewGlobalRef.
>>>>>>> � Is there any reason you did not implement the WeakRef methods. I'm not
>>>>>>> � a Java expert, so I don't know
>>>>>>> � if it's OK to not implement these methods. Maybe the official SDK
>>>>>>> � actually implements them as stubs.
>>
>>>>>> �I doubt it. �If the VM claims to support a JNI version greater than or
>>>>>> �equal to whichever one introduced {New|Delete}WeakGlobalRef, it should
>>>>>> �implement them properly. �However, our process thus far has been to only
>>>>>> �implement stuff as necessary to get a given app working. �That way we have
>>>>>> �a real-world test case to help make sure we get it right (or at least as
>>>>>> �right as that particular use of the feature needs it to be). �I guess
>>>>>> �we've just never needed DeleteWeakGlobalRef, and I don't remember why
>>>>>> �NewWeakGlobalRef was set to the NewGlobalRef.
>>
>>>>>> �Anyway, I'll look at what it takes to implement them properly.
>>
>>>>> I've attached a patch which implements them. �Please let me know if it works
>>>>> for you and I'll commit it.
>>
>>>>> Reading up on the semantics of these methods at
>>>>> http://java.sun.com/docs/books/jni/, I feel like the design is inherently
>>>>> dangerous. �For example, consider:
>>
>>>>> �jobject weak = NewWeakGlobalRef(env, foo);
>>
>>>>> �...
>>
>>>>> � if (! (*env)->IsSameObject(env, weak, NULL)) {
>>>>> � � // DANGER: if a GC occurs after the call to IsSameObject above and
>>>>> � � // before the call to CallVoidMethod below, "weak" may be nulled
>>>>> � � // out! �Since JNI makes no guarantee about what happens when null
>>>>> � � // references are dereferenced
>>>>> � � // (http://java.sun.com/docs/books/jni/html/pitfalls.html#11223),
>>>>> � � // this could lead to a crash or something similarly unpleasant.
>>>>> � � (*env)->CallVoidMethod(env, weak, someMethodID);
>>>>> � }
>>
>>>>> Is there something I'm missing here, or is this really just a fundamentally
>>>>> unsafe API?
>>
>>>> Replying to my reply to myself: yes, the above example is dangerous and
>>>> can potentially crash any VM, as explained here:
>>
>>>> � �http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.htm...
>>
>>>> The only way to make the above example safe is something like this:
>>
>>>> � �jobject weak = NewWeakGlobalRef(env, foo);
>>
>>>> � �...
>>
>>>> � �jobject strong = NewGlobalRef(env, weak);
>>>> � �if (strong) {
>>>> � � �(*env)->CallVoidMethod(env, � � strong, someMethodID);
>>>> � � �(*env)->DeleteGlobalRef(env, strong);
>>>> � �}
>>
>>>>>>> � However, I can't simply use NewGlobalRef for C++ objects as the GC
>>>>>>> � won't be able to collect these retained objects,
>>>>>>> � where a Java object is the owner of a C++ instance.
>>>>>>> � For these cases I would have used NewWeakGlobalRef.
>>>>>>> � Right now I'm not retaining the jobjects in question, but according to
>>>>>>> � the JNI-SDK these references can simply change.
>>
>>>>>>> ~ �Thanks
>>
>>>>>>> � On Feb 28, 11:39�pm, Joel Dice <joel.d...@gmail.com> wrote:
>>>>>>>> � On Tue, 28 Feb 2012, Daniel Jaeger wrote:
>>>>>>>>> � Joel,
>>
>>>>>>>>>> � On Feb 27, 5:29�pm, Joel Dice
>>
>> ...
>>
> Joel,
>
My understanding is that alloca is function-local, not block-local,
meaning if you call it inside a loop, the stack space will keep growing on
every iteration. That's in contrast with runtime-sized arrays which are
deallocated (or reused) when they go out of scope, not just when the
function containing that scope returns.
> ~~~
> I've finished implementing the VM in our codebase and I'm starting to
> work on the bindings but I'm noticing a lot of performance overhead
> when calling into native, or from native to VM. Ofcourse I anticipated
> some overhead but this is much more than I expected. There is also a
> high performance overhead when accessing properties.
> Every access is cached during VM startup: jfieldIDs, jclass's and
> jmethodIDs.
> The following metrics have been done on an iPhone 3GS, with the
> project compiled for release mode and using a boot image.
>
> native to vm invocation count=256 time=6ms
> native to vm field access count=256 time=3ms
>
> This is pretty heavy as the same code path without the field access
> takes only 1.5ms, but with the invocations takes 10ms.
That surprises me. Are you caching the jmethodIDs and jfieldIDs or
looking them up every time? If you're not caching them, that's probably
where the time is going. If you are, can you send me a standalone test
case so I can profile it?
> The method invocations could be optimized away in some cases by doing
> the following in java code.
>
> if ( javaobject.getfield "overridesthinkmethod" == true )
> {
> javaobject.invokevm "thinkmethod" (time, deltatime);
> }
>
> I'm not sure how Avian handles field access but according to the Sun
> docs field access should be quite snappy:
> "The overhead of field access using the JNI lies in the cost of
> calling through the JNIEnv. Rather than directly dereferencing
> objects, the native code has to perform a C function call which in
> turn dereferences the object. The function call is necessary because
> it isolates the native code from the internal object representation
> maintained by the virtual machine implementation. The JNI field access
> overhead is typically negligible because a function call takes only a
> few cycles."
There's more to it than that, though, when the VM uses a copying garbage
collector. We need to pin the object (or static field table) being
dereferenced in memory so that another thread doesn't try to move it while
we're reading from it. Also, we need to check whether the field is
volatile and, if it is, either synchronize access to it or execute the
appropriate memory barrier(s) to preserve volatile semantics.
Looking at the current implementations of Get*Field and Set*Field, I see
that we are currently using the "run" function to capture the thread state
in case an exception is thrown in the process of getting or setting the
field. If I can prove that no such exception is possible, I ought to be
able to remove that overhead, which should speed up your test case. We
can't do that for method invocations, though, since they may throw
exceptions.
FWIW, you're best bet for maximizing JNI performance with any VM is to
avoid native->java calls and field lookups wherever possible. Just pass
everything to your native method as primitives so it doesn't have to look
up fields in an object or call methods to get more info. If you need to
return more than one value, consider putting them in a primitive array
passed down from the java caller to be filled in by the native method.
Obviously, these techniques don't apply to all cases, but I've found them
to be valuable in general.
> Could method invocations from java to native be optimized during boot
> image generation by directly linking to the native symbol?
Yes, at a cost in code size. We still need to marshal the arguments into
the form dictated by JNI, which is different from the java to java calling
convention used by the VM, so we would need to either generate that
marshalling code at each call site or generate a "thunk" which does the
marshalling and through which all calls to the native method are routed.
If you really want to maximize java to native call speed, you can use an
Avian-specific fast calling convention, but then your native code will
only work with Avian, and you'll need to avoid blocking in such a method,
since the VM won't be able to GC until it returns.
> So the methodname/-address lookup could be skipped during runtime?
The address lookup is cached by the VM, so I doubt that's the bottleneck.
>>> On Feb 29, 3:22�am, Joel Dice <joel.d...@gmail.com> wrote:
>>>> On Tue, 28 Feb 2012, Daniel Jaeger wrote:
>>>>> Joel,
>>
>>>>> that was fast again. Excellent.
>>
>>>>> Yes, this is dangerous. But consider the case where the VM creates a
>>>>> jobject.
>>>>> The object's constructor invokes a native method that creates the
>>>>> native class-instance that belongs to the jobject.
>>>>> The native class also wants to hold a permanently valid reference to
>>>>> the jobject in the VM and vice versa.
>>>>> But the reference should not stop the VM from garbage collecting the
>>>>> object instance. Thus we need a weak global reference and not a strong
>>>>> global reference.
>>>>> If the object is being deleted by the VM, the VM will also invoke the
>>>>> finalizer and kill the native object (if the user didn't do it
>>>>> manually earlier).
>>
>>>>> ...
>>
>>>>> I guess even in the example you have posted above it could lead to a
>>>>> crash. If the object dies in-between the weak and strong reference
>>>>> creations.
>>
>>>> If that happens, the NewGlobalRef call to create the strong reference will
>>>> return null, in which case we won't try to dereference it. �Unless I'm
>>>> missing something, there's no possibility of a crash at any point (unless
>>>> there's a bug in the VM, of course).
>>
>>>>> The only solid situation is where the jobject owns the object that
>>>>> holds the weak reference : and both die together.
>>
>>>>> I haven't had time to see if the interpreter run works on the iOS
>>>>> devices. Do you pass special preprocessor flags to GCC when building
>>>>> an interpreter build or is this something that can be changed during
>>>>> runtime? Your related commit didn't seem to change much at all,
>>>>> besides specifying the bootclasspath jar.
>>
>>>> If you specify a process=interpret option to make, the -DBOOT_IMAGE
>>>> preprocessor flag will *not* be passed to GCC, which means boot.c will
>>>> define a bootJar function instead of the bootimageBin and codeimageBin
>>>> functions defined for the bootimage build. �When the VM is initialized, it
>>>> will first try to resolve the bootimageBin and codeimageBin functions, but
>>>> that will fail in the interpreted build, so it will fall back to a normal
>>>> initialization process, using the bootJar function to get access to the
>>>> embedded JAR file containing both the application classes and the standard
>>>> library classes.
>>
>>>> If you're asking whether it's possible to build Avian such that a boot
>>>> image is used for all the classes known at build time, but still allow the
>>>> interpreter to load classes at runtime, no, that's not currently possible.
>>>> We can do it on platforms where JIT compilation is available (i.e.
>>>> everywhere but iOS), but the non-JIT interpreter is not currently
>>>> compatible with boot image builds. �Making it compatible would require
>>>> some refactoring, which I'd be willing to pursue if there's enough
>>>> interest in it.
>>
>>>>> Your patch seemed to work great! Thanks.
>>
>>>>> Daniel
>>
>>>>> On Feb 29, 2:02�am, Joel Dice <joel.d...@gmail.com> wrote:
>>>>>> On Tue, 28 Feb 2012, Joel Dice wrote:
>>>>>>> On Tue, 28 Feb 2012, Joel Dice wrote:
>>
>>>>>>>> �On Tue, 28 Feb 2012, Daniel Jaeger wrote:
>>
>>>>>>>>> � Joel,
>>
>>>>>>>>> � super! That fixed the problem.
>>
>>>>>>>>>> � However, if you're talking about recompiling from
>>>>>>>>>> � source code to byte code on a build server and then loading and
>>>>>>>>>> � running
>>>>>>>>>> � the bytecode on the fly in interpreted mode, yes, that should work.
>>
>>>>>>>>> � Yes, that's exactly what I was talking about. I'll try the updated
>>>>>>>>> � hello-project in a minute.
>>
>>>>>>>>> � I came across another bug. envTable->DeleteWeakGlobalRef is not
>>>>>>>>> � initialized in jnienv.cpp.
>>>>>>>>> � I also noticed that NewWeakGlobalRef is simply pointing to
>>>>>>>>> � NewGlobalRef.
>>>>>>>>> � Is there any reason you did not implement the WeakRef methods. I'm not
>>>>>>>>> � a Java expert, so I don't know
>>>>>>>>> � if it's OK to not implement these methods. Maybe the official SDK
>>>>>>>>> � actually implements them as stubs.
>>
>>>>>>>> �I doubt it. �If the VM claims to support a JNI version greater than or
>>>>>>>> �equal to whichever one introduced {New|Delete}WeakGlobalRef, it should
>>>>>>>> �implement them properly. �However, our process thus far has been to only
>>>>>>>> �implement stuff as necessary to get a given app working. �That way we have
>>>>>>>> �a real-world test case to help make sure we get it right (or at least as
>>>>>>>> �right as that particular use of the feature needs it to be). �I guess
>>>>>>>> �we've just never needed DeleteWeakGlobalRef, and I don't remember why
>>>>>>>> �NewWeakGlobalRef was set to the NewGlobalRef.
>>
>>>>>>>> �Anyway, I'll look at what it takes to implement them properly.
>>
>>>>>>> I've attached a patch which implements them. �Please let me know if it works
>>>>>>> for you and I'll commit it.
>>
>>>>>>> Reading up on the semantics of these methods at
>>>>>>> http://java.sun.com/docs/books/jni/, I feel like the design is inherently
>>>>>>> dangerous. �For example, consider:
>>
>>>>>>> �jobject weak = NewWeakGlobalRef(env, foo);
>>
>>>>>>> �...
>>
>>>>>>> � if (! (*env)->IsSameObject(env, weak, NULL)) {
>>>>>>> � � // DANGER: if a GC occurs after the call to IsSameObject above and
>>>>>>> � � // before the call to CallVoidMethod below, "weak" may be nulled
>>>>>>> � � // out! �Since JNI makes no guarantee about what happens when null
>>>>>>> � � // references are dereferenced
>>>>>>> � � //
>>
>> My understanding is that alloca is function-local, not block-local,
>> meaning if you call it inside a loop, the stack space will keep growing on
>> every iteration. That's in contrast with runtime-sized arrays which are
>> deallocated (or reused) when they go out of scope, not just when the
>> function containing that scope returns.
> Yes, that's true, alloca blocks stay around until the function returns
> to the caller.
>
>> That surprises me. Are you caching the jmethodIDs and jfieldIDs or
>> looking them up every time? If you're not caching them, that's probably
>> where the time is going. If you are, can you send me a standalone test
>> case so I can profile it?
> Yes, as said. Both are cached when the VM starts. This is exactly the
> code from the
> engine:
>
Sorry, I should have read more carefully.
> vmObject_t *vmObject = object->GetValue();
> vmField_t *vmField = field->GetValue();
>
> PROFILE_FIELD_GETTER_START();
> bool value = _environment->GetBooleanField(vmObject, vmField);
> PROFILE_FIELD_GETTER_STOP();
>
> return value;
>
> The performance penalty scales up nearly linearly, with 1000 field
> lookups it's around 10ms.
> It's actually only a bool, so I wouldn't have thought that there are
> deeper lookups involved.
> What kind of test case are you looking for? A binary? But as you can
> see in the code above, nothing
> really dramatic happens.
I guess what I'm looking for is a realistic benchmark that I can profile.
Obviously, calling GetBooleanField in a tight loop will make it prominent
in a profile, but that can be addressed by just making one call before
entering the loop. I'd like to see a realistic design where Get*Field is
a bottleneck such that it can't be easily redesigned in such a way that
said method is no longer a bottleneck. I'm not claiming such a case is
unrealistic, but I'm having a hard time imagining it.
>> There's more to it than that, though, when the VM uses a copying garbage
>> collector. We need to pin the object (or static field table) being
>> dereferenced in memory so that another thread doesn't try to move it while
>> we're reading from it. Also, we need to check whether the field is
>> volatile and, if it is, either synchronize access to it or execute the
>> appropriate memory barrier(s) to preserve volatile semantics.
> Are there different GC implementations available right now? Execution
> performance is most important to us,
> games typically only allocate new objects during initialization or
> restart. So if there is a garbage collector available
> that trades allocation speed for faster lookups that'd be great.
No, sorry -- there's only one GC. Anyway, we ought to be able to make the
fast path fast enough and still safe with the current GC, especially for
workloads where collections are rare.
>> Looking at the current implementations of Get*Field and Set*Field, I see
>> that we are currently using the "run" function to capture the thread state
>> in case an exception is thrown in the process of getting or setting the
>> field. If I can prove that no such exception is possible, I ought to be
>> able to remove that overhead, which should speed up your test case. We
>> can't do that for method invocations, though, since they may throw
>> exceptions.
> Maybe it would be a good idea, at least for us, to introduce unsafe
> methods for
> property and method access then.
>
> <snip>
>
> I just tested the performance if we're not going down the run route,
> and we're not locking the field etc.:
>
> jboolean JNICALL GetBooleanField(Thread* t, jobject o, jfieldID
> field)
> {
> return cast<jboolean>(*o, fieldOffset(t, getField(t, field)));
> }
That might crash if another thread, during GC, deallocates the memory
previously allocated for that object while you're trying to dereference
it. Adding "ENTER(t, Thread::ActiveState);" right above the dereference
would fix that and shouldn't hurt performance much. It still won't be
correct for volatile fields, but it won't crash.
FWIW, this is what Avian's implementation used to look like:
jboolean JNICALL
GetBooleanField(Thread* t, jobject o, jfieldID field)
{
ENTER(t, Thread::ActiveState);
return cast<jboolean>(*o, field);
}
Back then, "field" was just an offset. It was only later that we
supported volatile fields properly, for which we need to look up the
VMField instance to check whether it is volatile or not. I guess we could
go back to the old way and just treat all fields as volatile. Might be an
interesting experiment.
> This greatly improves performance, 1000 lookups down to 3msec. Our
> scenario is that we're calling
> into the VM synchronously whenever the engine's Think... or Render...
> methods occur. I guess there are a lot
> of possibilities to improve performance at the cost of risking
> stability. But I think this situation allows
> us to be unsafe regarding object locks here, especially when getting/
> settings primitives.
> But I'm not sure about it, as I don't know how the actual VM works.
> Maybe you can clear this up:
> We will only call the getter/setter methods from the native side of
> the object in a synchronous fashion and
> if the vm object is the owner of the native object.
> Basically we can be sure that it won't be gc'd else we wouldn't be
> running through its think method.
It doesn't matter if it's reachable or not; objects may be moved during GC
and the old memory deleted, so any thread which tries to dereference the
old pointer will potentially crash. This is true even if the object
remains alive, since it's now in a new location.
> If we're running through a think method the object has to be added to
> a collection that is global i.e. at the end of the hierarchy it must
> be added to the main
> static container object enabling the game loop.
>
> Besides doing all the calls into the VM synchronously i.e. either
> native game code or java code executed,
> we're also running the engine and all calls into the vm on a single
> thread.
> Other parts that might run on a separate thread will never have the
> ability to call into the vm at all (i.e. the graphics renderer)
That will avoid the type of crash I described above, but it does limit
your options for multithreading in the future, and I can't guarantee Avian
might not someday launch internal threads which may trigger GCs.
>
> If we're not using a copying garbage collector, could we store a
> pointer to the reference obtained through the following call?
>
> fieldOffset(t, getField(t, field)))
Yes, that works if the GC never moves objects.
> As said, we're operating on objects owned by the VM object so we're
> sure it will be alive when we're getting properties.
>
>> FWIW, you're best bet for maximizing JNI performance with any VM is to
>> avoid native->java calls and field lookups wherever possible. Just pass
>> everything to your native method as primitives so it doesn't have to look
>> up fields in an object or call methods to get more info. If you need to
>> return more than one value, consider putting them in a primitive array
>> passed down from the java caller to be filled in by the native method.
>> Obviously, these techniques don't apply to all cases, but I've found them
>> to be valuable in general.
> Yes, that is exactly what I'm trying to do, avoid lookups wherever
> possible.
> About passing arrays. Consider that we have to fill multiple float[3]
> fields of an object at once.
> Would it be faster to create a large primitive array and fill it with
> all fields and pass it to the vm
> or simply set all float[3] arrays using the SetField methods? I guess
> if we can do it without
> locking and doing a "run" then primitive-locking-free setters would be
> faster.
Filling a large array and passing it to the VM will be faster currently,
and even if we optimize Set*Field as much as we can, I think the large
array strategy will still be faster.
>
>> Yes, at a cost in code size. We still need to marshal the arguments into
>> the form dictated by JNI, which is different from the java to java calling
>> convention used by the VM, so we would need to either generate that
>> marshalling code at each call site or generate a "thunk" which does the
>> marshalling and through which all calls to the native method are routed.
>
> Would it make sense to implement it performance wise?
Hard to say without trying it and profiling a realistic test case. I've
never encountered an app for which native call overhead was a bottleneck;
usually each call has enough work to do that the overhead was
insignificant compared to the time it took to do the work itself. I don't
do much game programming, though :)
>> If you really want to maximize java to native call speed, you can use an
>> Avian-specific fast calling convention, but then your native code will
>> only work with Avian, and you'll need to avoid blocking in such a method,
>> since the VM won't be able to GC until it returns.
>
> That's no problem, if everything continues to work out as it does
> right now,
> we're going to stick with Avian. We're really fond of the lightweight
> codebase.
> What has to be changed to export methods to be Avian compatible?
You can see some examples in builtin.cpp. The naming convention is just
like for JNI, except the prefix is "Avian_", not "Java_". Functions using
this convention always take three arguments and return int64_t or void.
For example:
extern "C" JNIEXPORT int64_t JNICALL
Avian_avian_resource_Handler_00024ResourceInputStream_read__JI_3BII
(Thread* t, object /*method*/, uintptr_t* arguments)
{
// t is the thread-local state
// method is a pointer to the VMMethod object being called
// arguments is an array of arguments being passed to the method, where
// longs (i.e. 64-bit values) always take two slots, regardless of
// whether we're on a 32-bit or 64-bit platform.
int64_t peer; memcpy(&peer, arguments, 8);
int32_t position = arguments[2];
object buffer = reinterpret_cast<object>(arguments[3]);
int32_t offset = arguments[4];
int32_t length = arguments[5];
if (length == 0) return 0;
System::Region* region = reinterpret_cast<System::Region*>(peer);
if (length > static_cast<jint>(region->length()) - position) {
length = static_cast<jint>(region->length()) - position;
}
if (length <= 0) {
return -1;
} else {
memcpy(&byteArrayBody(t, buffer, offset), region->start() + position,
length);
return length;
}
}
> Thanks again for your time and dedication, this is outstanding.
No problem; I'm excited to see Avian being used this way.
>> I guess what I'm looking for is a realistic benchmark that I can profile.
>> Obviously, calling GetBooleanField in a tight loop will make it prominent in a profile,
>> but that can be addressed by just making one call before entering the loop.
>> I'd like to see a realistic design where Get*Field is a bottleneck such that it can't be easily
>> redesigned in such a way that said method is no longer a bottleneck.
>> I'm not claiming such a case is unrealistic, but I'm having a hard time imagining it.
> Game logic/render updates are a really tight loop. A small example;
> before rendering an object
> we have to vist it's prerender method and check wheter it's
> transformation matrix needs to be updated
> because a value that's related to the transformation changed. Now with
> the scripting in place we have
> to check if the transformation update was initiated through java, if
> yes, the we have to pull
> the changed values from java and push it into our object, if not, we
> have to push the changes
> into java. These things are already deferred, so they happen at a
> maximum only once per frame per visible object.
> The number I posted were not the result of a simple for(…) they are
> the result of rendering a scene
> with 1000 objects visible.
Thanks, that helps me understand the problem better. Now, if you're
willing and able to provide a concrete example of this workload which I
can build and profile, I'll be happy to help optimize it. What I'm trying
to avoid is micro-optimization without measurement and feedback from a
realistic test case. If I try to write such a test case based on what
you've told me, the result might not emulate your workload, whereas you
know how to get it right the first time. No pressure, but it would make
it much easier for me to provide useful advice and improvements.
>> That might crash if another thread, during GC, deallocates the memory previously allocated for that object while you're trying to dereference it.
>> Adding "ENTER(t, Thread::ActiveState);" right above the dereference would fix that and shouldn't hurt performance much.
>> It still won't be correct for volatile fields, but it won't crash.
> Locking the threadstate to avoid GC'ing on every field access is not
> the proper solution for us I guess.
> I think locking the thread state when leaving the VM is probably
> better, so we can perform "Get**FieldUnsafe" method invocations and we
> only have to do it once. Locking the thread state with ENTER(t,
> Thread::ActiveState); on every field access seems to introduce to
> much performance overhead in situations where we're sure we're the
> only one accessing the vm. Maybe it's even better to fully halt the
> entire VM and all it's threads. How can all threads be stopped right
> now? Is there a facility to do this? Or should we just lock the
> Thread::ActiveState when we're done with the VM code and release the
> ENTER(t, Thread::ActiveState); resource when we're about to enter java
> code again?
Yes, "ENTER(t, Thread::ExclusiveState)" will ensure you're the only
VM-attached thread running, but that's expensive and unecessary for your
case. As long as you're in Thread::ActiveState, you can be sure no other
thread will GC.
Anyway, if you use the Avian calling convention, you'll already be in
ActiveState, so you get that for free. That's one of the nice things
about the convention - no state transitions at all when crossing the
native/java boundary, whereas the JNI convention requires the
conservative assumption that the native call will block, which means we
have to transtion to IdleState for the call and then back to ActiveState
before returning to Java.
>>> … could we store a pointer to the reference obtained through the following call?
>>> fieldOffset(t, getField(t, field)))
>> Yes, that works if the GC never moves objects.
> Do fieldOffsets change? then what I posted above would not work. After
> looking at the code I though they're just a constant defining a
> relative value depending on the layout of the class? How could: "
> return cast<jboolean>(*o, field); " have worked then if the GC moves
> field offsets around?
No, the field offsets never change for a given class. In fact, we
calculate them ahead of time for classes the VM uses internally, so
they're compile-time constants. Thus, a call to e.g. vm::stringData(t,
myString) is no more expensive after inlining and optimization than a C
struct field dereference through a pointer, e.g. myString->data. If you
need every last bit of performance, you may be able to use the type
generator (type-generator.cpp) to generate such inlineable functions for
your application, too.
>
>> Filling a large array and passing it to the VM will be faster currently, and even if we optimize Set*Field as much as we can,
>> I think the large array strategy will still be faster.
> Even if we just add then dereference pointers? Isn't that nearly as
> fast as it can get in such cases? After all to alloc and pass the
> array we have
> to invoke quite a lot JNI calls.
> Keep in mind that we might have busy scenes where thousands of objects
> want their state transform updated.
>
> NewFloatArray(…) // create array
> SetFloatArrayRegion(…) // fill in change set
> NewObjectArray(…) // create object array containing objects that
> changed,
> for each object
> SetObjectArrayElement() // fill in object that contain changes
> InvokeMethodVM(commit changes)
>
> And once the java vm is in the commit changes method, it still has to
> pull out each object and commit the change set to the java object.
> Basically it does the same thing as we could have done directly from
> native code, assigning the values to the fields.
I see now. Yes, you're right; doing it directly in native code should be
faster.
> Joel,
>
> we're seeing crashes when using the boot image on the workstation and
> on the device when calling from java to native and back to java. It
> doesn't happen every time and it's hard to trace.
Can you post a backtrace from GDB or LLDB?
> Everything works fine in compiled non-bootimage builds and with the
> interpreter.
> I also noticed that restarting the VM (killing and creating a new
> instance) is impossible when using a boot image.
It's not impossible. We're doing it in an app I'm helping to develop.
However, you need to make sure you call DestroyJavaVM before creating the
new instance. If any other non-daemon threads are still running when
DestroyJavaVM is called, that call will block until they exit.
Daemon threads are trickier. The VM won't wait from them to exit before
returning from a call to DestroyJavaVM, but we can't completely clean up a
VM instance until every attached thread, daemon or otherwise, has exited
or detached. Furthermore, we cannot safely force daemon threads to exit
in general, since they could be blocked in native code which will
eventually try to return control to the VM. The upshot is that the VM may
not actually be destroyed as soon as DestroyJavaVM returns if there are
still daemon threads running. I don't know how other VMs handle this
Ideally, the above wouldn't matter much, because we ought to be able to
support multiple VM instances concurrently active in the same VM and even
allow a given thread to attach to multiple instances simultaneously.
Indeed, Avian is highly reentrant in all but two or three places, which
are:
1. Signal handling (for e.g. NullPointerExceptions and
ArithmeticExceptions): multiple instances would need to cooperate so
that we could figure out which one a given signal should be delivered
to. The current code uses a global pointer to the current instance,
but we could turn that into an array and make the signal handler
offer the signal to each VM instance in the array until one of them
accepts it.
2. Sharing a heap image: currently, the heap image is mutable so we can
change relative pointers to absolute ones during VM initialization
and then later mark references for visiting during GC. If multiple
instances tried to do those things simultaneously, chaos would ensue.
We could fix this by storing a compressed version of the heap image
in the binary and letting each VM instance decompress it into its own
private area of memory.
3. (OpenJDK port only) Some of the JVM_* functions used by the OpenJDK
class library to call into the VM do not take JNIEnv* or JavaVM*
parameters, so we have to use global or thread-local variables to
figure out which instance they are intended for. Very annoying, and
I'm not sure how we could work around this.
Anyway, the bottom line is you currently can't have more than one
VM instance alive at a time, and you must make sure all your threads have
exited or detached before you call DestroyJavaVM to ensure that the
instance is really dead.
> Ideally, the above wouldn't matter much, because we ought to be able to
> support multiple VM instances concurrently active in the same VM and even
Sorry, that should read "active in the same process".
>> Thanks, that helps me understand the problem better. Now, if you're
>> willing and able to provide a concrete example of this workload which I
>> can build and profile, I'll be happy to help optimize it. What I'm trying
>> to avoid is micro-optimization without measurement and feedback from a
>> realistic test case. If I try to write such a test case based on what
>> you've told me, the result might not emulate your workload, whereas you
>> know how to get it right the first time. No pressure, but it would make
>> it much easier for me to provide useful advice and improvements.
> Unfortunately I can't provide you with our source code, but I could
> probably
> send you a binary build of the engine. Maybe you can inject/hook the c
> api creation
> of our built-in vm and simply inject a call to create your vm with
> code you can debug/profile.
> This engine branch currently builds on OSX, but I can probably setup a
> win32 build for you.
Anything but Windows! :) Linux or OSX would be fine, but what I'm really
looking for is the source for a representative test case, something that
does the same things the performance-sensitive path of your engine does.
I don't need access to the engine itself, just something that behaves like
it well enough to profile and optimize.
>
>> Yes, "ENTER(t, Thread::ExclusiveState)" will ensure you're the only
>> VM-attached thread running, but that's expensive and unnecessary for your
>> case.
> That's good, I can't stress how important it is that we've got full
> control
> of the VM and it's threading. Is there another way to really halt all
> vm threads
> i.e. if the user creates a Thread instance in a java class, that
> thread should also be halted.
Remember that these are native threads which may be executing either
native or Java code at any given instant. Such threads may hold arbitrary
resources like open file descriptors, mutexes, sockets, etc., all of which
would be leaked or unreleased if we just killed the threads outright, and
I wouldn't even know how to do that with pthreads. That's why
Thread.stop() is deprecated -- there's no safe way to implement it.
That means you really need your application threads to be well behaved
such that they respond to a request for an orderly shutdown, and you need
to block until they've all finished responding to the request.
> See below, I already noticed the VM spawns threads on it's own.
Yes, but you have some control over that, as I will explain.
>> No, the field offsets never change for a given class. In fact, we
>> calculate them ahead of time for classes the VM uses internally, so
>> they're compile-time constants. Thus, a call to e.g. vm::stringData(t,
>> myString) is no more expensive after inlining and optimization than a C
>> struct field dereference through a pointer, e.g. myString->data. If you
>> need every last bit of performance, you may be able to use the type
>> generator (type-generator.cpp) to generate such inlineable functions for
>> your application, too.
> That's good to know, that opens up a lot of room for performance
> improvements!
> Another thing that would be great if it's possible to do the same for
> java code in the vm, i.e.
> direct access to compile-time pre-mapped fields in native C structs.
There are two ways to do that in Java: Direct*Buffers and sun.misc.Unsafe.
Normally the latter is only available to privileged standard library code,
but Avian won't prevent application code from accessing it. Anyway, the
Unsafe APIs will be most convenient for what you're doing, and they'll
also be easier to implement in Avian than the Direct*Buffer stuff.
Implementing it efficiently means teaching the compiler to recognize e.g.
Unsafe.getBoolean and emit an intrinsic instead of a native call for it.
We already use intrinsics for e.g. Math.sqrt, which the compiler can
simplify to a single instruction if the processor architecture supports
it. All the infrastructure is in place, so adding the Unsafe methods
should be pretty easy.
>>> we're seeing crashes when using the boot image on the workstation and
>>> on the device when calling from java to native and back to java. It
>>> doesn't happen every time and it's hard to trace.
>> Can you post a backtrace from GDB or LLDB?
> Unfortunately not a useful one, the engine dies hard and gdb returns
> "can't unwind past frame"
> However I've already traced it manually, the last thing the engine
> does is call the native alloc method in the constructor of a java
> object.
> The VM seems to not be able to return back into the VM. Again, this
> only happens with the boot image!
Is it getting a SIGBUS or SIGSEGV, or something else?
That would indicate that it's receiving a signal while executing Java
code, since you're inside the code image. The only signals Java code
should ever generate are SIGBUS or SIGSEGV, which are turned into
NullPoitnerExceptions by the VM signal handler, or SIGFPE, which is
turned into an ArithmeticException.
Of course, when running in a debugger, the debugger will intercept the
signal before the VM has a chance to process it. In GDB, you can usually
suppress that by issuing e.g. "handle SIGSEGV nostop noprint pass", but in
my experience that doesn't work for all signal types in Apple's version of
GDB, so I have to resort to either detaching the debugger when it gets
such a signal or not even running it in the debugger in the first place.
> #1 0x00000021 in ?? ()
> <<<
>
> All other threads contain the same bt:
>>>>
> (gdb) bt
> #0 0x93c4b02e in __workq_kernreturn ()
> #1 0x9865accf in _pthread_wqthread ()
> #2 0x9865c6fe in start_wqthread ()
> <<<
>
> This is the corresponding java code:
>>>>
> public static void Initialize()
> {
> Console.WriteLine("App initialized");
>
> _main = new Node();
>
> Console.WriteLine("main node allocated");
>
> // create a 3d perspective camera node
> // the CameraNode origin + rotation define the origin and rotation
> of
> // the camera
> _scene = new CameraNode();
> � crash
My first guess is that NativeAlloc is somehow clobbering memory such that
the "this" reference or _transformation reference becomes corrupted. It
might help to disassemble the code surrounding the address where it
receives the signal and try to match that up with the original Java
bytecode. I'd be happy to help if you can provide a test case, but I
don't know if that's practical for you.
>
>>> I also noticed that restarting the VM (killing and creating a new
>>> instance) is impossible when using a boot image.
>> It's not impossible. We're doing it in an app I'm helping to develop.
>> However, you need to make sure you call DestroyJavaVM before creating the
>> new instance. If any other non-daemon threads are still running when
>> DestroyJavaVM is called, that call will block until they exit.
> That's strange even when I kill the instance and let it sleep for a
> few seconds
> and restart it, it still crashes when creating a new instance. However
> I can't test it
> right now as the boot image dies during initialization. And we're not
> spawning any threads
> in the VM. This also happens only with the boot image! My first
> thought was that you
> overwrite the contents of the writeable part of the boot image and
> thus it can't boot again.
Yes, that was originally a problem, but now (as of a few months ago)
there's code to ensure that the VM instance leaves the boot image in a
known good state before exiting so that the next instance can use it. If
you're not spawning any threads, I can't guess why it's not working. As
always, if you can provide a test case, I'll look into it.
>
>> Anyway, the bottom line is you currently can't have more than one
>> VM instance alive at a time, and you must make sure all your threads have
>> exited or detached before you call DestroyJavaVM to ensure that the
>> instance is really dead.
> One instance is enough :) As long as we can shut it down and restart
> it gracefully.
> I also noticed that VM does not seem to clean up all objects properly,
> that would be important
> to us, as we need to clean up all native instances. Basically all
> objects need to be able to finalize
> even if the engine does not clean up manually.
> But, right now, we're cleaning up manually, but the garbage collector
> does not properly clean up it seems.
> Even with a manual invocation of System.gc() I can't seem to get all
> objects to shut down properly.
What do you mean by getting "objects to shut down"? Do you mean all the
objects with non-trivial finalize methods are finalized? That happens in
the turnOffTheLights function in machine.cpp, but that will only run when
the last thread in the VM exits, and based on the problem you're having
above it sounds like that's not happening for some reason. Of course, if
a finalize method itself blocks forever, the VM will be unable to finish
cleaning up.
> <snip>
>
> Unfortunately I'm also able to crash the VM in interpreted mode. But
> this time I've got a proper backtrace.
> And I noticed that the VM does spawn it's own threads to perform gc/
> deallocation, this is something
> that should not happen.
No threads are spawned to perform GC. The only thread the VM ever spawns
is the finalize thread, and that's because the Java spec guarantees that
finalization happens in a non-application thread. In Avian, that thread
is only created lazily the first time an object with a non-trivial
finalize method is allocated. The Avian class library does not use
finalize anywhere, so only application code can trigger it. The upshot is
that if you don't want that thread to be spawned, you should not use
finalize; use phantom references instead.
I should have asked this earlier: are you using Avian's class library or
OpenJDK's?
> Even though we can handle these situations
> gracefully as we retain objects we still need, this still
> should not happen. We really need to be in full control over any
> threading that happens.
We could add a system property, e.g. avian.no.finalize.thread, which tells
the VM not to spawn a finalize thread even if objects are allocated which
would otherwise demand it, but then those objects simply won't be
finalized when they become unreachable.
> Thread 1 is obviously the main thread, I've added annotations and you
> can see how the engine calls into the VM
> and the VM calls back into the native code.
>
>
> THREAD 1
> #8 �
> �
> #51 0x000034b4 in main (argc=3, argv=0xbffff9bc) at main.m:12
>
>
> THREAD 7
> #0 0x93c4a9c6 in __pthread_kill ()
> #1 0x9865af78 in pthread_kill ()
> #2 0x9864bbdd in abort ()
> #3 0x001cdfc5 in abort (this=0x24a4740) at posix.cpp:893
> #4 0x00197f2c in vm::abort (s=0x24a4740) at system.h:170
> #5 0x001980fd in vm::abort (t=0xab63004) at machine.h:1789
> #6 0x001a0a83 in vm::enter (t=0xab63004, s=vm::Thread::ActiveState)
> at machine.cpp:2874
> #7 0x001979e1 in vm::StateResource::StateResource (this=0xb01019f0,
> t=0xab63004, state=vm::Thread::ActiveState) at machine.h:1662
> #8 0x00189d8c in DeleteGlobalRef (t=0xab63004, r=0xdc0094) at
> jnienv.cpp:2207
My line numbers are not matching up with what I'm seeing in this trace.
For example, jnienv.cpp:2207 is in NewWeakGlobalRef, not DeleteGlobalRef,
when I look at the latest code from the master branch of avian.git. Are
you using the latest code? Have you patched it? I'd like to help here,
but I'm having trouble following along due to the line number mismatch.
>> THREAD 7
>> #0 0x93c4a9c6 in __pthread_kill ()
>> #1 0x9865af78 in pthread_kill ()
>> #2 0x9864bbdd in abort ()
>> #3 0x001cdfc5 in abort (this=0x24a4740) at posix.cpp:893
>> #4 0x00197f2c in vm::abort (s=0x24a4740) at system.h:170
>> #5 0x001980fd in vm::abort (t=0xab63004) at machine.h:1789
>> #6 0x001a0a83 in vm::enter (t=0xab63004, s=vm::Thread::ActiveState)
>> at machine.cpp:2874
>> #7 0x001979e1 in vm::StateResource::StateResource (this=0xb01019f0,
>> t=0xab63004, state=vm::Thread::ActiveState) at machine.h:1662
>> #8 0x00189d8c in DeleteGlobalRef (t=0xab63004, r=0xdc0094) at
>> jnienv.cpp:2207
>
> My line numbers are not matching up with what I'm seeing in this trace. For
> example, jnienv.cpp:2207 is in NewWeakGlobalRef, not DeleteGlobalRef, when I
> look at the latest code from the master branch of avian.git. Are you using
> the latest code? Have you patched it? I'd like to help here, but I'm having
> trouble following along due to the line number mismatch.
By the way, you aren't by any chance storing a JNIEnv* in ScriptObject are
you? That would explain the crash. JNIEnv* is thread-specific, so if you
try to use in from different thread (the finalize thread, in this case),
bad things will happen. You need to always either use the JNIEnv* passed
to the latest native method call in the current thread or use
JavaVM::GetEnv to get it.
> Just looked at GetEnv:
> *t = static_cast<Thread*>(m->localThread->get());
> That's embarrassing, I should have looked at what function I'm
> calling :)
>
> Am I right in the assumption that a thread local environment is not
> necessary for all fields? For instance GetField* SetField* lookups can
> work with the
> environment created when the VM was created.
No, because those methods still need to do a thread state transtion from
idle to active and back again. You'll get random crashes depending on
what the thread that really owns that JNIEnv* happens to be doing at the
time.
If you're trying to avoid a thread local lookup, just pass the JNIEnv*
straight through from your native method to whatever functions need it.
> I'm still changing the code to always grab the thread local
> environment, I'll let you know as soon as I have tested it with the
> fix.
>
> But we still need to prevent the finalize thread, we can call it
> manually at the end of the frame, or if we've got frame time overhead
> (i.e. the computer renders faster than 60fps)
That's why phantom references exist -- you get control over when resources
owned by collected objects are released. Here are your options in order
of most control to least control:
1. Explicitly release native resources associated with a given object
when you're done using it (which means before it becomes unreachable,
of course). That's what SWT and many other successful libraries do.
I strongly recommend it.
2. Use phantom references to release native resources after the object
which own them become unreachable. The drawback here, and with option
3 below, is that when and why GC occurs is non-deterministic from the
point of view of the application, and the amount of native resources
in use is not and cannot be considered by the VM when it decides
whether or not to GC. All it sees is Java heap memory pressure; it
knows nothing about natively-allocated memory or other limited OS
resources attached to Java objects, so it may not GC often enough to
free them up.
3. Use Object.finalize(). This sucks. I consider it to be the absolute
worst "feature" of Java.
>
> On Mar 3, 5:56�pm, Daniel Jaeger <dwckongreg...@googlemail.com> wrote:
>>> By the way, you aren't by any chance storing a JNIEnv* in ScriptObject are you? �That would explain the crash.
>>> JNIEnv* is thread-specific, so if you try to use in from different thread (the finalize thread, in this case), bad > things will happen.
>>> You need to always either use the JNIEnv* passed to the latest native method call in the current thread or use JavaVM::GetEnv to get it.
>>
>> Great observation! Yes indeed, the JNIEnv* is stored when the vm-
>> instance is allocated. Albeit not in the ScriptObject but in the
>> Scripting:: class. It's basically
>> the wrapper for the JNIEnv the engine uses.
>> I'll see if this fixes the crashes and revert back to you, I'll also
>> reply to your other post in a minute.
>
Based on the code you posted earlier, this looks like a simple null
pointer dereference. I'd need to see the disassembly, but I'm guessing
we're seeing address=0x4 because we're trying to do a bounds check on an
array (the length of the array is at a four byte offset from the object
start on a 32-bit platform), but the array reference is null. If you ran
this outside a debugger, I'm guessing you'd get a NullPointerException.
I recommend adding "if (_transformation == null) throw new
NullPointerException();" before the array dereference and, and then work
backwards, moving it earlier and earlier in the code to find out when it's
becoming null.
> You were right again :)
>
> call to super (NodeBase())
> resolved=Java_drahtkern_Game_CameraNode_NativeAlloc at=0x1762a0
> native alloc done
> resolved=Avian_java_lang_Throwable_trace at=0x17abb0
> java/lang/NullPointerException: _transformation == null (post native
> call)
> at drahtkern/Game/CameraNode.<init> (line 37)
> at AppBase.Initialize (line 39)
>
> Right after the native call the _transformation array becomes NULL.
> It's important to note that the NativeAlloc method still contains an
> empty body, so it's just a simple invocation that does nothing.
The original code you sent looked like this:
_peer = NativeAlloc();
System.out.println("native alloc done");
_transformation[3] = -90;
What happens if you change that first line to "/*_peer =*/
NativeAlloc();"? What happens when you change it to "_peer = 0;"? I'm
wondering if writing to the _peer reference is somehow clobbering
the _transformation reference. It could be that there's a bug in field
offset calculation in the boot image generator that would cause that.
As always, if you can reduce this to a test case that I can run myself, it
will speed the process up considerably. Although we're clearly having
good luck so far with my random guessing and debugging-by-proxy :)
> I guess that was too fast, just happened in compiled mode. Restarted
> the same build and floats are working again.
> Is it possible that an attached debugger can cause this?
I wouldn't think so. When you see you're passing floats to the VM, do you
mean as arguments to Call*Method, SetFloatField, SetFloatArrayRegion, or
something else?
It does sound similar to the bug we fixed with float argument passing the
other day, but I don't know why it would now sometimes work and sometimes
not. Is it possible you have an old build cached somewhere that's being
used when debugging but not when running outside the debugger? When I do
iOS development, I'm in the habit of clearing out
~/Library/Developer/Xcode/DerivedData/* and ~/Library/Application
Support/iPhone Simulator/*/Applications/* before every build to avoid such
confusion.
>> What happens when you change it to "_peer = 0;"?
>> I'm wondering if writing to the _peer reference is somehow clobbering the _transformation reference.
>> It could be that there's a bug in field offset calculation in the boot image generator that would cause that.
>
> You were right again :)! - I wouldn't have thought that a simple
> assignment can mess up the class tables.
Please try pulling the latest from the Git repo and let me know how it
works.
http://oss.readytalk.com/gitweb?p=avian.git;a=commitdiff;h=ac63d084505cd14cbb2828a3fd93207d6a7eaf1b
>
>> I wouldn't think so. When you see you're passing floats to the VM,
>> do you mean as arguments to Call*Method, SetFloatField, SetFloatArrayRegion, or something else?
>
> I'm using CallVoidMethod(...)
> Haven't seen any issues with the SetField/ArrayRegion methods.
>
>> It does sound similar to the bug we fixed with float argument passing the other day, but I don't know why it would now sometimes work and sometimes
>> not. Is it possible you have an old build cached somewhere that's being used when debugging but not when running outside the debugger? When I do iOS
>> development, I'm in the habit of clearing out ~/Library/Developer/Xcode/DerivedData/* and ~/Library/Application Support/iPhone Simulator/*
>> /Applications/* before every build to avoid such confusion.
>
> Currently avian is being built with an Xcode project that's a direct
> dependency of the engine, so the avian lib will always be rebuilt
> whenever we rebuild the project. We've got a lot of these dependencies
> in the engine and we've never had problems with outdated libraries
> being linked in.
> However, I have ensured that I have not messed up avian with my custom
> build setup and modified image generator (added armv7, supports
> baseclasspath/jar + additional classpath and directly generates .o
> files, supports armv6+armv7 fat/universal binaries).
> This kind of setup is mandatory for the project to keep the deployment
> process simple. Building for release will automatically rebuild avian,
> build a universal bootimage and link it in. Works great.
That sounds reasonable. I'm afraid I don't have any more guesses about
the float argument passing problem. I can't imagine what would cause it
to work most of the time but occasionally stop working. I presume you've
fixed up all the incorrect uses of JNIEnv*, right?
> Here goes the reply to your other post:
>
>> Anything but Windows! :) Linux or OSX would be fine, but what I'm really looking for is the source for a representative test case,
>> something that does the same things the performance-sensitive path of your engine does.
>> I don't need access to the engine itself, just something that behaves like it well enough to profile and optimize.
> Building a representative test case without the engine will be really
> difficult I guess.
> But if you can work with the engine I've reassured that it's ok to
> send you a stripped down build.
> Anyways, for the time being I've setup that test case I've mentioned
> earlier.
Is that test case for debugging the nulled-out _transformation field, the
float argument passing problem, or for performance profiling? Or all
three?
Anyway, I haven't actually tried running it yet, since I don't have access
to a Mac right now.
>
>> Remember that these are native threads which may be executing either native or Java code at any given instant. Such threads may
>> hold arbitrary resources like open file descriptors, mutexes, sockets, etc., all of which would be leaked or unreleased if we
>> just killed the threads outright, and I wouldn't even know how to do that with pthreads. That's why Thread.stop() is deprecated
>> -- there's no safe way to implement it.
>> That means you really need your application threads to be well behaved such that they respond to a request for an orderly
>> shutdown, and you need to block until they've all finished responding to the request.
>
> No I wasn't talking about killing threads, my idea was to halt them -
> probably using a wait condition: http://linux.die.net/man/3/pthread_cond_wait
> But I'm not sure if that's even possible as I don't know how java
> threads are being handle in the vm.
They're just native pthreads (or Windows threads), with all the advantages
and limitations that implies. Anything you can do with a pthread, you can
do with a thread attached to the VM, basically.
I've lost track of what you're trying to accomplish here. I thought this
came up because you wanted to be able to access Java fields from native
code without worrying about a GC being initiated from another thread and
moving the object out from under you. If that's all you're worried about,
there's no need to pause all the other threads, just enter
Thread::ActiveState if you haven't already. Is the some other reason you
need all the other threads paused?
>> No threads are spawned to perform GC. The only thread the VM ever spawns is the finalize thread, and that's because the Java
>> spec guarantees that finalization happens in a non-application thread. In Avian, that thread is only created lazily the first
>> time an object with a non-trivial finalize method is allocated. The Avian class library does not use finalize anywhere, so only
>> application code can trigger it. The upshot is that if you don't want that thread to be spawned, you should not use finalize;
>> use phantom references instead.
> We don't mind breaking the Java spec as long as it suits our needs. So
> we can move the finalize thread onto the main thread and invoke
> finalize whenever we want to.
My point is that there's already an official, well-defined way to do this
in Java: phantom references. No need to hack the VM, and your application
code remains portable across VMs.
>> That sounds reasonable. I'm afraid I don't have any more guesses about
>> the float argument passing problem. I can't imagine what would cause it
>> to work most of the time but occasionally stop working. I presume you've
>> fixed up all the incorrect uses of JNIEnv*, right?
> Yes, the only way the engine can access the JNIEnv* is either by pass-
> through
> or by invoking MakeEnvironment() which grabs the current local thread.
> I already mentioned it, maybe it's related to the Jikes compiler which
> is
> built into the engine to dynamically recompile classes. I'm going to
> disable
> it and switch to javac to check if I can produce the error again.
Yeah, I'll admit I've never tested dynamic class redefinition, so there
may be bugs with that.
>> I've lost track of what you're trying to accomplish here. I thought this
>> came up because you wanted to be able to access Java fields from native
>> code without worrying about a GC being initiated from another thread and
>> moving the object out from under you. If that's all you're worried about,
>> there's no need to pause all the other threads, just enter
>> Thread::ActiveState if you haven't already. Is the some other reason you
>> need all the other threads paused?
> The engine is not thread safe for performance reasons so it would be
> great to be in control over what happens for various reasons.
The normal way to protect a non-thread-safe resource would be to protect
it with a mutex (or monitor in Java terms). Acquiring a mutex will be a
lot less expensive performance-wise than any attempt to pause all other
threads.
>
>> My point is that there's already an official, well-defined way to do this
>> in Java: phantom references. No need to hack the VM, and your application
>> code remains portable across VMs.
> That is true, but our code will not be easily portable anyway as there
> will be heavy
> use of the Avian_ calling convention.
I was assuming you'd implement two versions of each performance-sensitive
native method: a VM-independent JNI one and an Avian-specific one. Even
if you are committed to using Avian exclusively, though, I think you'll be
better off using an unmodified version rather than maintain a fork with
your customizations. The more your version diverges, the less help I'll
be able to provide, and the more difficult it will be for you to merge
upstream fixes and features.
> While I know that we can close our objects early on by calling a close
> method similar
> to SWT I want to ensure that it's impossible to break the engine on
> the native
> side through java. That basically means disabling multi threading.
Or mutual exclusion. If you really want to disable multithreading,
though, you can patch startThread to e.g. throw an
UnsupportedOperationException.
That looks reasonable for 32-bit values. FWIW, I went ahead and
implemented the relevant sun.misc.Unsafe methods, which cover the 8, 16,
32, and 64-bit cases:
http://oss.readytalk.com/gitweb?p=avian.git;a=commitdiff;h=e8e3c9066feca6e50d28870292522435d5869c99
When using such methods to access fields in native structures, you'll need
to take into account the details of the ABI you're building for, including
the sizes of types like bool and padding dictated by alignment
constraints. Thus, on one platform you might use
boolean value = unsafe.getByte(myStruct + 43) != 0;
to access a given bool field, whereas on another platform you might use
boolean value = unsafe.getInt(myStruct + 48) != 0;
for the same field because the size of the type is different and has
different alignment requirements, and the fields before it in the struct
might have different sizes and alignments. I definitely recommend against
using e.g. getInt to access a single byte, since it exposes you to
endianness issues and may trigger segfaults (due to reading past the end
of a structure) and bus errors (due to misaligned access).
SWT uses native calls to access the standard C sizeof and offsetof macros to
determine such information at runtime. You could also calculate them at
build time and generate a Java file with named constants to reference in
your code.
> Ah, there's a misunderstanding. We're actually stopping the VM,
> recompiling, restarting the VM while the engine is running.
> But doing this while the VM is alive would be absolutely great!
It should work, especially if you create a new classloader for each new
set of classes.
>> I was assuming you'd implement two versions of each performance-sensitive native method: a VM-independent JNI one and an Avian-
>> specific one. Even if you are committed to using Avian exclusively, though, I think you'll be better off using an unmodified
>> version rather than maintain a fork with your customizations. The more your version diverges, the less help I'll be able to
>> provide, and the more difficult it will be for you to merge upstream fixes and features.
> Yes, however it will be difficult as we'll have to implement more
> unsafe methods for field access etc.
If you're using the Avian calling convention, you'll have direct access to
object references and fields. You shouldn't need to add any new methods.
If you can't find what you need, let me know.
>> That looks reasonable for 32-bit values. FWIW, I went ahead and implemented the relevant sun.misc.Unsafe methods,
>> which cover the 8, 16, 32, and 64-bit cases:
> That is great. I'll try it out as soon as possible. However I dislike
> the fact that the sun.misc.Unsafe methods are not static and require
> the allocation of a proxy object. Isn't there a cost involved in
> passing the "this" pointer around on the stack for each get*
> invocation?
No, the compiler will ignore a value that's pushed onto the stack but
never used, and since the get/put methods are intrinsics, there's no
method invocation at all; just a straight register-to-memory or
memory-to-register move.
> Or will this be omitted by the compiler because the object has been
> directly popped off?
Yes. The value isn't needed, so it's never actually loaded, pushed, or
popped in the generated code.
Hi there,
i contacted Joel in private a few days ago and of course he prefers to
have a public conversation about the given topic. Sorry Joel, i should
have known better. I give the exact same answer to people that bug me
via e-mail about the framework :/
I'll cut and paste our convo in in-line style and end with a few
follow up comments.
[ME]
my name is Mario Zechner, i'm the creator of libgdx [1], a Java/C/C++
based cross-platform game development framework for the desktop and
Android. I've already infiltrated the Avian VM Google group. I hope
you don't mind me writting you directly. I'll try to keep it short
(but will most likely fail, sorry :/).
[JOEL]
I don't mind you writing me directly, but I do prefer to answer
questions about Avian on the Google group so others can benefit. That
way, I don't have to repeat myself if others have the same questions,
and the archives are available to anyone searching the Web for Avian-
related information.
I'm sure you're not the only one interested in using Avian on iOS.
[CUT SOMETHING HERE AS I DON'T KNOW IF ITS CONFIDENTIAL] So, I would
prefer that any further discussion happen on the group. Plus, I know
that there are some Apple experts subscribed to it who may be able to
help you out better than I can.
[ME]
First of all i have to say that i was highly impressed with what you
did with Avian. It's a very impressive and well executed project,
exactly the kind of lightweight yet capable JVM i've been looking for.
What i'm interested in the most is the AOT capabilities of Avian.
Cutting a long story short: i want to AOT compile Java bytecode to
Mach-o to be able to (statically) link it against iOS binaries. This
means i need the following:
- AOT compilation of byte code to a Mach-o container for ARMv6
(preferring the later of course), using the iOS ABI [2] (which seems
to be standard). binaryToObject seems to support that for a couple of
platforms, but not for ARM.
[JOEL]
Adding ARM support to mach-o.cpp should not be difficult. If you look
at that file, you'll see that most of the code is architecture-
agnostic.
The more challenging part might be porting arm.S, arm.h, and arm.cpp,
which currently support Linux only. I haven't looked at how the iOS
ABI compares to the ABI we use for Linux. I know the Linux and OS X
PowerPC ABIs are different enough that it was non-trivial to add
support for the former after we already had the latter working.
[ME]
- a runtime environment that allows me to execute the AOT compiled
bytecode (gc, locks & threading, a minimal runtime lib etc., basically
anything a JVM would provide for JIT compiled/interpreted code
execution). Avian seems to provide just that, in a nice cross-platform
way. Since there's a posix and darwin backend i'd assume only minimal
modifications would be necessary.
[JOEL]
I don't know much about iOS, but a quick look at the Wikipedia page
indicates that it shares its Darwin foundation with OS X and only
really differs at the UI level. In that case, there should be little
or no porting needed beyond the architecture- and ABI-specific parts I
mentioned above.
[ME]
- access to JNI methods, statically linked to the AOT compiled Mach-o
object file. That seems to be possible as well with some JNIEXPORT
magic if i understood things correctly.
[JOEL]
Yes, that should be easy if iOS at least allows dlsym calls to the
current executable. Otherwise, we'll have to get creative (e.g. build
a table of function pointers by hand or via code generation).
[ME]
So, apart from the missing supported vor ARM + Mach-o, everything
seems to be covered. Here are a few things i'm worried about:
- iOS is very restrictive in terms of dynamic loading, read: it
doesn't allow shared libs nor does it allow setting memory to be
executeable. With a bootimage build, would i need the later? It seems
to me that the AOT compiled code's symbols are directly exported
through the container file formats like ELF or Mach-o, i might be
wrong though. My knowledge of these things is very rusty so i couldn't
make 100% sense of the code i checked.
[JOEL]
If everything's in the bootimage, and iOS respects the readable/
writable/executable flags on the Mach-O segment in which the bootimage
resides, it should work fine. As I said above, though, if it doesn't
even allow dlsym calls to the current executable, we'll have to do
something special to make JNI work.
[ME]
- As oposed to other projects like Mono et. al you seem to have your
own GC instead of the Boehm-Demers-Weiser GC that's commonly used. Is
there any platform specific trickery involved that would prevent the
GC from working on iOS?
[JOEL]
There's no trickery there, so I would not expect any problems. The
Boehm GC must use all kinds of platform-specific trickery because it
needs to search for root references anywhere they might exist with
little or no help from the application or VM. That means it must find
the extent of each thread's stack, know how to force the contents all
relevant registers to memory, etc. Avian's GC expects the VM to tell
it where all the root references are, and the VM is designed such that
those references are all tracked explicitly; there's no need to search
for them.
[ME]
- What ABI do you target with the ARM backend?
[JOEL]
I'm not really sure what it's even called :) It's whatever modern
Linux-on-ARM systems use. Some call it the GNU EABI, but I take it
that's not the official name. Here are some links to the relevant
specifications:
http://wiki.debian.org/ArmEabiPort#References
Someone else did most of the work on the ARM port, so I'm not really
an expert. For the parts I contributed, I relied heavily on the
assembly output from GCC to answer questions about the ABI rather than
look at specifications.
[ME]
- Is there support for VFP or do you use softfp?
[JOEL]
The ARM port of the JIT compiler (which is also used for AOT
compilation) defers to out-of-line C++ helper functions for all
floating point ops. Whether those helper functions use hardware or
software FP depends on the compiler/flags used to build Avian.
[ME]
I'd appreciate any pointers you could throw my way. I've looked at
various alternatives, and the thing that draws me the most to Avian is
it's minimal runtime library as well as it's comparably small
code-base. I'm aware that it might not be production ready yet, but i
think i could cope with that if i was able to get an iOS port going.
[JOEL]
FWIW, we've been deploying Avian-based products at my company for over
a year now, so it's production ready as far as our needs go.
[ME]
I'd love to collaborate, even though you might not want C++ code
contributions from me. My brain's been poisoned with Java over the
last couple of years, and my C++ knowledge (and the little ARM
assembler knowledge i had) have mostly been replaced with hideous EE
framework nightmares. I'd still offer my help were possible, be it in
form of code or testing.
[JOEL]
The lack of comments/documentation in e.g. arm.cpp and the rest of the
VM would probably be the biggest hurdle even if you were a C++ expert.
That's something I hope to improve eventually, but writing
documentation isn't much fun :) Anyway, if you want to get started on
this, I'll be happy to help as time allows. I might start tinkering
with it myself if I can get an iOS dev/test environment set up.
[ME]
Another thing i could provide is a community full of people that would
love to help testing Avian, on iOS but potentially also on other
platforms. If Avian turns out to be stable enough it could be a nice
alternative for people that want their games to run "natively",
without the need for an installed JRE. Most of the games produced by
the community do not demand that much CPU-side performance, so even if
Avian can't reach HotSpot in terms of execution speed, it would still
be more than good enough for 99% of those games.
Finally I'd like to ask what your plans are in terms of potential
commercialization of Avian. I provide virtually all my non-day-job
code under a very permissive OSS licence (Apache 2) so other's can
benefit from it. I generate income by support contracts, workshops and
the like. Getting those gigs is merely a question of reputation. A
successful OSS project can get you that.
[JOEL]
I have no commercial plans. I'm just in it for the technical
challenge and learning opportunities. My day job pays the bills, and
I even get to work on Avian on company time since it's become an
essential piece of infrastructure for us.
[ME]
An iOS port of Avian that is compatible with the Appstore TOS could be
a huge seller of course, so i'd understand if you'd go a non OSS or
potentially (A)GPL road in that case. If you decided to keep the
current licence you'd make a ton of people happy though :)
[JOEL]
We'll definitely stick with the current license.
[ME]
Thanks if you read that far, this thing got longer than intended.
Whatever the outcome with regards to the above things, keep up the
insanely good work. I'll definitely give Avian a try for a couple of
projects of mine on the desktop.
[JOEL]
Thanks for the feedback. libgdx looks like a cool project, and I
agree that Avian would be a good choice to make it run on iOS. I'm
definitely curious to find out how much work the port would require.
FOLLOW-UP
Thanks to Joel for taking the time and answer my lengthy inquiry. Here
are the things that would need to be resolved:
- check ARM version used. ARMv6 would be preferred. Didn't have time
to look into that yet.
- check VFP support. Currently Avian calls out to C methods for all
floating point related functionality. That might actually be a good
thing in case of iOS. Though it incurs a function call as i understood
it, there's an oportunity to go from softfp to VFP and eventually also
have a NEON backend.
- check current ARM ABI against iOS ABI. I had a quick look and there
seem to be a few differences, nothing to worrying though.
- check dlsym availability. I couldn't find hard facts on this matter,
but from the looks of it it is at least possible to load symbols from
statically linked object files on iOS.
- extent mach-o support to ARM.
One thing i thought about: an alternative to outputting object files/
boot images would be to actually output an intermediate
representation, either C or LLVM IL. What i've seen so far that might
actually be possible. This way one could use the LLVM toolchain XCode
uses for iOS to compile the final binary which might make some things
easier. Input on that would be appreciated.
I'm currently moving to SF and will have a bit of time over the next 3
months. I'm not sure i'm up for the task (and i also lack a mac dev
environment) but i think it would be an awesome side-project.
Sorry for the mail-bomb, and thanks Joel for providing us with such an
awesome project!
Hi there,
i contacted Joel in private a few days ago and of course he prefers to
have a public conversation about the given topic. Sorry Joel, i should
have known better. I give the exact same answer to people that bug me
via e-mail about the framework :/
I'll cut and paste our convo in in-line style and end with a few
follow up comments.
[ME]
my name is Mario Zechner, i'm the creator of libgdx [1], a Java/C/C++
based cross-platform game development framework for the desktop and
Android. I've already infiltrated the Avian VM Google group. I hope
you don't mind me writting you directly. I'll try to keep it short
(but will most likely fail, sorry :/).
[JOEL]
I don't mind you writing me directly, but I do prefer to answer
questions about Avian on the Google group so others can benefit. That
way, I don't have to repeat myself if others have the same questions,
and the archives are available to anyone searching the Web for Avian-
related information.
I'm sure you're not the only one interested in using Avian on iOS.
[CUT SOMETHING HERE AS I DON'T KNOW IF ITS CONFIDENTIAL] So, I would
prefer that any further discussion happen on the group. Plus, I know
that there are some Apple experts subscribed to it who may be able to
help you out better than I can.
[ME]
First of all i have to say that i was highly impressed with what you
did with Avian. It's a very impressive and well executed project,
exactly the kind of lightweight yet capable JVM i've been looking for.
What i'm interested in the most is the AOT capabilities of Avian.
Cutting a long story short: i want to AOT compile Java bytecode to
Mach-o to be able to (statically) link it against iOS binaries. This
means i need the following:
- AOT compilation of byte code to a Mach-o container for ARMv6
(preferring the later of course), using the iOS ABI [2] (which seems
to be standard). binaryToObject seems to support that for a couple of
platforms, but not for ARM.
[JOEL]
Adding ARM support to mach-o.cpp should not be difficult. If you look
at that file, you'll see that most of the code is architecture-
agnostic.
The more challenging part might be porting arm.S, arm.h, and arm.cpp,
which currently support Linux only. I haven't looked at how the iOS
ABI compares to the ABI we use for Linux. I know the Linux and OS X
PowerPC ABIs are different enough that it was non-trivial to add
support for the former after we already had the latter working.
[ME]
- a runtime environment that allows me to execute the AOT compiled
bytecode (gc, locks & threading, a minimal runtime lib etc., basically
anything a JVM would provide for JIT compiled/interpreted code
execution). Avian seems to provide just that, in a nice cross-platform
way. Since there's a posix and darwin backend i'd assume only minimal
modifications would be necessary.
[JOEL]
I don't know much about iOS, but a quick look at the Wikipedia page
indicates that it shares its Darwin foundation with OS X and only
really differs at the UI level. In that case, there should be little
or no porting needed beyond the architecture- and ABI-specific parts I
mentioned above.
[ME]
- access to JNI methods, statically linked to the AOT compiled Mach-o
object file. That seems to be possible as well with some JNIEXPORT
magic if i understood things correctly.
[JOEL]
Yes, that should be easy if iOS at least allows dlsym calls to the
current executable. Otherwise, we'll have to get creative (e.g. build
a table of function pointers by hand or via code generation).
[ME]
So, apart from the missing supported vor ARM + Mach-o, everything
seems to be covered. Here are a few things i'm worried about:
- iOS is very restrictive in terms of dynamic loading, read: it
doesn't allow shared libs nor does it allow setting memory to be
executeable. With a bootimage build, would i need the later? It seems
to me that the AOT compiled code's symbols are directly exported
through the container file formats like ELF or Mach-o, i might be
wrong though. My knowledge of these things is very rusty so i couldn't
make 100% sense of the code i checked.
[JOEL]
If everything's in the bootimage, and iOS respects the readable/
writable/executable flags on the Mach-O segment in which the bootimage
resides, it should work fine. As I said above, though, if it doesn't
even allow dlsym calls to the current executable, we'll have to do
something special to make JNI work.
[ME]
- As oposed to other projects like Mono et. al you seem to have your
own GC instead of the Boehm-Demers-Weiser GC that's commonly used. Is
there any platform specific trickery involved that would prevent the
GC from working on iOS?
[JOEL]
There's no trickery there, so I would not expect any problems. The
Boehm GC must use all kinds of platform-specific trickery because it
needs to search for root references anywhere they might exist with
little or no help from the application or VM. That means it must find
the extent of each thread's stack, know how to force the contents all
relevant registers to memory, etc. Avian's GC expects the VM to tell
it where all the root references are, and the VM is designed such that
those references are all tracked explicitly; there's no need to search
for them.
[ME]
- What ABI do you target with the ARM backend?
[JOEL]
I'm not really sure what it's even called :) It's whatever modern
Linux-on-ARM systems use. Some call it the GNU EABI, but I take it
that's not the official name. Here are some links to the relevant
specifications:
http://wiki.debian.org/ArmEabiPort#References
Someone else did most of the work on the ARM port, so I'm not really
an expert. For the parts I contributed, I relied heavily on the
assembly output from GCC to answer questions about the ABI rather than
look at specifications.
[ME]
- Is there support for VFP or do you use softfp?
[JOEL]
The ARM port of the JIT compiler (which is also used for AOT
compilation) defers to out-of-line C++ helper functions for all
floating point ops. Whether those helper functions use hardware or
software FP depends on the compiler/flags used to build Avian.
[ME]
I'd appreciate any pointers you could throw my way. I've looked at
various alternatives, and the thing that draws me the most to Avian is
it's minimal runtime library as well as it's comparably small
code-base. I'm aware that it might not be production ready yet, but i
think i could cope with that if i was able to get an iOS port going.
[JOEL]
FWIW, we've been deploying Avian-based products at my company for over
a year now, so it's production ready as far as our needs go.
[ME]
I'd love to collaborate, even though you might not want C++ code
contributions from me. My brain's been poisoned with Java over the
last couple of years, and my C++ knowledge (and the little ARM
assembler knowledge i had) have mostly been replaced with hideous EE
framework nightmares. I'd still offer my help were possible, be it in
form of code or testing.
[JOEL]
The lack of comments/documentation in e.g. arm.cpp and the rest of the
VM would probably be the biggest hurdle even if you were a C++ expert.
That's something I hope to improve eventually, but writing
documentation isn't much fun :) Anyway, if you want to get started on
this, I'll be happy to help as time allows. I might start tinkering
with it myself if I can get an iOS dev/test environment set up.
[ME]
Another thing i could provide is a community full of people that would
love to help testing Avian, on iOS but potentially also on other
platforms. If Avian turns out to be stable enough it could be a nice
alternative for people that want their games to run "natively",
without the need for an installed JRE. Most of the games produced by
the community do not demand that much CPU-side performance, so even if
Avian can't reach HotSpot in terms of execution speed, it would still
be more than good enough for 99% of those games.
Finally I'd like to ask what your plans are in terms of potential
commercialization of Avian. I provide virtually all my non-day-job
code under a very permissive OSS licence (Apache 2) so other's can
benefit from it. I generate income by support contracts, workshops and
the like. Getting those gigs is merely a question of reputation. A
successful OSS project can get you that.
[JOEL]
I have no commercial plans. I'm just in it for the technical
challenge and learning opportunities. My day job pays the bills, and
I even get to work on Avian on company time since it's become an
essential piece of infrastructure for us.
[ME]
An iOS port of Avian that is compatible with the Appstore TOS could be
a huge seller of course, so i'd understand if you'd go a non OSS or
potentially (A)GPL road in that case. If you decided to keep the
current licence you'd make a ton of people happy though :)
[JOEL]
We'll definitely stick with the current license.
[ME]
Thanks if you read that far, this thing got longer than intended.
Whatever the outcome with regards to the above things, keep up the
insanely good work. I'll definitely give Avian a try for a couple of
projects of mine on the desktop.
[JOEL]
Thanks for the feedback. libgdx looks like a cool project, and I
agree that Avian would be a good choice to make it run on iOS. I'm
definitely curious to find out how much work the port would require.
FOLLOW-UP
Thanks to Joel for taking the time and answer my lengthy inquiry. Here
are the things that would need to be resolved:
- check ARM version used. ARMv6 would be preferred. Didn't have time
to look into that yet.
- check VFP support. Currently Avian calls out to C methods for all
floating point related functionality. That might actually be a good
thing in case of iOS. Though it incurs a function call as i understood
it, there's an oportunity to go from softfp to VFP and eventually also
have a NEON backend.
- check current ARM ABI against iOS ABI. I had a quick look and there
seem to be a few differences, nothing to worrying though.
- check dlsym availability. I couldn't find hard facts on this matter,
but from the looks of it it is at least possible to load symbols from
statically linked object files on iOS.
- extent mach-o support to ARM.
One thing i thought about: an alternative to outputting object files/
boot images would be to actually output an intermediate
representation, either C or LLVM IL. What i've seen so far that might
actually be possible. This way one could use the LLVM toolchain XCode
uses for iOS to compile the final binary which might make some things
easier. Input on that would be appreciated.
I'm currently moving to SF and will have a bit of time over the next 3
months. I'm not sure i'm up for the task (and i also lack a mac dev
environment) but i think it would be an awesome side-project.
Sorry for the mail-bomb, and thanks Joel for providing us with such an
awesome project!
> Hi all,
> I've just started to play with Avian, and I'm interested in the iOS port.
>
> Do you plan to support openjdk for iOS or just the avian classpath? Even if
> you don't plan to do it, do you think that it woud be feasible with
> a�reasonable�amount�of effort?
I haven't tried it yet, but it shouldn't be too hard. A good place to
start would be to modify the makefile
git://oss.readytalk.com/hello-ios.git to accept an openjdk= option. It
might help to look at app.mk in
git://oss.readytalk.com/avian-swt-examples.git for an example of how to do
that. It probably won't work without a few code changes, but they should
be pretty easy.
I'll give it a try when I have some time and access to an iOS device.
On Sun, 11 Mar 2012, Pablo Guerrero wrote:
> Hi all,
> I've just started to play with Avian, and I'm interested in the iOS port.
>
> Do you plan to support openjdk for iOS or just the avian classpath? Even if
> you don't plan to do it, do you think that it woud be feasible with
> a�reasonable�amount�of effort?