2014-04-20 20:14:48 -06:00
|
|
|
/* Copyright (c) 2008-2014, Avian Contributors
|
2008-02-19 11:06:52 -07:00
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software
|
|
|
|
for any purpose with or without fee is hereby granted, provided
|
|
|
|
that the above copyright notice and this permission notice appear
|
|
|
|
in all copies.
|
|
|
|
|
|
|
|
There is NO WARRANTY for this software. See license.txt for
|
|
|
|
details. */
|
|
|
|
|
2007-07-06 17:50:26 -06:00
|
|
|
#ifndef MACHINE_H
|
|
|
|
#define MACHINE_H
|
2007-06-24 19:34:07 -06:00
|
|
|
|
2013-02-27 13:25:50 -07:00
|
|
|
#include "avian/common.h"
|
2013-02-20 22:14:18 -07:00
|
|
|
#include "java-common.h"
|
2014-02-07 14:24:56 -07:00
|
|
|
#include <avian/system/system.h>
|
2014-02-21 17:06:17 -07:00
|
|
|
#include <avian/system/signal.h>
|
2014-02-07 14:24:56 -07:00
|
|
|
#include <avian/heap/heap.h>
|
2014-06-28 17:24:24 -06:00
|
|
|
#include <avian/util/hash.h>
|
2013-02-27 13:25:50 -07:00
|
|
|
#include "avian/finder.h"
|
|
|
|
#include "avian/processor.h"
|
|
|
|
#include "avian/constants.h"
|
|
|
|
#include "avian/arch.h"
|
2007-06-24 19:34:07 -06:00
|
|
|
|
2013-02-19 22:42:07 -07:00
|
|
|
using namespace avian::util;
|
|
|
|
|
2009-08-26 18:26:44 -06:00
|
|
|
#ifdef PLATFORM_WINDOWS
|
2007-10-24 11:24:19 -06:00
|
|
|
# define JNICALL __stdcall
|
|
|
|
#else
|
|
|
|
# define JNICALL
|
|
|
|
#endif
|
2007-06-24 19:34:07 -06:00
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
#define PROTECT(thread, name) \
|
2007-10-12 11:56:43 -06:00
|
|
|
Thread::SingleProtector MAKE_NAME(protector_) (thread, &name);
|
2007-07-06 09:24:06 -06:00
|
|
|
|
|
|
|
#define ACQUIRE(t, x) MonitorResource MAKE_NAME(monitorResource_) (t, x)
|
|
|
|
|
2010-09-16 19:43:27 -06:00
|
|
|
#define ACQUIRE_OBJECT(t, x) \
|
|
|
|
ObjectMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
|
|
|
|
2011-03-15 17:52:02 -06:00
|
|
|
#define ACQUIRE_FIELD_FOR_READ(t, field) \
|
|
|
|
FieldReadResource MAKE_NAME(monitorResource_) (t, field)
|
|
|
|
|
|
|
|
#define ACQUIRE_FIELD_FOR_WRITE(t, field) \
|
2011-04-10 14:42:46 -06:00
|
|
|
FieldWriteResource MAKE_NAME(monitorResource_) (t, field)
|
2011-03-15 17:52:02 -06:00
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
#define ACQUIRE_RAW(t, x) RawMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
|
|
|
|
|
|
|
#define ENTER(t, state) StateResource MAKE_NAME(stateResource_) (t, state)
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
#define THREAD_RESOURCE0(t, releaseBody) \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class MAKE_NAME(Resource_): public Thread::AutoResource { \
|
2010-12-27 15:55:23 -07:00
|
|
|
public: \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
MAKE_NAME(Resource_)(Thread* t): AutoResource(t) { } \
|
2010-12-27 15:55:23 -07:00
|
|
|
~MAKE_NAME(Resource_)() { releaseBody; } \
|
|
|
|
virtual void release() \
|
|
|
|
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
|
|
|
} MAKE_NAME(resource_)(t);
|
|
|
|
|
|
|
|
#define OBJECT_RESOURCE(t, name, releaseBody) \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class MAKE_NAME(Resource_): public Thread::AutoResource { \
|
2010-12-27 15:55:23 -07:00
|
|
|
public: \
|
|
|
|
MAKE_NAME(Resource_)(Thread* t, object name): \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
AutoResource(t), name(name), protector(t, &(this->name)) { } \
|
2010-12-27 15:55:23 -07:00
|
|
|
~MAKE_NAME(Resource_)() { releaseBody; } \
|
|
|
|
virtual void release() \
|
|
|
|
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
|
|
|
\
|
|
|
|
private: \
|
|
|
|
object name; \
|
|
|
|
Thread::SingleProtector protector; \
|
2014-05-28 22:17:25 -06:00
|
|
|
} MAKE_NAME(resource_)(t, reinterpret_cast<object>(name));
|
2010-12-27 15:55:23 -07:00
|
|
|
|
|
|
|
#define THREAD_RESOURCE(t, type, name, releaseBody) \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class MAKE_NAME(Resource_): public Thread::AutoResource { \
|
2010-12-27 15:55:23 -07:00
|
|
|
public: \
|
|
|
|
MAKE_NAME(Resource_)(Thread* t, type name): \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
AutoResource(t), name(name) { } \
|
2010-12-27 15:55:23 -07:00
|
|
|
~MAKE_NAME(Resource_)() { releaseBody; } \
|
|
|
|
virtual void release() \
|
|
|
|
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
|
|
|
\
|
|
|
|
private: \
|
|
|
|
type name; \
|
|
|
|
} MAKE_NAME(resource_)(t, name);
|
|
|
|
|
|
|
|
#define THREAD_RESOURCE2(t, type1, name1, type2, name2, releaseBody) \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class MAKE_NAME(Resource_): public Thread::AutoResource { \
|
2010-12-27 15:55:23 -07:00
|
|
|
public: \
|
|
|
|
MAKE_NAME(Resource_)(Thread* t, type1 name1, type2 name2): \
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
AutoResource(t), name1(name1), name2(name2) { } \
|
2010-12-27 15:55:23 -07:00
|
|
|
~MAKE_NAME(Resource_)() { releaseBody; } \
|
|
|
|
virtual void release() \
|
|
|
|
{ this->MAKE_NAME(Resource_)::~MAKE_NAME(Resource_)(); } \
|
|
|
|
\
|
|
|
|
private: \
|
|
|
|
type1 name1; \
|
|
|
|
type2 name2; \
|
|
|
|
} MAKE_NAME(resource_)(t, name1, name2);
|
|
|
|
|
2007-06-24 20:02:24 -06:00
|
|
|
namespace vm {
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
const bool Verbose = false;
|
2007-07-27 17:56:19 -06:00
|
|
|
const bool DebugRun = false;
|
2007-07-09 21:04:49 -06:00
|
|
|
const bool DebugStack = false;
|
2007-11-02 15:42:19 -06:00
|
|
|
const bool DebugMonitors = false;
|
2007-11-05 08:39:48 -07:00
|
|
|
const bool DebugReferences = false;
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2013-03-07 20:17:05 -07:00
|
|
|
const bool AbortOnOutOfMemoryError = false;
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
const uintptr_t HashTakenMark = 1;
|
|
|
|
const uintptr_t ExtendedMark = 2;
|
2007-10-22 11:22:30 -06:00
|
|
|
const uintptr_t FixedMark = 3;
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2008-11-22 14:47:18 -07:00
|
|
|
const unsigned ThreadHeapSizeInBytes = 64 * 1024;
|
|
|
|
const unsigned ThreadHeapSizeInWords = ThreadHeapSizeInBytes / BytesPerWord;
|
|
|
|
|
2010-06-19 16:40:21 -06:00
|
|
|
const unsigned ThreadBackupHeapSizeInBytes = 2 * 1024;
|
|
|
|
const unsigned ThreadBackupHeapSizeInWords
|
|
|
|
= ThreadBackupHeapSizeInBytes / BytesPerWord;
|
|
|
|
|
2008-11-22 14:48:10 -07:00
|
|
|
const unsigned ThreadHeapPoolSize = 64;
|
2008-11-22 14:47:18 -07:00
|
|
|
|
|
|
|
const unsigned FixedFootprintThresholdInBytes
|
|
|
|
= ThreadHeapPoolSize * ThreadHeapSizeInBytes;
|
|
|
|
|
2012-02-03 12:00:02 -07:00
|
|
|
// number of zombie threads which may accumulate before we force a GC
|
|
|
|
// to clean them up:
|
|
|
|
const unsigned ZombieCollectionThreshold = 16;
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
enum FieldCode {
|
|
|
|
VoidField,
|
|
|
|
ByteField,
|
|
|
|
CharField,
|
|
|
|
DoubleField,
|
|
|
|
FloatField,
|
|
|
|
IntField,
|
|
|
|
LongField,
|
|
|
|
ShortField,
|
|
|
|
BooleanField,
|
|
|
|
ObjectField
|
|
|
|
};
|
|
|
|
|
|
|
|
enum StackTag {
|
|
|
|
IntTag, // must be zero
|
|
|
|
ObjectTag
|
|
|
|
};
|
|
|
|
|
2011-03-17 09:41:23 -06:00
|
|
|
const int NativeLine = -2;
|
|
|
|
const int UnknownLine = -1;
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2009-05-03 14:57:11 -06:00
|
|
|
// class vmFlags:
|
2007-07-19 21:18:25 -06:00
|
|
|
const unsigned ReferenceFlag = 1 << 0;
|
|
|
|
const unsigned WeakReferenceFlag = 1 << 1;
|
2007-07-28 10:10:13 -06:00
|
|
|
const unsigned NeedInitFlag = 1 << 2;
|
2007-08-01 17:48:36 -06:00
|
|
|
const unsigned InitFlag = 1 << 3;
|
2009-07-20 14:12:38 -06:00
|
|
|
const unsigned InitErrorFlag = 1 << 4;
|
|
|
|
const unsigned PrimitiveFlag = 1 << 5;
|
|
|
|
const unsigned BootstrapFlag = 1 << 6;
|
2009-07-21 18:57:55 -06:00
|
|
|
const unsigned HasFinalizerFlag = 1 << 7;
|
2009-08-18 14:26:28 -06:00
|
|
|
const unsigned LinkFlag = 1 << 8;
|
|
|
|
const unsigned HasFinalMemberFlag = 1 << 9;
|
|
|
|
const unsigned SingletonFlag = 1 << 10;
|
|
|
|
const unsigned ContinuationFlag = 1 << 11;
|
2007-08-01 17:48:36 -06:00
|
|
|
|
2009-05-03 14:57:11 -06:00
|
|
|
// method vmFlags:
|
2007-08-01 17:48:36 -06:00
|
|
|
const unsigned ClassInitFlag = 1 << 0;
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
const unsigned ConstructorFlag = 1 << 1;
|
2007-07-19 21:18:25 -06:00
|
|
|
|
2009-06-22 16:25:13 -06:00
|
|
|
#ifndef JNI_VERSION_1_6
|
|
|
|
#define JNI_VERSION_1_6 0x00010006
|
|
|
|
#endif
|
|
|
|
|
2007-09-10 17:33:58 -06:00
|
|
|
typedef Machine JavaVM;
|
2007-07-06 09:24:06 -06:00
|
|
|
typedef Thread JNIEnv;
|
|
|
|
|
2007-06-24 19:34:07 -06:00
|
|
|
struct JNINativeMethod {
|
|
|
|
char* name;
|
|
|
|
char* signature;
|
|
|
|
void* function;
|
|
|
|
};
|
|
|
|
|
2007-06-24 20:02:24 -06:00
|
|
|
struct JavaVMVTable {
|
2007-06-24 19:34:07 -06:00
|
|
|
void* reserved0;
|
|
|
|
void* reserved1;
|
|
|
|
void* reserved2;
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *DestroyJavaVM)
|
|
|
|
(JavaVM*);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *AttachCurrentThread)
|
2007-09-10 17:33:58 -06:00
|
|
|
(JavaVM*, JNIEnv**, void*);
|
2007-06-24 19:34:07 -06:00
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *DetachCurrentThread)
|
|
|
|
(JavaVM*);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *GetEnv)
|
2007-09-10 17:33:58 -06:00
|
|
|
(JavaVM*, JNIEnv**, jint);
|
2007-06-24 19:34:07 -06:00
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *AttachCurrentThreadAsDaemon)
|
2007-09-10 17:33:58 -06:00
|
|
|
(JavaVM*, JNIEnv**, void*);
|
2007-06-24 19:34:07 -06:00
|
|
|
};
|
|
|
|
|
2007-06-24 20:02:24 -06:00
|
|
|
struct JNIEnvVTable {
|
2007-06-24 19:34:07 -06:00
|
|
|
void* reserved0;
|
|
|
|
void* reserved1;
|
|
|
|
void* reserved2;
|
|
|
|
void* reserved3;
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *GetVersion)
|
|
|
|
(JNIEnv*);
|
|
|
|
|
|
|
|
jclass
|
|
|
|
(JNICALL *DefineClass)
|
|
|
|
(JNIEnv*, const char*, jobject, const jbyte*, jsize);
|
|
|
|
|
|
|
|
jclass
|
|
|
|
(JNICALL *FindClass)
|
|
|
|
(JNIEnv*, const char*);
|
|
|
|
|
|
|
|
jmethodID
|
|
|
|
(JNICALL *FromReflectedMethod)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jfieldID
|
|
|
|
(JNICALL *FromReflectedField)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *ToReflectedMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, jboolean);
|
|
|
|
|
|
|
|
jclass
|
|
|
|
(JNICALL *GetSuperclass)
|
|
|
|
(JNIEnv*, jclass);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *IsAssignableFrom)
|
|
|
|
(JNIEnv*, jclass, jclass);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *ToReflectedField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jboolean);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *Throw)
|
|
|
|
(JNIEnv*, jthrowable);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *ThrowNew)
|
|
|
|
(JNIEnv*, jclass, const char*);
|
|
|
|
|
|
|
|
jthrowable
|
|
|
|
(JNICALL *ExceptionOccurred)
|
|
|
|
(JNIEnv*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ExceptionDescribe)
|
|
|
|
(JNIEnv*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ExceptionClear)
|
|
|
|
(JNIEnv*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *FatalError)
|
|
|
|
(JNIEnv*, const char*);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *PushLocalFrame)
|
|
|
|
(JNIEnv*, jint);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *PopLocalFrame)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *NewGlobalRef)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *DeleteGlobalRef)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *DeleteLocalRef)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *IsSameObject)
|
|
|
|
(JNIEnv*, jobject, jobject);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *NewLocalRef)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *EnsureLocalCapacity)
|
|
|
|
(JNIEnv*, jint);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *AllocObject)
|
|
|
|
(JNIEnv*, jclass);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *NewObject)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *NewObjectV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *NewObjectA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jclass
|
|
|
|
(JNICALL *GetObjectClass)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *IsInstanceOf)
|
|
|
|
(JNIEnv*, jobject, jclass);
|
|
|
|
|
|
|
|
jmethodID
|
|
|
|
(JNICALL *GetMethodID)
|
|
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallObjectMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallObjectMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallObjectMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallBooleanMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallBooleanMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallBooleanMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallByteMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallByteMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallByteMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallCharMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallCharMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallCharMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallShortMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallShortMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallShortMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallIntMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallIntMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallIntMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallLongMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallLongMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallLongMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallFloatMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallFloatMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallFloatMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallDoubleMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallDoubleMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallDoubleMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallVoidMethod)
|
|
|
|
(JNIEnv*, jobject, jmethodID, ...);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallVoidMethodV)
|
|
|
|
(JNIEnv*, jobject, jmethodID, va_list);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallVoidMethodA)
|
|
|
|
(JNIEnv*, jobject, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallNonvirtualObjectMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallNonvirtualObjectMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallNonvirtualObjectMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallNonvirtualBooleanMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallNonvirtualBooleanMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallNonvirtualBooleanMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallNonvirtualByteMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallNonvirtualByteMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallNonvirtualByteMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallNonvirtualCharMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallNonvirtualCharMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallNonvirtualCharMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallNonvirtualShortMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallNonvirtualShortMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
|
|
va_list);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallNonvirtualShortMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
|
|
const jvalue*);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallNonvirtualIntMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallNonvirtualIntMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
|
|
va_list);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallNonvirtualIntMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
|
|
const jvalue*);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallNonvirtualLongMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallNonvirtualLongMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID,
|
|
|
|
va_list);
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallNonvirtualLongMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallNonvirtualFloatMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallNonvirtualFloatMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallNonvirtualFloatMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallNonvirtualDoubleMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallNonvirtualDoubleMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallNonvirtualDoubleMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallNonvirtualVoidMethod)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallNonvirtualVoidMethodV)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallNonvirtualVoidMethodA)
|
|
|
|
(JNIEnv*, jobject, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jfieldID
|
|
|
|
(JNICALL *GetFieldID)
|
|
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *GetObjectField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *GetBooleanField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *GetByteField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *GetCharField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *GetShortField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *GetIntField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *GetLongField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *GetFloatField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *GetDoubleField)
|
|
|
|
(JNIEnv*, jobject, jfieldID);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetObjectField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jobject);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetBooleanField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jboolean);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetByteField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jbyte);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetCharField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jchar);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetShortField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jshort);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetIntField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetLongField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jlong);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetFloatField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jfloat);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetDoubleField)
|
|
|
|
(JNIEnv*, jobject, jfieldID, jdouble);
|
|
|
|
|
|
|
|
jmethodID
|
|
|
|
(JNICALL *GetStaticMethodID)
|
|
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallStaticObjectMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallStaticObjectMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *CallStaticObjectMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallStaticBooleanMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallStaticBooleanMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *CallStaticBooleanMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallStaticByteMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallStaticByteMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *CallStaticByteMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallStaticCharMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallStaticCharMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *CallStaticCharMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallStaticShortMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallStaticShortMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *CallStaticShortMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallStaticIntMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallStaticIntMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *CallStaticIntMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallStaticLongMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallStaticLongMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *CallStaticLongMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallStaticFloatMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallStaticFloatMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *CallStaticFloatMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallStaticDoubleMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallStaticDoubleMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *CallStaticDoubleMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallStaticVoidMethod)
|
|
|
|
(JNIEnv*, jclass, jmethodID, ...);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallStaticVoidMethodV)
|
|
|
|
(JNIEnv*, jclass, jmethodID, va_list);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *CallStaticVoidMethodA)
|
|
|
|
(JNIEnv*, jclass, jmethodID, const jvalue*);
|
|
|
|
|
|
|
|
jfieldID
|
|
|
|
(JNICALL *GetStaticFieldID)
|
|
|
|
(JNIEnv*, jclass, const char*, const char*);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *GetStaticObjectField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *GetStaticBooleanField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jbyte
|
|
|
|
(JNICALL *GetStaticByteField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jchar
|
|
|
|
(JNICALL *GetStaticCharField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jshort
|
|
|
|
(JNICALL *GetStaticShortField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *GetStaticIntField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *GetStaticLongField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jfloat
|
|
|
|
(JNICALL *GetStaticFloatField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
jdouble
|
|
|
|
(JNICALL *GetStaticDoubleField)
|
|
|
|
(JNIEnv*, jclass, jfieldID);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticObjectField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jobject);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticBooleanField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jboolean);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticByteField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jbyte);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticCharField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jchar);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticShortField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jshort);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticIntField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticLongField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jlong);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticFloatField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jfloat);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetStaticDoubleField)
|
|
|
|
(JNIEnv*, jclass, jfieldID, jdouble);
|
|
|
|
|
|
|
|
jstring
|
|
|
|
(JNICALL *NewString)
|
|
|
|
(JNIEnv*, const jchar*, jsize);
|
|
|
|
|
|
|
|
jsize
|
|
|
|
(JNICALL *GetStringLength)
|
|
|
|
(JNIEnv*, jstring);
|
|
|
|
|
|
|
|
const jchar*
|
|
|
|
(JNICALL *GetStringChars)
|
|
|
|
(JNIEnv*, jstring, jboolean*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseStringChars)
|
|
|
|
(JNIEnv*, jstring, const jchar*);
|
|
|
|
|
|
|
|
jstring
|
|
|
|
(JNICALL *NewStringUTF)
|
|
|
|
(JNIEnv*, const char*);
|
|
|
|
|
|
|
|
jsize
|
|
|
|
(JNICALL *GetStringUTFLength)
|
|
|
|
(JNIEnv*, jstring);
|
|
|
|
|
|
|
|
const char*
|
|
|
|
(JNICALL *GetStringUTFChars)
|
|
|
|
(JNIEnv*, jstring, jboolean*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseStringUTFChars)
|
|
|
|
(JNIEnv*, jstring, const char*);
|
|
|
|
|
|
|
|
jsize
|
|
|
|
(JNICALL *GetArrayLength)
|
|
|
|
(JNIEnv*, jarray);
|
|
|
|
|
|
|
|
jobjectArray
|
|
|
|
(JNICALL *NewObjectArray)
|
|
|
|
(JNIEnv*, jsize, jclass, jobject);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *GetObjectArrayElement)
|
|
|
|
(JNIEnv*, jobjectArray, jsize);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetObjectArrayElement)
|
|
|
|
(JNIEnv*, jobjectArray, jsize, jobject);
|
|
|
|
|
|
|
|
jbooleanArray
|
|
|
|
(JNICALL *NewBooleanArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jbyteArray
|
|
|
|
(JNICALL *NewByteArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jcharArray
|
|
|
|
(JNICALL *NewCharArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jshortArray
|
|
|
|
(JNICALL *NewShortArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jintArray
|
|
|
|
(JNICALL *NewIntArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jlongArray
|
|
|
|
(JNICALL *NewLongArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jfloatArray
|
|
|
|
(JNICALL *NewFloatArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jdoubleArray
|
|
|
|
(JNICALL *NewDoubleArray)
|
|
|
|
(JNIEnv*, jsize);
|
|
|
|
|
|
|
|
jboolean*
|
|
|
|
(JNICALL *GetBooleanArrayElements)
|
|
|
|
(JNIEnv*, jbooleanArray, jboolean*);
|
|
|
|
|
|
|
|
jbyte*
|
|
|
|
(JNICALL *GetByteArrayElements)
|
|
|
|
(JNIEnv*, jbyteArray, jboolean*);
|
|
|
|
|
|
|
|
jchar*
|
|
|
|
(JNICALL *GetCharArrayElements)
|
|
|
|
(JNIEnv*, jcharArray, jboolean*);
|
|
|
|
|
|
|
|
jshort*
|
|
|
|
(JNICALL *GetShortArrayElements)
|
|
|
|
(JNIEnv*, jshortArray, jboolean*);
|
|
|
|
|
|
|
|
jint*
|
|
|
|
(JNICALL *GetIntArrayElements)
|
|
|
|
(JNIEnv*, jintArray, jboolean*);
|
|
|
|
|
|
|
|
jlong*
|
|
|
|
(JNICALL *GetLongArrayElements)
|
|
|
|
(JNIEnv*, jlongArray, jboolean*);
|
|
|
|
|
|
|
|
jfloat*
|
|
|
|
(JNICALL *GetFloatArrayElements)
|
|
|
|
(JNIEnv*, jfloatArray, jboolean*);
|
|
|
|
|
|
|
|
jdouble*
|
|
|
|
(JNICALL *GetDoubleArrayElements)
|
|
|
|
(JNIEnv*, jdoubleArray, jboolean*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseBooleanArrayElements)
|
|
|
|
(JNIEnv*, jbooleanArray, jboolean*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseByteArrayElements)
|
|
|
|
(JNIEnv*, jbyteArray, jbyte*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseCharArrayElements)
|
|
|
|
(JNIEnv*, jcharArray, jchar*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseShortArrayElements)
|
|
|
|
(JNIEnv*, jshortArray, jshort*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseIntArrayElements)
|
|
|
|
(JNIEnv*, jintArray, jint*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseLongArrayElements)
|
|
|
|
(JNIEnv*, jlongArray, jlong*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseFloatArrayElements)
|
|
|
|
(JNIEnv*, jfloatArray, jfloat*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseDoubleArrayElements)
|
|
|
|
(JNIEnv*, jdoubleArray, jdouble*, jint);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetBooleanArrayRegion)
|
|
|
|
(JNIEnv*, jbooleanArray, jsize, jsize, jboolean*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetByteArrayRegion)
|
|
|
|
(JNIEnv*, jbyteArray, jsize, jsize, jbyte*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetCharArrayRegion)
|
|
|
|
(JNIEnv*, jcharArray, jsize, jsize, jchar*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetShortArrayRegion)
|
|
|
|
(JNIEnv*, jshortArray, jsize, jsize, jshort*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetIntArrayRegion)
|
|
|
|
(JNIEnv*, jintArray, jsize, jsize, jint*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetLongArrayRegion)
|
|
|
|
(JNIEnv*, jlongArray, jsize, jsize, jlong*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetFloatArrayRegion)
|
|
|
|
(JNIEnv*, jfloatArray, jsize, jsize, jfloat*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetDoubleArrayRegion)
|
|
|
|
(JNIEnv*, jdoubleArray, jsize, jsize, jdouble*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetBooleanArrayRegion)
|
|
|
|
(JNIEnv*, jbooleanArray, jsize, jsize, const jboolean*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetByteArrayRegion)
|
|
|
|
(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetCharArrayRegion)
|
|
|
|
(JNIEnv*, jcharArray, jsize, jsize, const jchar*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetShortArrayRegion)
|
|
|
|
(JNIEnv*, jshortArray, jsize, jsize, const jshort*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetIntArrayRegion)
|
|
|
|
(JNIEnv*, jintArray, jsize, jsize, const jint*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetLongArrayRegion)
|
|
|
|
(JNIEnv*, jlongArray, jsize, jsize, const jlong*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetFloatArrayRegion)
|
|
|
|
(JNIEnv*, jfloatArray, jsize, jsize, const jfloat*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *SetDoubleArrayRegion)
|
|
|
|
(JNIEnv*, jdoubleArray, jsize, jsize, const jdouble*);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *RegisterNatives)
|
|
|
|
(JNIEnv*, jclass, const JNINativeMethod*, jint);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *UnregisterNatives)
|
|
|
|
(JNIEnv*, jclass);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *MonitorEnter)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *MonitorExit)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
jint
|
|
|
|
(JNICALL *GetJavaVM)
|
|
|
|
(JNIEnv*, JavaVM**);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetStringRegion)
|
|
|
|
(JNIEnv*, jstring, jsize, jsize, jchar*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *GetStringUTFRegion)
|
|
|
|
(JNIEnv*, jstring, jsize, jsize, char*);
|
|
|
|
|
|
|
|
void*
|
|
|
|
(JNICALL *GetPrimitiveArrayCritical)
|
|
|
|
(JNIEnv*, jarray, jboolean*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleasePrimitiveArrayCritical)
|
|
|
|
(JNIEnv*, jarray, void*, jint);
|
|
|
|
|
|
|
|
const jchar*
|
|
|
|
(JNICALL *GetStringCritical)
|
|
|
|
(JNIEnv*, jstring, jboolean*);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *ReleaseStringCritical)
|
|
|
|
(JNIEnv*, jstring, const jchar*);
|
|
|
|
|
|
|
|
jweak
|
|
|
|
(JNICALL *NewWeakGlobalRef)
|
|
|
|
(JNIEnv*, jobject);
|
|
|
|
|
|
|
|
void
|
|
|
|
(JNICALL *DeleteWeakGlobalRef)
|
|
|
|
(JNIEnv*, jweak);
|
|
|
|
|
|
|
|
jboolean
|
|
|
|
(JNICALL *ExceptionCheck)
|
|
|
|
(JNIEnv*);
|
|
|
|
|
|
|
|
jobject
|
|
|
|
(JNICALL *NewDirectByteBuffer)
|
|
|
|
(JNIEnv*, void*, jlong);
|
|
|
|
|
|
|
|
void*
|
|
|
|
(JNICALL *GetDirectBufferAddress)
|
|
|
|
(JNIEnv* env, jobject);
|
|
|
|
|
|
|
|
jlong
|
|
|
|
(JNICALL *GetDirectBufferCapacity)
|
|
|
|
(JNIEnv*, jobject);
|
2008-06-15 14:17:52 -06:00
|
|
|
|
2007-06-24 19:34:07 -06:00
|
|
|
};
|
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
inline void
|
|
|
|
atomicOr(uint32_t* p, int v)
|
|
|
|
{
|
|
|
|
for (uint32_t old = *p;
|
|
|
|
not atomicCompareAndSwap32(p, old, old | v);
|
|
|
|
old = *p)
|
|
|
|
{ }
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
atomicAnd(uint32_t* p, int v)
|
|
|
|
{
|
|
|
|
for (uint32_t old = *p;
|
|
|
|
not atomicCompareAndSwap32(p, old, old & v);
|
|
|
|
old = *p)
|
|
|
|
{ }
|
|
|
|
}
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
inline int
|
|
|
|
strcmp(const int8_t* a, const int8_t* b)
|
|
|
|
{
|
|
|
|
return ::strcmp(reinterpret_cast<const char*>(a),
|
|
|
|
reinterpret_cast<const char*>(b));
|
|
|
|
}
|
|
|
|
|
2007-07-09 19:43:43 -06:00
|
|
|
void
|
|
|
|
noop();
|
|
|
|
|
2007-09-07 17:20:21 -06:00
|
|
|
class Reference {
|
|
|
|
public:
|
2012-02-29 11:51:30 -07:00
|
|
|
Reference(object target, Reference** handle, bool weak):
|
2007-09-07 17:20:21 -06:00
|
|
|
target(target),
|
2007-09-29 21:33:38 -06:00
|
|
|
next(*handle),
|
2009-12-16 19:16:51 -07:00
|
|
|
handle(handle),
|
2012-02-29 11:51:30 -07:00
|
|
|
count(0),
|
|
|
|
weak(weak)
|
2007-09-29 21:33:38 -06:00
|
|
|
{
|
|
|
|
if (next) {
|
|
|
|
next->handle = &next;
|
|
|
|
}
|
|
|
|
*handle = this;
|
|
|
|
}
|
2007-09-07 17:20:21 -06:00
|
|
|
|
|
|
|
object target;
|
|
|
|
Reference* next;
|
2007-09-29 21:33:38 -06:00
|
|
|
Reference** handle;
|
2009-12-16 19:16:51 -07:00
|
|
|
unsigned count;
|
2012-02-29 11:51:30 -07:00
|
|
|
bool weak;
|
2007-09-07 17:20:21 -06:00
|
|
|
};
|
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
class Classpath;
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
class Gc {
|
2007-07-06 09:24:06 -06:00
|
|
|
public:
|
2007-09-12 21:15:16 -06:00
|
|
|
enum Type {
|
2007-07-06 09:24:06 -06:00
|
|
|
#include "type-enums.cpp"
|
2007-09-12 21:15:16 -06:00
|
|
|
};
|
2014-05-28 22:17:25 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
class GcObject {
|
|
|
|
public:
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
T* as(Thread* t);
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
bool isa(Thread* t);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
template <class T>
|
|
|
|
T& field_at(size_t offset)
|
|
|
|
{
|
|
|
|
return *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(this) + offset);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class GcFinalizer;
|
2014-06-20 22:16:33 -06:00
|
|
|
class GcClassLoader;
|
2014-06-28 17:24:24 -06:00
|
|
|
class GcJreference;
|
|
|
|
class GcArray;
|
|
|
|
class GcThrowable;
|
2014-05-28 22:17:25 -06:00
|
|
|
|
|
|
|
class Machine {
|
|
|
|
public:
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2008-01-09 18:20:36 -07:00
|
|
|
enum AllocationType {
|
|
|
|
MovableAllocation,
|
|
|
|
FixedAllocation,
|
|
|
|
ImmortalAllocation
|
2007-10-13 19:18:25 -06:00
|
|
|
};
|
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
enum Root {
|
|
|
|
BootLoader,
|
|
|
|
AppLoader,
|
|
|
|
BootstrapClassMap,
|
2012-09-24 17:43:34 -06:00
|
|
|
PackageMap,
|
2010-09-22 13:58:46 -06:00
|
|
|
FindLoadedClassMethod,
|
|
|
|
LoadClassMethod,
|
2010-09-14 10:49:41 -06:00
|
|
|
MonitorMap,
|
|
|
|
StringMap,
|
|
|
|
ByteArrayMap,
|
2011-08-29 19:00:17 -06:00
|
|
|
PoolMap,
|
2010-11-26 12:41:31 -07:00
|
|
|
ClassRuntimeDataTable,
|
|
|
|
MethodRuntimeDataTable,
|
2010-09-14 10:49:41 -06:00
|
|
|
JNIMethodTable,
|
2011-03-15 19:34:00 -06:00
|
|
|
JNIFieldTable,
|
2010-09-14 10:49:41 -06:00
|
|
|
ShutdownHooks,
|
2010-12-27 15:55:23 -07:00
|
|
|
FinalizerThread,
|
2010-09-14 10:49:41 -06:00
|
|
|
ObjectsToFinalize,
|
2011-03-25 19:11:38 -06:00
|
|
|
ObjectsToClean,
|
2010-09-14 10:49:41 -06:00
|
|
|
NullPointerException,
|
2010-12-19 17:47:21 -07:00
|
|
|
ArithmeticException,
|
2010-11-05 13:18:28 -06:00
|
|
|
ArrayIndexOutOfBoundsException,
|
2010-12-27 15:55:23 -07:00
|
|
|
OutOfMemoryError,
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
Shutdown,
|
2010-11-05 13:18:28 -06:00
|
|
|
VirtualFileFinders,
|
2013-02-21 15:37:17 -07:00
|
|
|
VirtualFiles,
|
2013-03-05 09:06:19 -07:00
|
|
|
ArrayInterfaceTable,
|
|
|
|
ThreadTerminated
|
2010-09-14 10:49:41 -06:00
|
|
|
};
|
|
|
|
|
2013-03-05 09:06:19 -07:00
|
|
|
static const unsigned RootCount = ThreadTerminated + 1;
|
2010-09-14 10:49:41 -06:00
|
|
|
|
|
|
|
Machine(System* system, Heap* heap, Finder* bootFinder, Finder* appFinder,
|
|
|
|
Processor* processor, Classpath* classpath, const char** properties,
|
2011-08-05 18:06:29 -06:00
|
|
|
unsigned propertyCount, const char** arguments,
|
2012-03-14 12:36:42 -06:00
|
|
|
unsigned argumentCount, unsigned stackSizeInBytes);
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
~Machine() {
|
2007-07-06 09:24:06 -06:00
|
|
|
dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
void dispose();
|
|
|
|
|
2007-09-10 17:33:58 -06:00
|
|
|
JavaVMVTable* vtable;
|
2007-07-06 09:24:06 -06:00
|
|
|
System* system;
|
2008-01-13 15:05:08 -07:00
|
|
|
Heap::Client* heapClient;
|
2007-07-06 09:24:06 -06:00
|
|
|
Heap* heap;
|
2010-09-14 10:49:41 -06:00
|
|
|
Finder* bootFinder;
|
|
|
|
Finder* appFinder;
|
2007-09-23 19:39:03 -06:00
|
|
|
Processor* processor;
|
2010-09-10 15:05:29 -06:00
|
|
|
Classpath* classpath;
|
2007-07-06 09:24:06 -06:00
|
|
|
Thread* rootThread;
|
|
|
|
Thread* exclusive;
|
2009-08-24 17:51:31 -06:00
|
|
|
Thread* finalizeThread;
|
2007-09-07 17:20:21 -06:00
|
|
|
Reference* jniReferences;
|
2014-04-09 10:16:53 +04:00
|
|
|
char** properties;
|
2008-11-11 08:20:49 -07:00
|
|
|
unsigned propertyCount;
|
2011-08-05 18:06:29 -06:00
|
|
|
const char** arguments;
|
|
|
|
unsigned argumentCount;
|
2012-02-03 12:00:02 -07:00
|
|
|
unsigned threadCount;
|
2007-07-06 09:24:06 -06:00
|
|
|
unsigned activeCount;
|
|
|
|
unsigned liveCount;
|
2009-08-19 14:27:03 -06:00
|
|
|
unsigned daemonCount;
|
2007-10-28 13:14:53 -06:00
|
|
|
unsigned fixedFootprint;
|
2012-03-14 12:36:42 -06:00
|
|
|
unsigned stackSizeInBytes;
|
2007-09-10 17:33:58 -06:00
|
|
|
System::Local* localThread;
|
2007-07-06 09:24:06 -06:00
|
|
|
System::Monitor* stateLock;
|
|
|
|
System::Monitor* heapLock;
|
|
|
|
System::Monitor* classLock;
|
2007-07-19 21:18:25 -06:00
|
|
|
System::Monitor* referenceLock;
|
2009-08-19 14:27:03 -06:00
|
|
|
System::Monitor* shutdownLock;
|
2008-01-29 08:19:15 -07:00
|
|
|
System::Library* libraries;
|
2011-03-15 17:27:17 -06:00
|
|
|
FILE* errorLog;
|
2012-06-02 09:06:22 -06:00
|
|
|
BootImage* bootimage;
|
2014-06-28 17:24:24 -06:00
|
|
|
GcArray* types;
|
|
|
|
GcArray* roots;
|
2014-05-28 22:17:25 -06:00
|
|
|
GcFinalizer* finalizers;
|
|
|
|
GcFinalizer* tenuredFinalizers;
|
2014-06-28 17:24:24 -06:00
|
|
|
GcFinalizer* finalizeQueue;
|
|
|
|
GcJreference* weakReferences;
|
|
|
|
GcJreference* tenuredWeakReferences;
|
2007-07-06 09:24:06 -06:00
|
|
|
bool unsafe;
|
2011-03-25 19:11:38 -06:00
|
|
|
bool collecting;
|
2009-06-10 18:15:00 -06:00
|
|
|
bool triedBuiltinOnLoad;
|
2011-02-02 08:46:20 -07:00
|
|
|
bool dumpedHeapOnOOM;
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
bool alive;
|
2007-09-10 17:33:58 -06:00
|
|
|
JavaVMVTable javaVMVTable;
|
2007-07-06 09:24:06 -06:00
|
|
|
JNIEnvVTable jniEnvVTable;
|
2008-11-22 14:47:18 -07:00
|
|
|
uintptr_t* heapPool[ThreadHeapPoolSize];
|
2007-08-22 08:50:29 -06:00
|
|
|
unsigned heapPoolIndex;
|
2012-06-02 09:06:22 -06:00
|
|
|
unsigned bootimageSize;
|
2007-07-06 09:24:06 -06:00
|
|
|
};
|
|
|
|
|
2007-07-28 15:28:25 -06:00
|
|
|
void
|
2014-06-28 17:24:24 -06:00
|
|
|
printTrace(Thread* t, GcThrowable* exception);
|
2007-07-28 15:28:25 -06:00
|
|
|
|
2007-10-23 16:21:28 -06:00
|
|
|
void
|
|
|
|
enterActiveState(Thread* t);
|
|
|
|
|
2008-01-13 15:05:08 -07:00
|
|
|
#ifdef VM_STRESS
|
|
|
|
|
|
|
|
inline void stress(Thread* t);
|
|
|
|
|
|
|
|
#else // not VM_STRESS
|
|
|
|
|
|
|
|
#define stress(t)
|
|
|
|
|
|
|
|
#endif // not VM_STRESS
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
uint64_t
|
|
|
|
runThread(Thread*, uintptr_t*);
|
2008-06-25 10:28:11 -06:00
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
uint64_t
|
|
|
|
run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*),
|
|
|
|
uintptr_t* arguments);
|
2009-08-24 17:51:31 -06:00
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
void
|
|
|
|
checkDaemon(Thread* t);
|
|
|
|
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
object&
|
|
|
|
root(Thread* t, Machine::Root root);
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
extern "C" uint64_t
|
|
|
|
vmRun(uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments,
|
|
|
|
void* checkpoint);
|
|
|
|
|
|
|
|
extern "C" void
|
|
|
|
vmRun_returnAddress();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
class GcThread;
|
|
|
|
class GcThrowable;
|
|
|
|
class GcString;
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
class Thread {
|
|
|
|
public:
|
|
|
|
enum State {
|
|
|
|
NoState,
|
|
|
|
ActiveState,
|
|
|
|
IdleState,
|
|
|
|
ZombieState,
|
2007-07-17 19:33:00 -06:00
|
|
|
JoinedState,
|
2007-07-06 09:24:06 -06:00
|
|
|
ExclusiveState,
|
|
|
|
ExitState
|
|
|
|
};
|
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
static const unsigned UseBackupHeapFlag = 1 << 0;
|
|
|
|
static const unsigned WaitingFlag = 1 << 1;
|
|
|
|
static const unsigned TracingFlag = 1 << 2;
|
|
|
|
static const unsigned DaemonFlag = 1 << 3;
|
|
|
|
static const unsigned StressFlag = 1 << 4;
|
|
|
|
static const unsigned ActiveFlag = 1 << 5;
|
2010-11-16 10:50:19 -07:00
|
|
|
static const unsigned SystemFlag = 1 << 6;
|
2012-02-03 17:20:20 -07:00
|
|
|
static const unsigned JoinFlag = 1 << 7;
|
2010-09-14 10:49:41 -06:00
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
class Protector {
|
|
|
|
public:
|
2007-10-12 11:56:43 -06:00
|
|
|
Protector(Thread* t): t(t), next(t->protector) {
|
2007-07-06 09:24:06 -06:00
|
|
|
t->protector = this;
|
|
|
|
}
|
|
|
|
|
2008-11-11 08:20:49 -07:00
|
|
|
~Protector() {
|
2007-07-06 09:24:06 -06:00
|
|
|
t->protector = next;
|
|
|
|
}
|
|
|
|
|
2007-10-12 11:56:43 -06:00
|
|
|
virtual void visit(Heap::Visitor* v) = 0;
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
Thread* t;
|
|
|
|
Protector* next;
|
|
|
|
};
|
|
|
|
|
2007-10-12 11:56:43 -06:00
|
|
|
class SingleProtector: public Protector {
|
|
|
|
public:
|
2014-05-28 22:17:25 -06:00
|
|
|
SingleProtector(Thread* t, void* p): Protector(t), p(p) { }
|
2007-10-12 11:56:43 -06:00
|
|
|
|
|
|
|
virtual void visit(Heap::Visitor* v) {
|
2007-10-28 13:14:53 -06:00
|
|
|
v->visit(p);
|
2007-10-12 11:56:43 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
void* p;
|
2007-10-12 11:56:43 -06:00
|
|
|
};
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
class Resource {
|
|
|
|
public:
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
Resource(Thread* t, Resource* next): t(t), next(next) {
|
2010-12-27 15:55:23 -07:00
|
|
|
t->resource = this;
|
|
|
|
}
|
|
|
|
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
virtual void release() = 0;
|
|
|
|
|
|
|
|
Thread* t;
|
|
|
|
Resource* next;
|
|
|
|
};
|
|
|
|
|
|
|
|
class AutoResource: public Resource {
|
|
|
|
public:
|
|
|
|
AutoResource(Thread* t): Resource(t, t->resource) { }
|
|
|
|
|
|
|
|
~AutoResource() {
|
2010-12-27 15:55:23 -07:00
|
|
|
t->resource = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void release() = 0;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class ClassInitStack: public AutoResource {
|
2009-07-20 14:12:38 -06:00
|
|
|
public:
|
2014-05-28 22:17:25 -06:00
|
|
|
ClassInitStack(Thread* t, GcClass* class_):
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
AutoResource(t),
|
2009-07-20 14:12:38 -06:00
|
|
|
next(t->classInitStack),
|
|
|
|
class_(class_),
|
|
|
|
protector(t, &(this->class_))
|
|
|
|
{
|
|
|
|
t->classInitStack = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~ClassInitStack() {
|
2010-12-27 15:55:23 -07:00
|
|
|
t->classInitStack = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void release() {
|
|
|
|
this->ClassInitStack::~ClassInitStack();
|
2009-07-20 14:12:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
ClassInitStack* next;
|
2014-05-28 22:17:25 -06:00
|
|
|
GcClass* class_;
|
2009-07-20 14:12:38 -06:00
|
|
|
SingleProtector protector;
|
|
|
|
};
|
|
|
|
|
2014-06-23 17:17:13 -06:00
|
|
|
class LibraryLoadStack: public AutoResource {
|
|
|
|
public:
|
2014-06-20 22:16:33 -06:00
|
|
|
LibraryLoadStack(Thread* t, GcClassLoader* classLoader)
|
2014-07-01 10:18:45 -06:00
|
|
|
: AutoResource(t),
|
|
|
|
next(t->libraryLoadStack),
|
|
|
|
classLoader(classLoader),
|
|
|
|
protector(t, &(this->classLoader))
|
2014-06-23 17:17:13 -06:00
|
|
|
{
|
|
|
|
t->libraryLoadStack = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~LibraryLoadStack() {
|
|
|
|
t->libraryLoadStack = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void release() {
|
|
|
|
this->LibraryLoadStack::~LibraryLoadStack();
|
|
|
|
}
|
|
|
|
|
|
|
|
LibraryLoadStack* next;
|
2014-06-20 22:16:33 -06:00
|
|
|
GcClassLoader* classLoader;
|
2014-06-23 17:17:13 -06:00
|
|
|
SingleProtector protector;
|
|
|
|
};
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
class Checkpoint {
|
|
|
|
public:
|
|
|
|
Checkpoint(Thread* t):
|
|
|
|
t(t),
|
|
|
|
next(t->checkpoint),
|
|
|
|
resource(t->resource),
|
|
|
|
protector(t->protector),
|
|
|
|
noThrow(false)
|
|
|
|
{
|
|
|
|
t->checkpoint = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~Checkpoint() {
|
|
|
|
t->checkpoint = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void NO_RETURN unwind() = 0;
|
|
|
|
|
|
|
|
Thread* t;
|
|
|
|
Checkpoint* next;
|
|
|
|
Resource* resource;
|
|
|
|
Protector* protector;
|
|
|
|
bool noThrow;
|
|
|
|
};
|
|
|
|
|
|
|
|
class RunCheckpoint: public Checkpoint {
|
|
|
|
public:
|
|
|
|
RunCheckpoint(Thread* t):
|
|
|
|
Checkpoint(t),
|
support stack unwinding without using a frame pointer
Previously, we unwound the stack by following the chain of frame
pointers for normal returns, stack trace creation, and exception
unwinding. On x86, this required reserving EBP/RBP for frame pointer
duties, making it unavailable for general computation and requiring
that it be explicitly saved and restored on entry and exit,
respectively.
On PowerPC, we use an ABI that makes the stack pointer double as a
frame pointer, so it doesn't cost us anything. We've been using the
same convention on ARM, but it doesn't match the native calling
convention, which makes it unusable when we want to call native code
from Java and pass arguments on the stack.
So far, the ARM calling convention mismatch hasn't been an issue
because we've never passed more arguments from Java to native code
than would fit in registers. However, we must now pass an extra
argument (the thread pointer) to e.g. divideLong so it can throw an
exception on divide by zero, which means the last argument must be
passed on the stack. This will clobber the linkage area we've been
using to hold the frame pointer, so we need to stop using it.
One solution would be to use the same convention on ARM as we do on
x86, but this would introduce the same overhead of making a register
unavailable for general use and extra code at method entry and exit.
Instead, this commit removes the need for a frame pointer. Unwinding
involves consulting a map of instruction offsets to frame sizes which
is generated at compile time. This is necessary because stack trace
creation can happen at any time due to Thread.getStackTrace being
called by another thread, and the frame size varies during the
execution of a method.
So far, only x86(_64) is working, and continuations and tail call
optimization are probably broken. More to come.
2011-01-16 19:05:05 -07:00
|
|
|
stack(0)
|
2010-12-27 15:55:23 -07:00
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void unwind() {
|
|
|
|
void* stack = this->stack;
|
|
|
|
this->stack = 0;
|
|
|
|
expect(t->m->system, stack);
|
2011-01-27 21:06:01 -07:00
|
|
|
vmJump(voidPointer(vmRun_returnAddress), 0, stack, t, 0, 0);
|
2010-12-27 15:55:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void* stack;
|
|
|
|
};
|
|
|
|
|
2007-07-28 15:28:25 -06:00
|
|
|
class Runnable: public System::Runnable {
|
|
|
|
public:
|
|
|
|
Runnable(Thread* t): t(t) { }
|
|
|
|
|
|
|
|
virtual void attach(System::Thread* st) {
|
|
|
|
t->systemThread = st;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void run() {
|
2007-10-23 16:21:28 -06:00
|
|
|
enterActiveState(t);
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
vm::run(t, runThread, 0);
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (t->exception and reinterpret_cast<object>(t->exception) != root(t, Machine::Shutdown)) {
|
2010-12-27 15:55:23 -07:00
|
|
|
printTrace(t, t->exception);
|
2007-07-28 15:28:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
t->exit();
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
virtual bool interrupted();
|
2007-07-28 15:28:25 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
virtual void setInterrupted(bool v);
|
2007-07-28 15:28:25 -06:00
|
|
|
|
|
|
|
Thread* t;
|
|
|
|
};
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
Thread(Machine* m, GcThread* javaThread, Thread* parent);
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2007-10-25 16:06:05 -06:00
|
|
|
void init();
|
2007-07-07 12:09:16 -06:00
|
|
|
void exit();
|
2007-07-06 09:24:06 -06:00
|
|
|
void dispose();
|
|
|
|
|
|
|
|
JNIEnvVTable* vtable;
|
2007-09-23 19:39:03 -06:00
|
|
|
Machine* m;
|
2007-07-07 12:09:16 -06:00
|
|
|
Thread* parent;
|
|
|
|
Thread* peer;
|
2007-07-06 09:24:06 -06:00
|
|
|
Thread* child;
|
2010-02-04 17:56:21 -07:00
|
|
|
Thread* waitNext;
|
2007-07-06 09:24:06 -06:00
|
|
|
State state;
|
2007-09-07 17:20:21 -06:00
|
|
|
unsigned criticalLevel;
|
2007-07-07 12:09:16 -06:00
|
|
|
System::Thread* systemThread;
|
2010-02-04 17:56:21 -07:00
|
|
|
System::Monitor* lock;
|
2014-06-28 17:24:24 -06:00
|
|
|
GcThread* javaThread;
|
|
|
|
GcThrowable* exception;
|
2007-07-06 09:24:06 -06:00
|
|
|
unsigned heapIndex;
|
2007-08-22 20:24:25 -06:00
|
|
|
unsigned heapOffset;
|
2007-07-06 09:24:06 -06:00
|
|
|
Protector* protector;
|
2009-07-20 14:12:38 -06:00
|
|
|
ClassInitStack* classInitStack;
|
2014-06-23 17:17:13 -06:00
|
|
|
LibraryLoadStack* libraryLoadStack;
|
2010-12-27 15:55:23 -07:00
|
|
|
Resource* resource;
|
|
|
|
Checkpoint* checkpoint;
|
2007-07-28 15:28:25 -06:00
|
|
|
Runnable runnable;
|
2007-09-06 18:21:52 -06:00
|
|
|
uintptr_t* defaultHeap;
|
2007-09-12 21:15:16 -06:00
|
|
|
uintptr_t* heap;
|
2010-06-19 16:40:21 -06:00
|
|
|
uintptr_t backupHeap[ThreadBackupHeapSizeInWords];
|
2008-04-09 13:08:13 -06:00
|
|
|
unsigned backupHeapIndex;
|
2010-09-14 10:49:41 -06:00
|
|
|
unsigned flags;
|
2007-07-06 09:24:06 -06:00
|
|
|
};
|
|
|
|
|
2014-06-26 18:17:46 -06:00
|
|
|
class GcJfield;
|
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
class Classpath {
|
|
|
|
public:
|
|
|
|
virtual object
|
2014-05-28 22:17:25 -06:00
|
|
|
makeJclass(Thread* t, GcClass* class_) = 0;
|
2010-09-10 15:05:29 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
virtual GcString*
|
2010-09-10 15:05:29 -06:00
|
|
|
makeString(Thread* t, object array, int32_t offset, int32_t length) = 0;
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
virtual GcThread*
|
2010-09-10 15:05:29 -06:00
|
|
|
makeThread(Thread* t, Thread* parent) = 0;
|
|
|
|
|
2012-12-19 12:39:33 -07:00
|
|
|
virtual object
|
2014-05-28 22:17:25 -06:00
|
|
|
makeJMethod(Thread* t, GcMethod* vmMethod) = 0;
|
2012-12-19 12:39:33 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
virtual GcMethod*
|
2012-12-19 12:39:33 -07:00
|
|
|
getVMMethod(Thread* t, object jmethod) = 0;
|
|
|
|
|
|
|
|
virtual object
|
2014-06-28 17:24:24 -06:00
|
|
|
makeJField(Thread* t, GcField* vmField) = 0;
|
2012-12-19 12:39:33 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
virtual GcField*
|
2014-06-26 18:17:46 -06:00
|
|
|
getVMField(Thread* t, GcJfield* jfield) = 0;
|
2012-12-19 12:39:33 -07:00
|
|
|
|
2012-05-02 11:41:36 -06:00
|
|
|
virtual void
|
|
|
|
clearInterrupted(Thread* t) = 0;
|
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
virtual void
|
|
|
|
runThread(Thread* t) = 0;
|
|
|
|
|
2011-04-10 11:26:44 -06:00
|
|
|
virtual void
|
2014-05-28 22:17:25 -06:00
|
|
|
resolveNative(Thread* t, GcMethod* method) = 0;
|
2011-04-10 11:26:44 -06:00
|
|
|
|
2013-03-15 13:28:01 -06:00
|
|
|
virtual void
|
|
|
|
interceptMethods(Thread* t) = 0;
|
|
|
|
|
2013-02-22 14:41:24 -07:00
|
|
|
virtual void
|
|
|
|
preBoot(Thread* t) = 0;
|
|
|
|
|
2014-04-11 19:38:11 -06:00
|
|
|
virtual bool mayInitClasses() = 0;
|
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
virtual void
|
|
|
|
boot(Thread* t) = 0;
|
|
|
|
|
2010-09-20 17:31:23 -06:00
|
|
|
virtual const char*
|
|
|
|
bootClasspath() = 0;
|
|
|
|
|
2013-02-21 15:37:17 -07:00
|
|
|
virtual object
|
|
|
|
makeDirectByteBuffer(Thread* t, void* p, jlong capacity) = 0;
|
|
|
|
|
|
|
|
virtual void*
|
|
|
|
getDirectBufferAddress(Thread* t, object buffer) = 0;
|
|
|
|
|
|
|
|
virtual int64_t
|
|
|
|
getDirectBufferCapacity(Thread* t, object buffer) = 0;
|
|
|
|
|
2013-03-05 15:43:49 -07:00
|
|
|
virtual bool
|
2014-06-28 17:24:24 -06:00
|
|
|
canTailCall(Thread* t, GcMethod* caller, GcByteArray* calleeClassName,
|
|
|
|
GcByteArray* calleeMethodName, GcByteArray* calleeMethodSpec) = 0;
|
2013-03-05 15:43:49 -07:00
|
|
|
|
2014-06-20 22:16:33 -06:00
|
|
|
virtual GcClassLoader* libraryClassLoader(Thread* t, GcMethod* caller) = 0;
|
2014-07-01 10:18:45 -06:00
|
|
|
|
2013-04-19 13:00:47 -06:00
|
|
|
virtual void
|
|
|
|
shutDown(Thread* t) = 0;
|
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
virtual void
|
|
|
|
dispose() = 0;
|
|
|
|
};
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
|
|
|
template <class T>
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class ThreadRuntimeArray: public Thread::AutoResource {
|
2010-12-27 15:55:23 -07:00
|
|
|
public:
|
2011-02-02 08:15:07 -07:00
|
|
|
ThreadRuntimeArray(Thread* t, unsigned size):
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
AutoResource(t),
|
2010-12-27 15:55:23 -07:00
|
|
|
body(static_cast<T*>(t->m->heap->allocate(size * sizeof(T)))),
|
|
|
|
size(size)
|
|
|
|
{ }
|
|
|
|
|
2011-02-02 08:15:07 -07:00
|
|
|
~ThreadRuntimeArray() {
|
2010-12-27 15:55:23 -07:00
|
|
|
t->m->heap->free(body, size * sizeof(T));
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void release() {
|
2011-02-02 08:15:07 -07:00
|
|
|
ThreadRuntimeArray::~ThreadRuntimeArray();
|
2010-12-27 15:55:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
T* body;
|
|
|
|
unsigned size;
|
|
|
|
};
|
|
|
|
|
|
|
|
# define THREAD_RUNTIME_ARRAY(thread, type, name, size) \
|
2011-02-02 08:15:07 -07:00
|
|
|
ThreadRuntimeArray<type> name(thread, size);
|
2010-12-27 15:55:23 -07:00
|
|
|
|
|
|
|
#else // not _MSC_VER
|
|
|
|
|
2013-02-20 17:20:17 -07:00
|
|
|
# define THREAD_RUNTIME_ARRAY(thread, type, name, size) \
|
|
|
|
type name##_body[size];
|
2010-12-27 15:55:23 -07:00
|
|
|
|
|
|
|
#endif // not _MSC_VER
|
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
Classpath*
|
2010-11-05 13:18:28 -06:00
|
|
|
makeClasspath(System* system, Allocator* allocator, const char* javaHome,
|
|
|
|
const char* embedPrefix);
|
2010-09-10 15:05:29 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
typedef uint64_t (JNICALL *FastNativeFunction)(Thread*, GcMethod*, uintptr_t*);
|
2009-05-03 14:57:11 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
2007-07-06 09:24:06 -06:00
|
|
|
objectClass(Thread*, object o)
|
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
return reinterpret_cast<GcClass*>(maskAlignedPointer(fieldAtOffset<object>(o, 0)));
|
|
|
|
}
|
|
|
|
inline GcClass*
|
|
|
|
objectClass(Thread*, GcObject* o)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<GcClass*>(maskAlignedPointer(fieldAtOffset<object>(o, 0)));
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
2012-03-14 12:36:42 -06:00
|
|
|
inline unsigned
|
|
|
|
stackSizeInWords(Thread* t)
|
|
|
|
{
|
|
|
|
return t->m->stackSizeInBytes / BytesPerWord;
|
|
|
|
}
|
|
|
|
|
2007-07-15 19:03:02 -06:00
|
|
|
void
|
|
|
|
enter(Thread* t, Thread::State state);
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2007-10-23 16:21:28 -06:00
|
|
|
inline void
|
|
|
|
enterActiveState(Thread* t)
|
|
|
|
{
|
|
|
|
enter(t, Thread::ActiveState);
|
|
|
|
}
|
|
|
|
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class StateResource: public Thread::AutoResource {
|
2007-07-06 09:24:06 -06:00
|
|
|
public:
|
2010-12-27 15:55:23 -07:00
|
|
|
StateResource(Thread* t, Thread::State state):
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
AutoResource(t), oldState(t->state)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
2007-07-06 09:24:06 -06:00
|
|
|
enter(t, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
~StateResource() { enter(t, oldState); }
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
virtual void release() {
|
|
|
|
this->StateResource::~StateResource();
|
|
|
|
}
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
private:
|
|
|
|
Thread::State oldState;
|
|
|
|
};
|
|
|
|
|
2007-09-29 21:33:38 -06:00
|
|
|
inline void
|
|
|
|
dispose(Thread* t, Reference* r)
|
|
|
|
{
|
|
|
|
*(r->handle) = r->next;
|
2007-10-08 15:41:41 -06:00
|
|
|
if (r->next) {
|
|
|
|
r->next->handle = r->handle;
|
|
|
|
}
|
2008-04-13 12:15:04 -06:00
|
|
|
t->m->heap->free(r, sizeof(*r));
|
2007-09-29 21:33:38 -06:00
|
|
|
}
|
|
|
|
|
2009-12-16 19:16:51 -07:00
|
|
|
inline void
|
|
|
|
acquire(Thread*, Reference* r)
|
|
|
|
{
|
|
|
|
++ r->count;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
release(Thread* t, Reference* r)
|
|
|
|
{
|
|
|
|
if ((-- r->count) == 0) {
|
|
|
|
dispose(t, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-15 19:03:02 -06:00
|
|
|
void
|
2013-03-07 20:17:05 -07:00
|
|
|
collect(Thread* t, Heap::CollectionType type, int pendingAllocation = 0);
|
2007-07-15 19:03:02 -06:00
|
|
|
|
2009-08-19 14:27:03 -06:00
|
|
|
void
|
|
|
|
shutDown(Thread* t);
|
|
|
|
|
2007-07-15 19:03:02 -06:00
|
|
|
#ifdef VM_STRESS
|
|
|
|
|
|
|
|
inline void
|
|
|
|
stress(Thread* t)
|
|
|
|
{
|
2010-12-09 22:17:57 -07:00
|
|
|
if ((not t->m->unsafe)
|
|
|
|
and (t->flags & (Thread::StressFlag | Thread::TracingFlag)) == 0
|
2007-07-16 18:23:23 -06:00
|
|
|
and t->state != Thread::NoState
|
|
|
|
and t->state != Thread::IdleState)
|
|
|
|
{
|
2010-09-16 19:43:27 -06:00
|
|
|
atomicOr(&(t->flags), Thread::StressFlag);
|
2007-07-15 19:03:02 -06:00
|
|
|
|
|
|
|
# ifdef VM_STRESS_MAJOR
|
2010-09-16 19:43:27 -06:00
|
|
|
collect(t, Heap::MajorCollection);
|
2007-07-15 19:03:02 -06:00
|
|
|
# else // not VM_STRESS_MAJOR
|
2010-09-16 19:43:27 -06:00
|
|
|
collect(t, Heap::MinorCollection);
|
2007-07-15 19:03:02 -06:00
|
|
|
# endif // not VM_STRESS_MAJOR
|
|
|
|
|
2010-09-16 19:43:27 -06:00
|
|
|
atomicAnd(&(t->flags), ~Thread::StressFlag);
|
2007-07-15 19:03:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // not VM_STRESS
|
|
|
|
|
2007-08-01 17:48:36 -06:00
|
|
|
inline void
|
|
|
|
acquire(Thread* t, System::Monitor* m)
|
|
|
|
{
|
|
|
|
if (not m->tryAcquire(t->systemThread)) {
|
|
|
|
ENTER(t, Thread::IdleState);
|
|
|
|
m->acquire(t->systemThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
stress(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
release(Thread* t, System::Monitor* m)
|
|
|
|
{
|
|
|
|
m->release(t->systemThread);
|
|
|
|
}
|
|
|
|
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
class MonitorResource: public Thread::AutoResource {
|
2007-07-06 09:24:06 -06:00
|
|
|
public:
|
2010-12-27 15:55:23 -07:00
|
|
|
MonitorResource(Thread* t, System::Monitor* m):
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
AutoResource(t), m(m)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
2007-08-01 17:48:36 -06:00
|
|
|
acquire(t, m);
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
2007-08-01 17:48:36 -06:00
|
|
|
~MonitorResource() {
|
2010-12-27 15:55:23 -07:00
|
|
|
vm::release(t, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void release() {
|
|
|
|
this->MonitorResource::~MonitorResource();
|
2007-08-01 17:48:36 -06:00
|
|
|
}
|
2007-07-06 09:24:06 -06:00
|
|
|
|
|
|
|
private:
|
|
|
|
System::Monitor* m;
|
|
|
|
};
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
class RawMonitorResource: public Thread::Resource {
|
2007-07-06 09:24:06 -06:00
|
|
|
public:
|
2010-12-27 15:55:23 -07:00
|
|
|
RawMonitorResource(Thread* t, System::Monitor* m):
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
Resource(t, t->resource), m(m)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
2007-08-13 18:37:00 -06:00
|
|
|
m->acquire(t->systemThread);
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
2007-08-01 17:48:36 -06:00
|
|
|
~RawMonitorResource() {
|
fix crash on exit due to order of operations bug in ~RawMonitorResource
The problem (which we've only been able to reproduce consistently with
the openjdk-src process=interpret build on Linux virtual machines) was
a race condition during VM shutdown. Thread "A" would exit, see there
were other threads still running and thus enter ZombieState, which
involves acquiring and releasing a lock using RawMonitorResource.
Then the last thread (thread "B") would exit, wait for thread "A" to
release the lock, then shut down the VM, freeing all memory. However,
thread "A" writes to its Thread object one last time after releasing
the lock (in ~Resource, the destructor of the superclass of
RawMonitorResource, which sets Thread::resource). If thread "B" frees
that Thread before ~Resource runs, we end up writing to freed memory.
Thus, we need to update Thread::resource before releasing the lock.
Apparently C++ destructors run in order from most derived to least
derived, which is not what we want here. My solution to split
Resource into two classes, one that has no destructor and another that
extends it (called AutoResource) which does hafe a destructor. Now
all the classes which used to extend Resource extend AutoResource,
except for RawMonitorResource, which extends Resource directly so it
can control the order of operations.
2014-05-10 23:06:29 -06:00
|
|
|
t->resource = next;
|
2010-12-27 15:55:23 -07:00
|
|
|
vm::release(t, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void release() {
|
|
|
|
this->RawMonitorResource::~RawMonitorResource();
|
2007-08-01 17:48:36 -06:00
|
|
|
}
|
2007-07-06 09:24:06 -06:00
|
|
|
|
|
|
|
private:
|
|
|
|
System::Monitor* m;
|
|
|
|
};
|
|
|
|
|
2013-02-11 18:51:39 -07:00
|
|
|
inline Aborter* getAborter(Thread* t) {
|
|
|
|
return t->m->system;
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
2010-06-19 16:40:21 -06:00
|
|
|
inline bool
|
2008-04-09 13:08:13 -06:00
|
|
|
ensure(Thread* t, unsigned sizeInBytes)
|
|
|
|
{
|
2013-02-10 18:06:15 -07:00
|
|
|
if (t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord)
|
2008-11-22 14:47:18 -07:00
|
|
|
> ThreadHeapSizeInWords)
|
2008-04-09 13:08:13 -06:00
|
|
|
{
|
2010-06-19 16:40:21 -06:00
|
|
|
if (sizeInBytes <= ThreadBackupHeapSizeInBytes) {
|
2010-09-14 10:49:41 -06:00
|
|
|
expect(t, (t->flags & Thread::UseBackupHeapFlag) == 0);
|
2009-03-03 20:05:48 -07:00
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
atomicOr(&(t->flags), Thread::UseBackupHeapFlag);
|
2009-03-03 20:05:48 -07:00
|
|
|
|
2010-06-19 16:40:21 -06:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return true;
|
2008-04-09 13:08:13 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-27 19:54:30 -06:00
|
|
|
object
|
2008-01-09 18:20:36 -07:00
|
|
|
allocate2(Thread* t, unsigned sizeInBytes, bool objectMask);
|
|
|
|
|
|
|
|
object
|
|
|
|
allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type,
|
2008-04-13 12:15:04 -06:00
|
|
|
unsigned sizeInBytes, bool objectMask);
|
2007-07-06 09:24:06 -06:00
|
|
|
|
|
|
|
inline object
|
|
|
|
allocateSmall(Thread* t, unsigned sizeInBytes)
|
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord)
|
2009-07-17 19:37:46 -06:00
|
|
|
<= ThreadHeapSizeInWords);
|
|
|
|
|
2007-09-06 18:21:52 -06:00
|
|
|
object o = reinterpret_cast<object>(t->heap + t->heapIndex);
|
2013-02-10 18:06:15 -07:00
|
|
|
t->heapIndex += ceilingDivide(sizeInBytes, BytesPerWord);
|
2007-07-06 09:24:06 -06:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline object
|
2007-10-27 19:54:30 -06:00
|
|
|
allocate(Thread* t, unsigned sizeInBytes, bool objectMask)
|
2007-07-06 09:24:06 -06:00
|
|
|
{
|
2007-07-15 19:03:02 -06:00
|
|
|
stress(t);
|
|
|
|
|
2013-02-10 18:06:15 -07:00
|
|
|
if (UNLIKELY(t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord)
|
2008-11-22 14:47:18 -07:00
|
|
|
> ThreadHeapSizeInWords
|
2007-09-23 19:39:03 -06:00
|
|
|
or t->m->exclusive))
|
2007-07-06 09:24:06 -06:00
|
|
|
{
|
2008-01-09 18:20:36 -07:00
|
|
|
return allocate2(t, sizeInBytes, objectMask);
|
2007-07-06 09:24:06 -06:00
|
|
|
} else {
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, t->criticalLevel == 0);
|
2007-07-06 09:24:06 -06:00
|
|
|
return allocateSmall(t, sizeInBytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-28 13:14:53 -06:00
|
|
|
inline void
|
|
|
|
mark(Thread* t, object o, unsigned offset, unsigned count)
|
|
|
|
{
|
2009-11-19 18:13:00 -07:00
|
|
|
t->m->heap->mark(o, offset / BytesPerWord, count);
|
2007-10-28 13:14:53 -06:00
|
|
|
}
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2007-10-28 13:14:53 -06:00
|
|
|
inline void
|
|
|
|
mark(Thread* t, object o, unsigned offset)
|
|
|
|
{
|
2009-11-19 18:13:00 -07:00
|
|
|
t->m->heap->mark(o, offset / BytesPerWord, 1);
|
2007-10-28 13:14:53 -06:00
|
|
|
}
|
2007-10-27 19:54:30 -06:00
|
|
|
|
2008-07-05 14:21:13 -06:00
|
|
|
inline void
|
2007-10-22 11:22:30 -06:00
|
|
|
set(Thread* t, object target, unsigned offset, object value)
|
2007-07-16 17:58:37 -06:00
|
|
|
{
|
2013-02-10 17:38:51 -07:00
|
|
|
fieldAtOffset<object>(target, offset) = value;
|
2007-10-22 11:22:30 -06:00
|
|
|
mark(t, target, offset);
|
2007-07-16 17:58:37 -06:00
|
|
|
}
|
|
|
|
|
2014-06-28 14:41:27 -06:00
|
|
|
inline void
|
|
|
|
set(Thread* t, GcObject* target, unsigned offset, GcObject* value)
|
|
|
|
{
|
|
|
|
fieldAtOffset<GcObject*>(target, offset) = value;
|
|
|
|
mark(t, reinterpret_cast<object>(target), offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
setObject(Thread* t, GcObject* target, unsigned offset, GcObject* value)
|
|
|
|
{
|
|
|
|
set(t, target, offset, value);
|
|
|
|
}
|
|
|
|
|
2007-07-14 11:31:01 -06:00
|
|
|
inline void
|
2014-05-28 22:17:25 -06:00
|
|
|
setObjectClass(Thread*, object o, GcClass* c)
|
2007-07-14 11:31:01 -06:00
|
|
|
{
|
2013-02-10 17:38:51 -07:00
|
|
|
fieldAtOffset<object>(o, 0)
|
2008-01-09 18:20:36 -07:00
|
|
|
= reinterpret_cast<object>
|
2014-05-28 22:17:25 -06:00
|
|
|
(reinterpret_cast<intptr_alias_t>(c)
|
2011-02-14 11:47:59 -07:00
|
|
|
| (reinterpret_cast<intptr_alias_t>
|
2013-02-10 17:38:51 -07:00
|
|
|
(fieldAtOffset<object>(o, 0)) & (~PointerMask)));
|
2007-07-14 11:31:01 -06:00
|
|
|
}
|
|
|
|
|
2008-11-11 08:20:49 -07:00
|
|
|
inline const char*
|
|
|
|
findProperty(Machine* m, const char* name)
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < m->propertyCount; ++i) {
|
|
|
|
const char* p = m->properties[i];
|
|
|
|
const char* n = name;
|
|
|
|
while (*p and *p != '=' and *n and *p == *n) {
|
|
|
|
++ p;
|
|
|
|
++ n;
|
|
|
|
}
|
|
|
|
if (*p == '=' and *n == 0) {
|
|
|
|
return p + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline const char*
|
|
|
|
findProperty(Thread* t, const char* name)
|
|
|
|
{
|
|
|
|
return findProperty(t->m, name);
|
|
|
|
}
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
object&
|
2014-06-28 17:24:24 -06:00
|
|
|
arrayBodyUnsafe(Thread*, GcArray*, unsigned);
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2007-11-04 14:15:28 -07:00
|
|
|
bool
|
2014-05-28 22:17:25 -06:00
|
|
|
instanceOf(Thread* t, GcClass* class_, object o);
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
T* GcObject::as(Thread* t UNUSED)
|
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if(this == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t,
|
2014-05-28 22:17:25 -06:00
|
|
|
t->m->unsafe
|
|
|
|
|| instanceOf(t, reinterpret_cast<GcClass*>(arrayBodyUnsafe(t, t->m->types, T::Type)), reinterpret_cast<object>(this)));
|
|
|
|
return static_cast<T*>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
bool GcObject::isa(Thread* t)
|
|
|
|
{
|
|
|
|
return instanceOf(t, reinterpret_cast<GcClass*>(arrayBodyUnsafe(t, t->m->types, T::Type)), reinterpret_cast<object>(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
T* cast(Thread* t UNUSED, object o)
|
|
|
|
{
|
|
|
|
if(o == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t,
|
2014-05-28 22:17:25 -06:00
|
|
|
t->m->unsafe || instanceOf(t,
|
|
|
|
reinterpret_cast<GcClass*>(arrayBodyUnsafe(
|
|
|
|
t, t->m->types, T::Type)),
|
|
|
|
o));
|
|
|
|
return reinterpret_cast<T*>(o);
|
|
|
|
}
|
2007-11-04 14:15:28 -07:00
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
#include "type-declarations.cpp"
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
|
2014-06-20 22:16:33 -06:00
|
|
|
inline object&
|
2014-06-28 17:24:24 -06:00
|
|
|
arrayBodyUnsafe(Thread*, GcArray* a, unsigned index)
|
2014-06-20 22:16:33 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return a->body()[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Thread::Runnable::interrupted() {
|
|
|
|
return t->javaThread and t->javaThread->interrupted();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Thread::Runnable::setInterrupted(bool v) {
|
|
|
|
t->javaThread->interrupted() = v;
|
2014-06-20 22:16:33 -06:00
|
|
|
}
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
inline uint64_t
|
2011-02-01 17:45:43 -07:00
|
|
|
runRaw(Thread* t,
|
|
|
|
uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
|
|
|
Thread::RunCheckpoint checkpoint(t);
|
|
|
|
return vmRun(function, arguments, &checkpoint);
|
|
|
|
}
|
|
|
|
|
2011-02-01 17:45:43 -07:00
|
|
|
inline uint64_t
|
|
|
|
run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments)
|
|
|
|
{
|
|
|
|
ENTER(t, Thread::ActiveState);
|
|
|
|
return runRaw(t, function, arguments);
|
|
|
|
}
|
|
|
|
|
2010-12-01 15:42:46 -07:00
|
|
|
inline void
|
|
|
|
runJavaThread(Thread* t)
|
|
|
|
{
|
|
|
|
t->m->classpath->runThread(t);
|
|
|
|
}
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
void
|
|
|
|
runFinalizeThread(Thread* t);
|
|
|
|
|
|
|
|
inline uint64_t
|
|
|
|
runThread(Thread* t, uintptr_t*)
|
|
|
|
{
|
|
|
|
t->m->localThread->set(t);
|
|
|
|
|
|
|
|
checkDaemon(t);
|
|
|
|
|
|
|
|
if (t == t->m->finalizeThread) {
|
|
|
|
runFinalizeThread(t);
|
|
|
|
} else if (t->javaThread) {
|
|
|
|
runJavaThread(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
inline bool
|
|
|
|
startThread(Thread* t, Thread* p)
|
|
|
|
{
|
2012-02-03 17:20:20 -07:00
|
|
|
p->flags |= Thread::JoinFlag;
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
return t->m->system->success(t->m->system->start(&(p->runnable)));
|
|
|
|
}
|
|
|
|
|
2010-12-20 19:00:23 -07:00
|
|
|
inline void
|
|
|
|
addThread(Thread* t, Thread* p)
|
|
|
|
{
|
|
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, p->state == Thread::NoState);
|
2013-07-14 15:59:43 -06:00
|
|
|
expect(t, t->state == Thread::ActiveState || t->state == Thread::ExclusiveState || t->state == Thread::NoState);
|
2010-12-20 19:00:23 -07:00
|
|
|
|
|
|
|
p->state = Thread::IdleState;
|
2012-02-03 12:00:02 -07:00
|
|
|
++ t->m->threadCount;
|
2010-12-20 19:00:23 -07:00
|
|
|
++ t->m->liveCount;
|
|
|
|
|
|
|
|
p->peer = p->parent->child;
|
|
|
|
p->parent->child = p;
|
2011-02-11 21:13:11 -07:00
|
|
|
|
2011-02-15 08:47:48 -07:00
|
|
|
if (p->javaThread) {
|
2014-06-28 17:24:24 -06:00
|
|
|
p->javaThread->peer() = reinterpret_cast<jlong>(p);
|
2011-02-15 08:47:48 -07:00
|
|
|
}
|
2010-12-20 19:00:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
removeThread(Thread* t, Thread* p)
|
|
|
|
{
|
|
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, p->state == Thread::IdleState);
|
2010-12-20 19:00:23 -07:00
|
|
|
|
|
|
|
-- t->m->liveCount;
|
2012-02-03 12:00:02 -07:00
|
|
|
-- t->m->threadCount;
|
2010-12-20 19:00:23 -07:00
|
|
|
|
|
|
|
t->m->stateLock->notifyAll(t->systemThread);
|
|
|
|
|
|
|
|
p->parent->child = p->peer;
|
2011-02-11 21:13:11 -07:00
|
|
|
|
2011-02-15 08:47:48 -07:00
|
|
|
if (p->javaThread) {
|
2014-06-28 17:24:24 -06:00
|
|
|
p->javaThread->peer() = 0;
|
2011-02-15 08:47:48 -07:00
|
|
|
}
|
2012-02-03 12:00:02 -07:00
|
|
|
|
|
|
|
p->dispose();
|
2010-12-20 19:00:23 -07:00
|
|
|
}
|
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
inline Thread*
|
2014-06-28 13:16:26 -06:00
|
|
|
startThread(Thread* t, GcThread* javaThread)
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
{
|
2012-02-03 12:00:02 -07:00
|
|
|
{ PROTECT(t, javaThread);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2012-02-03 12:00:02 -07:00
|
|
|
stress(t);
|
|
|
|
|
|
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2012-02-03 12:00:02 -07:00
|
|
|
if (t->m->threadCount > t->m->liveCount + ZombieCollectionThreshold) {
|
|
|
|
collect(t, Heap::MinorCollection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
Thread* p = t->m->processor->makeThread(t->m, javaThread, t);
|
|
|
|
|
2010-12-20 19:00:23 -07:00
|
|
|
addThread(t, p);
|
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
if (startThread(t, p)) {
|
|
|
|
return p;
|
|
|
|
} else {
|
2010-12-20 19:00:23 -07:00
|
|
|
removeThread(t, p);
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
registerDaemon(Thread* t)
|
|
|
|
{
|
|
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
|
|
|
|
atomicOr(&(t->flags), Thread::DaemonFlag);
|
|
|
|
|
|
|
|
++ t->m->daemonCount;
|
2014-05-28 22:17:25 -06:00
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
t->m->stateLock->notifyAll(t->systemThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
checkDaemon(Thread* t)
|
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if (t->javaThread->daemon()) {
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
registerDaemon(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
inline uint64_t
|
|
|
|
initAttachedThread(Thread* t, uintptr_t* arguments)
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
{
|
2010-12-27 15:55:23 -07:00
|
|
|
bool daemon = arguments[0];
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
t->javaThread = t->m->classpath->makeThread(t, t->m->rootThread);
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
t->javaThread->peer() = reinterpret_cast<jlong>(t);
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
|
|
|
|
if (daemon) {
|
2014-06-28 17:24:24 -06:00
|
|
|
t->javaThread->daemon() = true;
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
|
|
|
|
registerDaemon(t);
|
|
|
|
}
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
t->m->localThread->set(t);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Thread*
|
|
|
|
attachThread(Machine* m, bool daemon)
|
|
|
|
{
|
|
|
|
Thread* t = m->processor->makeThread(m, 0, m->rootThread);
|
|
|
|
m->system->attach(&(t->runnable));
|
|
|
|
|
|
|
|
addThread(t, t);
|
|
|
|
|
|
|
|
enter(t, Thread::ActiveState);
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
|
2011-02-15 21:44:27 -07:00
|
|
|
uintptr_t arguments[] = { daemon };
|
|
|
|
|
2010-12-27 15:55:23 -07:00
|
|
|
if (run(t, initAttachedThread, arguments)) {
|
|
|
|
enter(t, Thread::IdleState);
|
|
|
|
return t;
|
|
|
|
} else {
|
|
|
|
t->exit();
|
|
|
|
return 0;
|
|
|
|
}
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
}
|
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
inline object&
|
|
|
|
root(Thread* t, Machine::Root root)
|
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return t->m->roots->body()[root];
|
2010-09-14 10:49:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
setRoot(Thread* t, Machine::Root root, object value)
|
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
set(t, reinterpret_cast<object>(t->m->roots), ArrayBody + (root * BytesPerWord), value);
|
2010-09-14 10:49:41 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
|
|
|
type(Thread* t, Gc::Type type)
|
2010-09-14 10:49:41 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcClass>(t, t->m->types->body()[type]);
|
2010-09-14 10:49:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-05-28 22:17:25 -06:00
|
|
|
setType(Thread* t, Gc::Type type, GcClass* value)
|
2010-09-14 10:49:41 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
set(t, t->m->types, ArrayBody + (type * BytesPerWord), value);
|
2010-09-14 10:49:41 -06:00
|
|
|
}
|
|
|
|
|
2007-10-22 11:22:30 -06:00
|
|
|
inline bool
|
|
|
|
objectFixed(Thread*, object o)
|
|
|
|
{
|
2011-02-14 11:47:59 -07:00
|
|
|
return (alias(o, 0) & (~PointerMask)) == FixedMark;
|
2007-10-22 11:22:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
objectExtended(Thread*, object o)
|
|
|
|
{
|
2011-02-14 11:47:59 -07:00
|
|
|
return (alias(o, 0) & (~PointerMask)) == ExtendedMark;
|
2007-10-22 11:22:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
hashTaken(Thread*, object o)
|
|
|
|
{
|
2011-02-14 11:47:59 -07:00
|
|
|
return (alias(o, 0) & (~PointerMask)) == HashTakenMark;
|
2007-10-22 11:22:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
2014-05-28 22:17:25 -06:00
|
|
|
baseSize(Thread* t UNUSED, object o, GcClass* class_)
|
2007-10-22 11:22:30 -06:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, class_->fixedSize() >= BytesPerWord);
|
2012-02-27 18:16:01 -07:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
return ceilingDivide(class_->fixedSize(), BytesPerWord)
|
|
|
|
+ ceilingDivide(class_->arrayElementSize()
|
|
|
|
* fieldAtOffset<uintptr_t>(o, class_->fixedSize() - BytesPerWord),
|
2007-10-22 11:22:30 -06:00
|
|
|
BytesPerWord);
|
|
|
|
}
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
object
|
2007-11-25 16:00:55 -07:00
|
|
|
makeTrace(Thread* t, Processor::StackWalker* walker);
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2007-11-25 16:00:55 -07:00
|
|
|
object
|
2008-04-09 13:08:13 -06:00
|
|
|
makeTrace(Thread* t, Thread* target);
|
|
|
|
|
|
|
|
inline object
|
|
|
|
makeTrace(Thread* t)
|
|
|
|
{
|
|
|
|
return makeTrace(t, t);
|
|
|
|
}
|
2007-07-06 09:24:06 -06:00
|
|
|
|
2008-07-05 14:21:13 -06:00
|
|
|
inline object
|
2014-05-28 22:17:25 -06:00
|
|
|
makeNew(Thread* t, GcClass* class_)
|
2007-09-27 16:20:54 -06:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, t->state == Thread::NoState or t->state == Thread::ActiveState);
|
2008-01-17 18:27:44 -07:00
|
|
|
|
2007-09-27 16:20:54 -06:00
|
|
|
PROTECT(t, class_);
|
2014-05-28 22:17:25 -06:00
|
|
|
unsigned sizeInBytes = pad(class_->fixedSize());
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, sizeInBytes);
|
2014-05-28 22:17:25 -06:00
|
|
|
object instance = allocate(t, sizeInBytes, class_->objectMask());
|
2007-10-27 19:54:30 -06:00
|
|
|
setObjectClass(t, instance, class_);
|
2007-09-27 16:20:54 -06:00
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2009-07-21 18:57:55 -06:00
|
|
|
object
|
2014-05-28 22:17:25 -06:00
|
|
|
makeNewGeneral(Thread* t, GcClass* class_);
|
2007-09-27 16:20:54 -06:00
|
|
|
|
|
|
|
inline object
|
2014-05-28 22:17:25 -06:00
|
|
|
make(Thread* t, GcClass* class_)
|
2007-09-27 16:20:54 -06:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
if (UNLIKELY(class_->vmFlags()
|
2009-07-21 18:57:55 -06:00
|
|
|
& (WeakReferenceFlag | HasFinalizerFlag)))
|
|
|
|
{
|
|
|
|
return makeNewGeneral(t, class_);
|
2007-09-27 16:20:54 -06:00
|
|
|
} else {
|
|
|
|
return makeNew(t, class_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray*
|
2012-03-03 18:37:27 -07:00
|
|
|
makeByteArrayV(Thread* t, const char* format, va_list a, int size);
|
2010-12-27 15:55:23 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray*
|
2007-07-06 09:24:06 -06:00
|
|
|
makeByteArray(Thread* t, const char* format, ...);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcString*
|
2007-07-06 09:24:06 -06:00
|
|
|
makeString(Thread* t, const char* format, ...);
|
|
|
|
|
2012-06-11 23:30:22 +00:00
|
|
|
#ifndef HAVE_StringOffset
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline uint32_t GcString::length(Thread* t)
|
2012-06-11 23:30:22 +00:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcCharArray>(t, this->data())->length();
|
2012-06-11 23:30:22 +00:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline uint32_t GcString::offset(Thread*)
|
2012-06-11 23:30:22 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-23 15:22:10 -06:00
|
|
|
# ifndef HAVE_StringHash32
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcString*
|
2014-04-23 15:22:10 -06:00
|
|
|
makeString(Thread* t, object data, int32_t hash, int32_t)
|
|
|
|
{
|
|
|
|
return makeString(t, data, hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
# endif // not HAVE_StringHash32
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcString*
|
2014-06-26 18:17:46 -06:00
|
|
|
makeString(Thread* t, object odata, unsigned offset, unsigned length, unsigned)
|
2012-06-11 23:30:22 +00:00
|
|
|
{
|
2014-06-26 18:17:46 -06:00
|
|
|
GcCharArray* data = cast<GcCharArray>(t, odata);
|
|
|
|
if (offset == 0 and length == data->length()) {
|
|
|
|
return makeString(t, reinterpret_cast<object>(data), 0, 0);
|
2012-06-11 23:30:22 +00:00
|
|
|
} else {
|
|
|
|
PROTECT(t, data);
|
|
|
|
|
2014-06-26 18:17:46 -06:00
|
|
|
GcCharArray* array = makeCharArray(t, length);
|
2012-06-11 23:30:22 +00:00
|
|
|
|
2014-06-26 18:17:46 -06:00
|
|
|
memcpy(array->body().begin(), &data->body()[offset],
|
2012-06-11 23:30:22 +00:00
|
|
|
length * 2);
|
|
|
|
|
2014-06-26 18:17:46 -06:00
|
|
|
return makeString(t, reinterpret_cast<object>(array), 0, 0);
|
2012-06-11 23:30:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // not HAVE_StringOffset
|
|
|
|
|
2010-04-20 10:03:07 -06:00
|
|
|
int
|
2014-06-28 17:24:24 -06:00
|
|
|
stringUTFLength(Thread* t, GcString* string, unsigned start, unsigned length);
|
2010-09-10 15:05:29 -06:00
|
|
|
|
|
|
|
inline int
|
2014-06-28 17:24:24 -06:00
|
|
|
stringUTFLength(Thread* t, GcString* string)
|
2010-09-10 15:05:29 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return stringUTFLength(t, string, 0, string->length(t));
|
2010-09-10 15:05:29 -06:00
|
|
|
}
|
2010-04-20 10:03:07 -06:00
|
|
|
|
2007-07-07 19:06:32 -06:00
|
|
|
void
|
2014-06-28 17:24:24 -06:00
|
|
|
stringChars(Thread* t, GcString* string, unsigned start, unsigned length,
|
2010-09-10 15:05:29 -06:00
|
|
|
char* chars);
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
stringChars(Thread* t, GcString* string, char* chars)
|
2010-09-10 15:05:29 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
stringChars(t, string, 0, string->length(t), chars);
|
2010-09-10 15:05:29 -06:00
|
|
|
}
|
2007-07-07 19:06:32 -06:00
|
|
|
|
2008-04-01 17:24:43 -06:00
|
|
|
void
|
2014-06-28 17:24:24 -06:00
|
|
|
stringChars(Thread* t, GcString* string, unsigned start, unsigned length,
|
2010-09-10 15:05:29 -06:00
|
|
|
uint16_t* chars);
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
stringChars(Thread* t, GcString* string, uint16_t* chars)
|
2010-09-10 15:05:29 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
stringChars(t, string, 0, string->length(t), chars);
|
2010-09-10 15:05:29 -06:00
|
|
|
}
|
2008-04-01 17:24:43 -06:00
|
|
|
|
2010-04-20 10:03:07 -06:00
|
|
|
void
|
2014-06-28 17:24:24 -06:00
|
|
|
stringUTFChars(Thread* t, GcString* string, unsigned start, unsigned length,
|
2010-09-10 15:05:29 -06:00
|
|
|
char* chars, unsigned charsLength);
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
stringUTFChars(Thread* t, GcString* string, char* chars, unsigned charsLength)
|
2010-09-10 15:05:29 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
stringUTFChars(t, string, 0, string->length(t), chars, charsLength);
|
2010-09-10 15:05:29 -06:00
|
|
|
}
|
2010-04-20 10:03:07 -06:00
|
|
|
|
2007-07-23 19:44:20 -06:00
|
|
|
bool
|
2014-05-28 22:17:25 -06:00
|
|
|
isAssignableFrom(Thread* t, GcClass* a, GcClass* b);
|
2007-07-23 19:44:20 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMethod*
|
2014-05-28 22:17:25 -06:00
|
|
|
classInitializer(Thread* t, GcClass* class_);
|
2007-07-28 10:10:13 -06:00
|
|
|
|
2007-09-12 18:21:37 -06:00
|
|
|
object
|
|
|
|
frameMethod(Thread* t, int frame);
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
inline uintptr_t&
|
2007-08-19 13:45:51 -06:00
|
|
|
extendedWord(Thread* t UNUSED, object o, unsigned baseSize)
|
2007-07-06 09:24:06 -06:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, objectExtended(t, o));
|
2013-02-10 17:38:51 -07:00
|
|
|
return fieldAtOffset<uintptr_t>(o, baseSize * BytesPerWord);
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
extendedSize(Thread* t, object o, unsigned baseSize)
|
|
|
|
{
|
|
|
|
return baseSize + objectExtended(t, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
markHashTaken(Thread* t, object o)
|
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, not objectExtended(t, o));
|
|
|
|
assertT(t, not objectFixed(t, o));
|
2007-08-18 15:24:29 -06:00
|
|
|
|
2007-09-23 19:39:03 -06:00
|
|
|
ACQUIRE_RAW(t, t->m->heapLock);
|
2007-10-27 19:54:30 -06:00
|
|
|
|
2011-02-14 11:47:59 -07:00
|
|
|
alias(o, 0) |= HashTakenMark;
|
2008-01-14 09:39:57 -07:00
|
|
|
t->m->heap->pad(o);
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline uint32_t
|
|
|
|
takeHash(Thread*, object o)
|
|
|
|
{
|
2011-03-27 20:22:59 -06:00
|
|
|
// some broken code implicitly relies on System.identityHashCode
|
|
|
|
// always returning a non-negative number (e.g. old versions of
|
|
|
|
// com/sun/xml/bind/v2/util/CollisionCheckStack.hash), hence the "&
|
|
|
|
// 0x7FFFFFFF":
|
|
|
|
return (reinterpret_cast<uintptr_t>(o) / BytesPerWord) & 0x7FFFFFFF;
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline uint32_t
|
|
|
|
objectHash(Thread* t, object o)
|
|
|
|
{
|
|
|
|
if (objectExtended(t, o)) {
|
|
|
|
return extendedWord(t, o, baseSize(t, o, objectClass(t, o)));
|
|
|
|
} else {
|
2007-10-22 11:22:30 -06:00
|
|
|
if (not objectFixed(t, o)) {
|
|
|
|
markHashTaken(t, o);
|
|
|
|
}
|
2007-07-06 09:24:06 -06:00
|
|
|
return takeHash(t, o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
objectEqual(Thread*, object a, object b)
|
|
|
|
{
|
|
|
|
return a == b;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline uint32_t
|
2014-06-28 17:24:24 -06:00
|
|
|
byteArrayHash(Thread* t UNUSED, object ao)
|
2007-07-06 09:24:06 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* a = cast<GcByteArray>(t, ao);
|
|
|
|
return hash(a->body());
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
2007-07-28 18:02:32 -06:00
|
|
|
inline uint32_t
|
2014-06-28 17:24:24 -06:00
|
|
|
charArrayHash(Thread* t UNUSED, object ao)
|
2007-07-28 18:02:32 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* a = cast<GcByteArray>(t, ao);
|
|
|
|
return hash(a->body());
|
2007-07-28 18:02:32 -06:00
|
|
|
}
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
inline bool
|
2014-06-28 17:24:24 -06:00
|
|
|
byteArrayEqual(Thread* t UNUSED, object ao, object bo)
|
2007-07-06 09:24:06 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* a = cast<GcByteArray>(t, ao);
|
|
|
|
GcByteArray* b = cast<GcByteArray>(t, bo);
|
2007-07-06 09:24:06 -06:00
|
|
|
return a == b or
|
2014-06-28 17:24:24 -06:00
|
|
|
((a->length() == b->length()) and
|
|
|
|
memcmp(a->body().begin(), b->body().begin(),
|
|
|
|
a->length()) == 0);
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
2007-07-28 18:02:32 -06:00
|
|
|
inline uint32_t
|
2014-06-28 17:24:24 -06:00
|
|
|
stringHash(Thread* t, object so)
|
2007-07-28 18:02:32 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcString* s = cast<GcString>(t, so);
|
|
|
|
if (s->hashCode() == 0 and s->length(t)) {
|
|
|
|
object data = reinterpret_cast<object>(s->data());
|
2014-05-28 22:17:25 -06:00
|
|
|
if (objectClass(t, data) == type(t, GcByteArray::Type)) {
|
2014-06-28 17:24:24 -06:00
|
|
|
s->hashCode() = hash
|
|
|
|
(cast<GcByteArray>(t, data)->body().subslice(s->offset(t), s->length(t)));
|
2007-07-28 18:02:32 -06:00
|
|
|
} else {
|
2014-06-28 17:24:24 -06:00
|
|
|
s->hashCode() = hash
|
|
|
|
(cast<GcCharArray>(t, data)->body().subslice(s->offset(t), s->length(t)));
|
2007-07-28 18:02:32 -06:00
|
|
|
}
|
|
|
|
}
|
2014-06-28 17:24:24 -06:00
|
|
|
return s->hashCode();
|
2007-07-28 18:02:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline uint16_t
|
2014-06-28 17:24:24 -06:00
|
|
|
stringCharAt(Thread* t, GcString* s, int i)
|
2007-07-28 18:02:32 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
object data = reinterpret_cast<object>(s->data());
|
2014-05-28 22:17:25 -06:00
|
|
|
if (objectClass(t, data) == type(t, GcByteArray::Type)) {
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcByteArray>(t, data)->body()[s->offset(t) + i];
|
2007-07-28 18:02:32 -06:00
|
|
|
} else {
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcCharArray>(t, data)->body()[s->offset(t) + i];
|
2007-07-28 18:02:32 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2014-06-28 17:24:24 -06:00
|
|
|
stringEqual(Thread* t, object ao, object bo)
|
2007-07-28 18:02:32 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcString* a = cast<GcString>(t, ao);
|
|
|
|
GcString* b = cast<GcString>(t, bo);
|
2007-07-28 18:02:32 -06:00
|
|
|
if (a == b) {
|
|
|
|
return true;
|
2014-06-28 17:24:24 -06:00
|
|
|
} else if (a->length(t) == b->length(t)) {
|
|
|
|
for (unsigned i = 0; i < a->length(t); ++i) {
|
2007-07-28 18:02:32 -06:00
|
|
|
if (stringCharAt(t, a, i) != stringCharAt(t, b, i)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-06 09:24:06 -06:00
|
|
|
inline uint32_t
|
2014-05-28 22:17:25 -06:00
|
|
|
methodHash(Thread* t, object mo)
|
2007-07-06 09:24:06 -06:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
GcMethod* method = cast<GcMethod>(t, mo);
|
2014-06-20 22:16:33 -06:00
|
|
|
return byteArrayHash(t, reinterpret_cast<object>(method->name()))
|
|
|
|
^ byteArrayHash(t, reinterpret_cast<object>(method->spec()));
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2014-05-28 22:17:25 -06:00
|
|
|
methodEqual(Thread* t, object ao, object bo)
|
2007-07-06 09:24:06 -06:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
GcMethod* a = cast<GcMethod>(t, ao);
|
|
|
|
GcMethod* b = cast<GcMethod>(t, bo);
|
2007-07-06 09:24:06 -06:00
|
|
|
return a == b or
|
2014-06-20 22:16:33 -06:00
|
|
|
(byteArrayEqual(t, reinterpret_cast<object>(a->name()), reinterpret_cast<object>(b->name())) and
|
|
|
|
byteArrayEqual(t, reinterpret_cast<object>(a->spec()), reinterpret_cast<object>(b->spec())));
|
2007-07-06 09:24:06 -06:00
|
|
|
}
|
|
|
|
|
2007-09-28 17:41:03 -06:00
|
|
|
class MethodSpecIterator {
|
|
|
|
public:
|
|
|
|
MethodSpecIterator(Thread* t, const char* s):
|
|
|
|
t(t), s(s + 1)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
const char* next() {
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, *s != ')');
|
2007-09-28 17:41:03 -06:00
|
|
|
|
|
|
|
const char* p = s;
|
|
|
|
|
|
|
|
switch (*s) {
|
|
|
|
case 'L':
|
|
|
|
while (*s and *s != ';') ++ s;
|
|
|
|
++ s;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '[':
|
|
|
|
while (*s == '[') ++ s;
|
|
|
|
switch (*s) {
|
|
|
|
case 'L':
|
|
|
|
while (*s and *s != ';') ++ s;
|
|
|
|
++ s;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
++ s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2007-09-28 17:41:03 -06:00
|
|
|
default:
|
|
|
|
++ s;
|
|
|
|
break;
|
|
|
|
}
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2007-09-28 17:41:03 -06:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasNext() {
|
|
|
|
return *s != ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* returnSpec() {
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, *s == ')');
|
2007-09-28 17:41:03 -06:00
|
|
|
return s + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Thread* t;
|
|
|
|
const char* s;
|
|
|
|
};
|
|
|
|
|
2007-07-14 11:31:01 -06:00
|
|
|
unsigned
|
|
|
|
fieldCode(Thread* t, unsigned javaCode);
|
|
|
|
|
|
|
|
unsigned
|
|
|
|
fieldType(Thread* t, unsigned code);
|
|
|
|
|
|
|
|
unsigned
|
|
|
|
primitiveSize(Thread* t, unsigned code);
|
|
|
|
|
|
|
|
inline unsigned
|
2007-11-02 15:08:14 -06:00
|
|
|
fieldSize(Thread* t, unsigned code)
|
2007-07-14 11:31:01 -06:00
|
|
|
{
|
|
|
|
if (code == ObjectField) {
|
|
|
|
return BytesPerWord;
|
|
|
|
} else {
|
|
|
|
return primitiveSize(t, code);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-02 15:08:14 -06:00
|
|
|
inline unsigned
|
2014-06-28 17:24:24 -06:00
|
|
|
fieldSize(Thread* t, GcField* field)
|
2007-11-02 15:08:14 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return fieldSize(t, field->code());
|
2007-11-02 15:08:14 -06:00
|
|
|
}
|
|
|
|
|
2011-03-15 17:52:02 -06:00
|
|
|
inline void
|
2014-04-23 15:22:10 -06:00
|
|
|
scanMethodSpec(Thread* t, const char* s, bool static_,
|
|
|
|
unsigned* parameterCount, unsigned* parameterFootprint,
|
2011-03-15 17:52:02 -06:00
|
|
|
unsigned* returnCode)
|
|
|
|
{
|
|
|
|
unsigned count = 0;
|
2014-04-23 15:22:10 -06:00
|
|
|
unsigned footprint = 0;
|
2011-03-15 17:52:02 -06:00
|
|
|
MethodSpecIterator it(t, s);
|
2014-04-23 15:22:10 -06:00
|
|
|
while (it.hasNext()) {
|
2011-03-15 17:52:02 -06:00
|
|
|
++ count;
|
2014-04-23 15:22:10 -06:00
|
|
|
switch (*it.next()) {
|
|
|
|
case 'J':
|
|
|
|
case 'D':
|
|
|
|
footprint += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
++ footprint;
|
2014-05-28 22:17:25 -06:00
|
|
|
break;
|
2014-04-23 15:22:10 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not static_) {
|
|
|
|
++ footprint;
|
2011-03-15 17:52:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
*parameterCount = count;
|
2014-04-23 15:22:10 -06:00
|
|
|
*parameterFootprint = footprint;
|
2011-03-15 17:52:02 -06:00
|
|
|
*returnCode = fieldCode(t, *it.returnSpec());
|
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcClass*
|
2014-06-28 17:24:24 -06:00
|
|
|
findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec);
|
2007-07-30 17:19:05 -06:00
|
|
|
|
2009-07-21 18:57:55 -06:00
|
|
|
inline bool
|
2014-06-27 22:00:05 -06:00
|
|
|
emptyMethod(Thread* t UNUSED, GcMethod* method)
|
2009-07-21 18:57:55 -06:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
return ((method->flags() & ACC_NATIVE) == 0)
|
2014-06-27 22:00:05 -06:00
|
|
|
and (method->code()->length() == 1)
|
|
|
|
and (method->code()->body()[0] == return_);
|
2009-07-21 18:57:55 -06:00
|
|
|
}
|
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
object
|
|
|
|
parseUtf8(Thread* t, const char* data, unsigned length);
|
|
|
|
|
2013-02-03 14:09:29 -07:00
|
|
|
object
|
2014-06-28 17:24:24 -06:00
|
|
|
parseUtf8(Thread* t, GcByteArray* array);
|
2013-02-03 14:09:29 -07:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcClass*
|
2014-06-28 15:11:31 -06:00
|
|
|
parseClass(Thread* t, GcClassLoader* loader, const uint8_t* data, unsigned length,
|
2014-05-28 22:17:25 -06:00
|
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type);
|
2007-07-30 17:19:05 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcClass*
|
2014-06-28 17:24:24 -06:00
|
|
|
resolveClass(Thread* t, GcClassLoader* loader, GcByteArray* name, bool throw_ = true,
|
2014-05-28 22:17:25 -06:00
|
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type);
|
2009-06-10 18:15:00 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveClass(Thread* t, GcClassLoader* loader, const char* name, bool throw_ = true,
|
2014-05-28 22:17:25 -06:00
|
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type)
|
2009-06-10 18:15:00 -06:00
|
|
|
{
|
2009-08-10 07:56:16 -06:00
|
|
|
PROTECT(t, loader);
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* n = makeByteArray(t, "%s", name);
|
2011-03-17 21:42:15 -06:00
|
|
|
return resolveClass(t, loader, n, throw_, throwType);
|
2009-08-10 07:56:16 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcClass*
|
2011-03-17 21:42:15 -06:00
|
|
|
resolveSystemClass
|
2014-06-28 17:24:24 -06:00
|
|
|
(Thread* t, GcClassLoader* loader, GcByteArray* name, bool throw_ = true,
|
2014-05-28 22:17:25 -06:00
|
|
|
Gc::Type throwType = GcNoClassDefFoundError::Type);
|
2009-08-10 07:56:16 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveSystemClass(Thread* t, GcClassLoader* loader, const char* name)
|
2009-08-10 07:56:16 -06:00
|
|
|
{
|
2010-09-14 10:49:41 -06:00
|
|
|
return resolveSystemClass(t, loader, makeByteArray(t, "%s", name));
|
2009-06-10 18:15:00 -06:00
|
|
|
}
|
2007-07-14 11:31:01 -06:00
|
|
|
|
2009-08-18 14:26:28 -06:00
|
|
|
void
|
2014-06-28 15:11:31 -06:00
|
|
|
linkClass(Thread* t, GcClassLoader* loader, GcClass* class_);
|
2009-08-18 14:26:28 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcMethod*
|
|
|
|
resolveMethod(Thread* t, GcClass* class_, const char* methodName,
|
2007-09-23 19:39:03 -06:00
|
|
|
const char* methodSpec);
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveMethod(Thread* t, GcClassLoader* loader, const char* className,
|
2009-08-10 07:56:16 -06:00
|
|
|
const char* methodName, const char* methodSpec)
|
2009-06-10 18:15:00 -06:00
|
|
|
{
|
2010-12-27 15:55:23 -07:00
|
|
|
return resolveMethod
|
|
|
|
(t, resolveClass(t, loader, className), methodName, methodSpec);
|
2009-06-10 18:15:00 -06:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcField*
|
2014-05-28 22:17:25 -06:00
|
|
|
resolveField(Thread* t, GcClass* class_, const char* fieldName,
|
2009-06-10 18:15:00 -06:00
|
|
|
const char* fieldSpec);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcField*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveField(Thread* t, GcClassLoader* loader, const char* className,
|
2009-08-10 07:56:16 -06:00
|
|
|
const char* fieldName, const char* fieldSpec)
|
2009-06-10 18:15:00 -06:00
|
|
|
{
|
2010-12-27 15:55:23 -07:00
|
|
|
return resolveField
|
|
|
|
(t, resolveClass(t, loader, className), fieldName, fieldSpec);
|
2009-06-10 18:15:00 -06:00
|
|
|
}
|
|
|
|
|
2009-07-20 14:12:38 -06:00
|
|
|
bool
|
2014-05-28 22:17:25 -06:00
|
|
|
classNeedsInit(Thread* t, GcClass* c);
|
2008-11-29 21:58:09 -07:00
|
|
|
|
2009-07-20 14:12:38 -06:00
|
|
|
bool
|
2014-05-28 22:17:25 -06:00
|
|
|
preInitClass(Thread* t, GcClass* c);
|
2009-07-20 14:12:38 -06:00
|
|
|
|
|
|
|
void
|
2014-05-28 22:17:25 -06:00
|
|
|
postInitClass(Thread* t, GcClass* c);
|
2009-07-20 14:12:38 -06:00
|
|
|
|
|
|
|
void
|
2014-05-28 22:17:25 -06:00
|
|
|
initClass(Thread* t, GcClass* c);
|
2007-08-22 21:30:37 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcClass*
|
2014-06-28 17:24:24 -06:00
|
|
|
resolveObjectArrayClass(Thread* t, GcClassLoader* loader, GcClass* elementClass);
|
2010-11-27 16:27:30 -07:00
|
|
|
|
2007-07-14 11:31:01 -06:00
|
|
|
object
|
2014-05-28 22:17:25 -06:00
|
|
|
makeObjectArray(Thread* t, GcClass* elementClass, unsigned count);
|
2007-07-14 11:31:01 -06:00
|
|
|
|
2009-09-18 18:01:54 -06:00
|
|
|
inline object
|
|
|
|
makeObjectArray(Thread* t, unsigned count)
|
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
return makeObjectArray(t, type(t, GcJobject::Type), count);
|
2009-09-18 18:01:54 -06:00
|
|
|
}
|
|
|
|
|
2007-09-06 18:21:52 -06:00
|
|
|
object
|
2014-06-28 17:24:24 -06:00
|
|
|
findFieldInClass(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec);
|
2007-09-06 18:21:52 -06:00
|
|
|
|
2010-11-04 11:02:09 -06:00
|
|
|
inline object
|
2014-05-28 22:17:25 -06:00
|
|
|
findFieldInClass2(Thread* t, GcClass* class_, const char* name, const char* spec)
|
2010-11-04 11:02:09 -06:00
|
|
|
{
|
|
|
|
PROTECT(t, class_);
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* n = makeByteArray(t, "%s", name);
|
2010-11-04 11:02:09 -06:00
|
|
|
PROTECT(t, n);
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* s = makeByteArray(t, "%s", spec);
|
2010-11-04 11:02:09 -06:00
|
|
|
return findFieldInClass(t, class_, n, s);
|
|
|
|
}
|
|
|
|
|
2012-03-26 18:06:16 -06:00
|
|
|
object
|
2014-06-28 17:24:24 -06:00
|
|
|
findMethodInClass(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec);
|
2007-09-06 18:21:52 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcThrowable*
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
makeThrowable
|
2014-05-28 22:17:25 -06:00
|
|
|
(Thread* t, Gc::Type type, object message = 0, object trace = 0,
|
2014-06-28 17:24:24 -06:00
|
|
|
GcThrowable* cause = 0)
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
{
|
|
|
|
PROTECT(t, message);
|
|
|
|
PROTECT(t, trace);
|
|
|
|
PROTECT(t, cause);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
if (trace == 0) {
|
|
|
|
trace = makeTrace(t);
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcThrowable* result = cast<GcThrowable>(t, make(t, vm::type(t, type)));
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
set(t, reinterpret_cast<object>(result), ThrowableMessage, message);
|
|
|
|
set(t, reinterpret_cast<object>(result), ThrowableTrace, trace);
|
avoid inifinite recursion if java.lang.Object is missing; refactoring
When trying to create an array class, we try to resolve
java.lang.Object so we can use its vtable in the array class.
However, if Object is missing, we'll try to create and throw a
ClassNotFoundException, which requires creating an array to store the
stack trace, which requires creating an array class, which requires
resolving Object, etc.. This commit short-circuits this process by
telling resolveClass not to create and throw an exception if it can't
find Object.
While doing the above work, I noticed that the implementations of
Classpath::makeThrowable in classpath-avian.cpp and
classpath-openjdk.cpp were identical, so I made makeThrowable a
top-level function.
Finally, I discovered that Thread.setDaemon can only be called before
the target thread has been started, which allowed me to simplify the
code to track daemon threads in the VM.
2010-12-09 19:38:12 -07:00
|
|
|
set(t, result, ThrowableCause, cause);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcThrowable*
|
2014-05-28 22:17:25 -06:00
|
|
|
makeThrowableV(Thread* t, Gc::Type type, const char* format, va_list a,
|
2012-03-03 18:37:27 -07:00
|
|
|
int size)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* s = makeByteArrayV(t, format, a, size);
|
2010-12-27 15:55:23 -07:00
|
|
|
|
2012-03-03 18:37:27 -07:00
|
|
|
if (s) {
|
2014-06-28 17:24:24 -06:00
|
|
|
GcString* message = t->m->classpath->makeString
|
|
|
|
(t, reinterpret_cast<object>(s), 0, s->length() - 1);
|
2010-12-27 15:55:23 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
return makeThrowable(t, type, reinterpret_cast<object>(message));
|
2012-03-03 18:37:27 -07:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2010-12-27 15:55:23 -07:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcThrowable*
|
2014-05-28 22:17:25 -06:00
|
|
|
makeThrowable(Thread* t, Gc::Type type, const char* format, ...)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
2012-03-03 18:37:27 -07:00
|
|
|
int size = 256;
|
|
|
|
while (true) {
|
|
|
|
va_list a;
|
|
|
|
va_start(a, format);
|
2014-06-28 17:24:24 -06:00
|
|
|
GcThrowable* r = makeThrowableV(t, type, format, a, size);
|
2012-03-03 18:37:27 -07:00
|
|
|
va_end(a);
|
2010-12-27 15:55:23 -07:00
|
|
|
|
2012-03-03 18:37:27 -07:00
|
|
|
if (r) {
|
|
|
|
return r;
|
|
|
|
} else {
|
|
|
|
size *= 2;
|
|
|
|
}
|
|
|
|
}
|
2010-12-27 15:55:23 -07:00
|
|
|
}
|
|
|
|
|
2011-01-27 11:54:41 -07:00
|
|
|
void
|
|
|
|
popResources(Thread* t);
|
|
|
|
|
2013-03-07 20:17:05 -07:00
|
|
|
} // namespace vm
|
|
|
|
|
2013-11-08 08:35:10 -07:00
|
|
|
AVIAN_EXPORT void
|
2013-03-07 20:17:05 -07:00
|
|
|
vmPrintTrace(vm::Thread* t);
|
|
|
|
|
2013-11-08 08:35:10 -07:00
|
|
|
AVIAN_EXPORT void
|
2013-04-09 17:45:19 -06:00
|
|
|
vmfPrintTrace(vm::Thread* t, FILE* out);
|
|
|
|
|
2013-03-07 20:17:05 -07:00
|
|
|
namespace vm {
|
|
|
|
|
|
|
|
void
|
|
|
|
dumpHeap(Thread* t, FILE* out);
|
|
|
|
|
2011-01-27 11:54:41 -07:00
|
|
|
inline void NO_RETURN
|
2014-06-28 17:24:24 -06:00
|
|
|
throw_(Thread* t, GcThrowable* e)
|
2011-01-27 11:54:41 -07:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, t->exception == 0);
|
|
|
|
assertT(t, e);
|
2011-01-27 11:54:41 -07:00
|
|
|
|
|
|
|
expect(t, not t->checkpoint->noThrow);
|
|
|
|
|
|
|
|
t->exception = e;
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
if (objectClass(t, e) == type(t, GcOutOfMemoryError::Type)) {
|
2013-03-07 20:17:05 -07:00
|
|
|
#ifdef AVIAN_HEAPDUMP
|
|
|
|
if (not t->m->dumpedHeapOnOOM) {
|
|
|
|
t->m->dumpedHeapOnOOM = true;
|
|
|
|
const char* path = findProperty(t, "avian.heap.dump");
|
|
|
|
if (path) {
|
|
|
|
FILE* out = vm::fopen(path, "wb");
|
|
|
|
if (out) {
|
|
|
|
dumpHeap(t, out);
|
|
|
|
fclose(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif//AVIAN_HEAPDUMP
|
|
|
|
|
|
|
|
if (AbortOnOutOfMemoryError) {
|
|
|
|
fprintf(stderr, "OutOfMemoryError\n");
|
|
|
|
vmPrintTrace(t);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-11 21:13:11 -07:00
|
|
|
// printTrace(t, e);
|
|
|
|
|
2011-01-27 11:54:41 -07:00
|
|
|
popResources(t);
|
|
|
|
|
|
|
|
t->checkpoint->unwind();
|
|
|
|
|
|
|
|
abort(t);
|
|
|
|
}
|
2010-12-27 15:55:23 -07:00
|
|
|
|
|
|
|
inline void NO_RETURN
|
2011-02-11 21:13:11 -07:00
|
|
|
throwNew
|
2014-05-28 22:17:25 -06:00
|
|
|
(Thread* t, Gc::Type type, object message = 0, object trace = 0,
|
2014-06-28 17:24:24 -06:00
|
|
|
GcThrowable* cause = 0)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
2011-02-11 21:13:11 -07:00
|
|
|
throw_(t, makeThrowable(t, type, message, trace, cause));
|
2010-12-27 15:55:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void NO_RETURN
|
2014-05-28 22:17:25 -06:00
|
|
|
throwNew(Thread* t, Gc::Type type, const char* format, ...)
|
2010-12-27 15:55:23 -07:00
|
|
|
{
|
2012-03-03 18:37:27 -07:00
|
|
|
int size = 256;
|
|
|
|
while (true) {
|
|
|
|
va_list a;
|
|
|
|
va_start(a, format);
|
2014-06-28 17:24:24 -06:00
|
|
|
GcThrowable* r = makeThrowableV(t, type, format, a, size);
|
2012-03-03 18:37:27 -07:00
|
|
|
va_end(a);
|
2010-12-27 15:55:23 -07:00
|
|
|
|
2012-03-03 18:37:27 -07:00
|
|
|
if (r) {
|
|
|
|
throw_(t, r);
|
|
|
|
} else {
|
|
|
|
size *= 2;
|
|
|
|
}
|
|
|
|
}
|
2010-12-27 15:55:23 -07:00
|
|
|
}
|
|
|
|
|
2007-09-06 18:21:52 -06:00
|
|
|
object
|
2014-06-28 17:24:24 -06:00
|
|
|
findInHierarchyOrNull(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec,
|
|
|
|
object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*));
|
2010-11-04 11:02:09 -06:00
|
|
|
|
|
|
|
inline object
|
2014-06-28 17:24:24 -06:00
|
|
|
findInHierarchy(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec,
|
|
|
|
object (*find)(Thread*, GcClass*, GcByteArray*, GcByteArray*),
|
2014-05-28 22:17:25 -06:00
|
|
|
Gc::Type errorType, bool throw_ = true)
|
2010-11-04 11:02:09 -06:00
|
|
|
{
|
|
|
|
object o = findInHierarchyOrNull(t, class_, name, spec, find);
|
|
|
|
|
2011-03-15 17:52:02 -06:00
|
|
|
if (throw_ and o == 0) {
|
2010-12-27 15:55:23 -07:00
|
|
|
throwNew(t, errorType, "%s %s not found in %s",
|
2014-06-28 17:24:24 -06:00
|
|
|
name->body().begin(),
|
|
|
|
spec->body().begin(),
|
2014-06-20 22:16:33 -06:00
|
|
|
class_->name()->body().begin());
|
2010-11-04 11:02:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return o;
|
|
|
|
}
|
2007-09-06 18:21:52 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
2014-06-28 17:24:24 -06:00
|
|
|
findMethod(Thread* t, GcClass* class_, GcByteArray* name, GcByteArray* spec)
|
2007-09-06 18:21:52 -06:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
return cast<GcMethod>(t, findInHierarchy
|
|
|
|
(t, class_, name, spec, findMethodInClass, GcNoSuchMethodError::Type));
|
2007-09-06 18:21:52 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
|
|
|
findMethodOrNull(Thread* t, GcClass* class_, const char* name, const char* spec)
|
2010-11-04 11:02:09 -06:00
|
|
|
{
|
|
|
|
PROTECT(t, class_);
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* n = makeByteArray(t, "%s", name);
|
2010-11-04 11:02:09 -06:00
|
|
|
PROTECT(t, n);
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* s = makeByteArray(t, "%s", spec);
|
2014-05-28 22:17:25 -06:00
|
|
|
return cast<GcMethod>(t, findInHierarchyOrNull(t, class_, n, s, findMethodInClass));
|
2010-11-04 11:02:09 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
|
|
|
findVirtualMethod(Thread* t, GcMethod* method, GcClass* class_)
|
2008-06-25 10:28:11 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcMethod>(t, cast<GcArray>(t, class_->virtualTable())->body()[method->offset()]);
|
2008-06-25 10:28:11 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
|
|
|
findInterfaceMethod(Thread* t, GcMethod* method, GcClass* class_)
|
2009-09-18 18:01:54 -06:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, (class_->vmFlags() & BootstrapFlag) == 0);
|
2009-09-18 18:01:54 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
object interface = reinterpret_cast<object>(method->class_());
|
|
|
|
GcArray* itable = cast<GcArray>(t, class_->interfaceTable());
|
|
|
|
for (unsigned i = 0; i < itable->length(); i += 2) {
|
|
|
|
if (itable->body()[i] == interface) {
|
|
|
|
return cast<GcMethod>(t, cast<GcArray>(t, itable->body()[i + 1])->body()[method->offset()]);
|
2009-09-18 18:01:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
abort(t);
|
|
|
|
}
|
|
|
|
|
2007-07-14 11:31:01 -06:00
|
|
|
inline unsigned
|
2007-08-19 13:45:51 -06:00
|
|
|
objectArrayLength(Thread* t UNUSED, object array)
|
2007-07-14 11:31:01 -06:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, objectClass(t, array)->fixedSize() == BytesPerWord * 2);
|
|
|
|
assertT(t, objectClass(t, array)->arrayElementSize() == BytesPerWord);
|
2013-02-10 17:38:51 -07:00
|
|
|
return fieldAtOffset<uintptr_t>(array, BytesPerWord);
|
2007-07-14 11:31:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline object&
|
2007-08-19 13:45:51 -06:00
|
|
|
objectArrayBody(Thread* t UNUSED, object array, unsigned index)
|
2007-07-14 11:31:01 -06:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, objectClass(t, array)->fixedSize() == BytesPerWord * 2);
|
|
|
|
assertT(t, objectClass(t, array)->arrayElementSize() == BytesPerWord);
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, objectClass(t, array)->objectMask()
|
|
|
|
== cast<GcClass>(t, t->m->types->body()[GcArray::Type])->objectMask());
|
2013-02-10 17:38:51 -07:00
|
|
|
return fieldAtOffset<object>(array, ArrayBody + (index * BytesPerWord));
|
2007-07-14 11:31:01 -06:00
|
|
|
}
|
|
|
|
|
2007-11-20 15:24:02 -07:00
|
|
|
unsigned
|
|
|
|
parameterFootprint(Thread* t, const char* s, bool static_);
|
|
|
|
|
2007-07-06 17:50:26 -06:00
|
|
|
void
|
|
|
|
addFinalizer(Thread* t, object target, void (*finalize)(Thread*, object));
|
|
|
|
|
2010-12-16 16:46:25 -07:00
|
|
|
inline bool
|
|
|
|
acquireSystem(Thread* t, Thread* target)
|
|
|
|
{
|
|
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
|
2012-02-03 17:20:20 -07:00
|
|
|
if (t->state != Thread::JoinedState) {
|
2010-12-16 16:46:25 -07:00
|
|
|
atomicOr(&(target->flags), Thread::SystemFlag);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
releaseSystem(Thread* t, Thread* target)
|
|
|
|
{
|
|
|
|
ACQUIRE_RAW(t, t->m->stateLock);
|
|
|
|
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, t->state != Thread::JoinedState);
|
2010-12-16 16:46:25 -07:00
|
|
|
|
|
|
|
atomicAnd(&(target->flags), ~Thread::SystemFlag);
|
|
|
|
}
|
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
inline bool
|
|
|
|
atomicCompareAndSwapObject(Thread* t, object target, unsigned offset,
|
|
|
|
object old, object new_)
|
|
|
|
{
|
2013-02-10 17:38:51 -07:00
|
|
|
if (atomicCompareAndSwap(&fieldAtOffset<uintptr_t>(target, offset),
|
2010-02-04 17:56:21 -07:00
|
|
|
reinterpret_cast<uintptr_t>(old),
|
|
|
|
reinterpret_cast<uintptr_t>(new_)))
|
|
|
|
{
|
|
|
|
mark(t, target, offset);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The following two methods (monitorAtomicAppendAcquire and
|
|
|
|
// monitorAtomicPollAcquire) use the Michael and Scott Non-Blocking
|
|
|
|
// Queue Algorithm: http://www.cs.rochester.edu/u/michael/PODC96.html
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorAtomicAppendAcquire(Thread* t, GcMonitor* monitor, GcMonitorNode* node)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2011-03-25 18:21:15 -06:00
|
|
|
if (node == 0) {
|
|
|
|
PROTECT(t, monitor);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
node = makeMonitorNode(t, t, 0);
|
2011-03-25 18:21:15 -06:00
|
|
|
}
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
while (true) {
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitorNode* tail = cast<GcMonitorNode>(t, monitor->acquireTail());
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
loadMemoryBarrier();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
object next = tail->next();
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
loadMemoryBarrier();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (tail == cast<GcMonitorNode>(t, monitor->acquireTail())) {
|
2010-02-04 17:56:21 -07:00
|
|
|
if (next) {
|
|
|
|
atomicCompareAndSwapObject
|
2014-06-28 17:24:24 -06:00
|
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireTail, reinterpret_cast<object>(tail), next);
|
2010-02-04 17:56:21 -07:00
|
|
|
} else if (atomicCompareAndSwapObject
|
2014-06-28 17:24:24 -06:00
|
|
|
(t, reinterpret_cast<object>(tail), MonitorNodeNext, 0, reinterpret_cast<object>(node)))
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
|
|
|
atomicCompareAndSwapObject
|
2014-06-28 17:24:24 -06:00
|
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireTail, reinterpret_cast<object>(tail), reinterpret_cast<object>(node));
|
2010-02-04 17:56:21 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Thread*
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorAtomicPollAcquire(Thread* t, GcMonitor* monitor, bool remove)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
|
|
|
while (true) {
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitorNode* head = cast<GcMonitorNode>(t, monitor->acquireHead());
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
loadMemoryBarrier();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitorNode* tail = cast<GcMonitorNode>(t, monitor->acquireTail());
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
loadMemoryBarrier();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitorNode* next = cast<GcMonitorNode>(t, head->next());
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
loadMemoryBarrier();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (head == cast<GcMonitorNode>(t, monitor->acquireHead())) {
|
2010-02-04 17:56:21 -07:00
|
|
|
if (head == tail) {
|
|
|
|
if (next) {
|
|
|
|
atomicCompareAndSwapObject
|
2014-06-28 17:24:24 -06:00
|
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireTail, reinterpret_cast<object>(tail), reinterpret_cast<object>(next));
|
2010-02-04 17:56:21 -07:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
2014-06-28 17:24:24 -06:00
|
|
|
Thread* value = static_cast<Thread*>(next->value());
|
2010-02-04 17:56:21 -07:00
|
|
|
if ((not remove)
|
|
|
|
or atomicCompareAndSwapObject
|
2014-06-28 17:24:24 -06:00
|
|
|
(t, reinterpret_cast<object>(monitor), MonitorAcquireHead, reinterpret_cast<object>(head), reinterpret_cast<object>(next)))
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorTryAcquire(Thread* t, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if (monitor->owner() == t
|
2010-02-04 17:56:21 -07:00
|
|
|
or (monitorAtomicPollAcquire(t, monitor, false) == 0
|
|
|
|
and atomicCompareAndSwap
|
2014-06-28 17:24:24 -06:00
|
|
|
(reinterpret_cast<uintptr_t*>(&monitor->owner()), 0,
|
2010-02-04 17:56:21 -07:00
|
|
|
reinterpret_cast<uintptr_t>(t))))
|
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
++ monitor->depth();
|
2010-02-04 17:56:21 -07:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorAcquire(Thread* t, GcMonitor* monitor, GcMonitorNode* node = 0)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
|
|
|
if (not monitorTryAcquire(t, monitor)) {
|
|
|
|
PROTECT(t, monitor);
|
2011-03-25 18:21:15 -06:00
|
|
|
PROTECT(t, node);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
ACQUIRE(t, t->lock);
|
|
|
|
|
2011-03-25 18:21:15 -06:00
|
|
|
monitorAtomicAppendAcquire(t, monitor, node);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
// note that we don't try to acquire the lock until we're first in
|
|
|
|
// line, both because it's fair and because we don't support
|
|
|
|
// removing elements from arbitrary positions in the queue
|
|
|
|
|
|
|
|
while (not (t == monitorAtomicPollAcquire(t, monitor, false)
|
|
|
|
and atomicCompareAndSwap
|
2014-06-28 17:24:24 -06:00
|
|
|
(reinterpret_cast<uintptr_t*>(&monitor->owner()), 0,
|
2010-02-04 17:56:21 -07:00
|
|
|
reinterpret_cast<uintptr_t>(t))))
|
|
|
|
{
|
|
|
|
ENTER(t, Thread::IdleState);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
t->lock->wait(t->systemThread, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(t, t == monitorAtomicPollAcquire(t, monitor, true));
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
++ monitor->depth();
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorRelease(Thread* t, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
expect(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (-- monitor->depth() == 0) {
|
|
|
|
monitor->owner() = 0;
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
storeLoadMemoryBarrier();
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
Thread* next = monitorAtomicPollAcquire(t, monitor, false);
|
|
|
|
|
2010-12-16 16:46:25 -07:00
|
|
|
if (next and acquireSystem(t, next)) {
|
2010-02-04 17:56:21 -07:00
|
|
|
ACQUIRE(t, next->lock);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
next->lock->notify(t->systemThread);
|
2010-12-16 16:46:25 -07:00
|
|
|
|
|
|
|
releaseSystem(t, next);
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorAppendWait(Thread* t, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
expect(t, (t->flags & Thread::WaitingFlag) == 0);
|
2010-02-04 17:56:21 -07:00
|
|
|
expect(t, t->waitNext == 0);
|
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
atomicOr(&(t->flags), Thread::WaitingFlag);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (monitor->waitTail()) {
|
|
|
|
static_cast<Thread*>(monitor->waitTail())->waitNext = t;
|
2010-02-04 17:56:21 -07:00
|
|
|
} else {
|
2014-06-28 17:24:24 -06:00
|
|
|
monitor->waitHead() = t;
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
monitor->waitTail() = t;
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorRemoveWait(Thread* t, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
Thread* previous = 0;
|
2014-06-28 17:24:24 -06:00
|
|
|
for (Thread* current = static_cast<Thread*>(monitor->waitHead());
|
2010-02-04 17:56:21 -07:00
|
|
|
current; current = current->waitNext)
|
|
|
|
{
|
|
|
|
if (t == current) {
|
2014-06-28 17:24:24 -06:00
|
|
|
if (t == monitor->waitHead()) {
|
|
|
|
monitor->waitHead() = t->waitNext;
|
2010-02-04 17:56:21 -07:00
|
|
|
} else {
|
|
|
|
previous->waitNext = t->waitNext;
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (t == monitor->waitTail()) {
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, t->waitNext == 0);
|
2014-06-28 17:24:24 -06:00
|
|
|
monitor->waitTail() = previous;
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
t->waitNext = 0;
|
2010-09-14 10:49:41 -06:00
|
|
|
atomicAnd(&(t->flags), ~Thread::WaitingFlag);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
previous = current;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
abort(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorFindWait(Thread* t, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
for (Thread* current = static_cast<Thread*>(monitor->waitHead());
|
2010-02-04 17:56:21 -07:00
|
|
|
current; current = current->waitNext)
|
|
|
|
{
|
|
|
|
if (t == current) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorWait(Thread* t, GcMonitor* monitor, int64_t time)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
expect(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
bool interrupted;
|
|
|
|
unsigned depth;
|
|
|
|
|
|
|
|
PROTECT(t, monitor);
|
|
|
|
|
2011-03-25 18:21:15 -06:00
|
|
|
// pre-allocate monitor node so we don't get an OutOfMemoryError
|
|
|
|
// when we try to re-acquire the monitor below
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitorNode* monitorNode = makeMonitorNode(t, t, 0);
|
2011-03-25 18:21:15 -06:00
|
|
|
PROTECT(t, monitorNode);
|
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
{ ACQUIRE(t, t->lock);
|
|
|
|
|
|
|
|
monitorAppendWait(t, monitor);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
depth = monitor->depth();
|
|
|
|
monitor->depth() = 1;
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
monitorRelease(t, monitor);
|
|
|
|
|
|
|
|
ENTER(t, Thread::IdleState);
|
|
|
|
|
2011-07-12 18:01:17 -06:00
|
|
|
interrupted = t->lock->waitAndClearInterrupted(t->systemThread, time);
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
|
2011-03-25 18:21:15 -06:00
|
|
|
monitorAcquire(t, monitor, monitorNode);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
monitor->depth() = depth;
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
if (t->flags & Thread::WaitingFlag) {
|
2010-02-04 17:56:21 -07:00
|
|
|
monitorRemoveWait(t, monitor);
|
|
|
|
} else {
|
|
|
|
expect(t, not monitorFindWait(t, monitor));
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
return interrupted;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Thread*
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorPollWait(Thread* t UNUSED, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, monitor->owner() == t);
|
2010-02-04 17:56:21 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
Thread* next = static_cast<Thread*>(monitor->waitHead());
|
2010-02-04 17:56:21 -07:00
|
|
|
|
|
|
|
if (next) {
|
2014-06-28 17:24:24 -06:00
|
|
|
monitor->waitHead() = next->waitNext;
|
2010-09-14 10:49:41 -06:00
|
|
|
atomicAnd(&(next->flags), ~Thread::WaitingFlag);
|
2010-02-04 17:56:21 -07:00
|
|
|
next->waitNext = 0;
|
2014-06-28 17:24:24 -06:00
|
|
|
if (next == monitor->waitTail()) {
|
|
|
|
monitor->waitTail() = 0;
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
} else {
|
2014-06-28 17:24:24 -06:00
|
|
|
assertT(t, monitor->waitTail() == 0);
|
2010-02-04 17:56:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorNotify(Thread* t, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
expect(t, monitor->owner() == t);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
Thread* next = monitorPollWait(t, monitor);
|
|
|
|
|
|
|
|
if (next) {
|
|
|
|
ACQUIRE(t, next->lock);
|
|
|
|
|
|
|
|
next->lock->notify(t->systemThread);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
monitorNotifyAll(Thread* t, GcMonitor* monitor)
|
2010-02-04 17:56:21 -07:00
|
|
|
{
|
|
|
|
PROTECT(t, monitor);
|
|
|
|
|
|
|
|
while (monitorNotify(t, monitor)) { }
|
|
|
|
}
|
|
|
|
|
2010-09-16 19:43:27 -06:00
|
|
|
class ObjectMonitorResource {
|
|
|
|
public:
|
2014-06-28 17:24:24 -06:00
|
|
|
ObjectMonitorResource(Thread* t, GcMonitor* o): o(o), protector(t, &(this->o)) {
|
2010-09-16 19:43:27 -06:00
|
|
|
monitorAcquire(protector.t, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
~ObjectMonitorResource() {
|
|
|
|
monitorRelease(protector.t, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitor* o;
|
2010-09-16 19:43:27 -06:00
|
|
|
Thread::SingleProtector protector;
|
|
|
|
};
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitor*
|
2007-11-27 15:23:00 -07:00
|
|
|
objectMonitor(Thread* t, object o, bool createNew);
|
2007-07-06 17:50:26 -06:00
|
|
|
|
2007-07-07 17:47:35 -06:00
|
|
|
inline void
|
|
|
|
acquire(Thread* t, object o)
|
|
|
|
{
|
2008-01-19 13:12:16 -07:00
|
|
|
unsigned hash;
|
|
|
|
if (DebugMonitors) {
|
|
|
|
hash = objectHash(t, o);
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitor* m = objectMonitor(t, o, true);
|
2007-07-10 22:19:26 -06:00
|
|
|
|
|
|
|
if (DebugMonitors) {
|
2010-09-16 19:43:27 -06:00
|
|
|
fprintf(stderr, "thread %p acquires %p for %x\n", t, m, hash);
|
2007-07-10 22:19:26 -06:00
|
|
|
}
|
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
monitorAcquire(t, m);
|
2007-07-07 17:47:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
release(Thread* t, object o)
|
|
|
|
{
|
2008-01-19 13:12:16 -07:00
|
|
|
unsigned hash;
|
|
|
|
if (DebugMonitors) {
|
|
|
|
hash = objectHash(t, o);
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
2007-07-10 22:19:26 -06:00
|
|
|
|
|
|
|
if (DebugMonitors) {
|
2010-09-16 19:43:27 -06:00
|
|
|
fprintf(stderr, "thread %p releases %p for %x\n", t, m, hash);
|
2007-07-10 22:19:26 -06:00
|
|
|
}
|
|
|
|
|
2010-02-04 17:56:21 -07:00
|
|
|
monitorRelease(t, m);
|
2007-07-07 17:47:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
wait(Thread* t, object o, int64_t milliseconds)
|
|
|
|
{
|
2008-01-19 13:12:16 -07:00
|
|
|
unsigned hash;
|
|
|
|
if (DebugMonitors) {
|
|
|
|
hash = objectHash(t, o);
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
2007-07-10 22:19:26 -06:00
|
|
|
|
|
|
|
if (DebugMonitors) {
|
2009-10-14 10:01:37 -06:00
|
|
|
fprintf(stderr, "thread %p waits %d millis on %p for %x\n",
|
|
|
|
t, static_cast<int>(milliseconds), m, hash);
|
2007-07-10 22:19:26 -06:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (m and m->owner() == t) {
|
2010-02-04 17:56:21 -07:00
|
|
|
PROTECT(t, m);
|
|
|
|
|
|
|
|
bool interrupted = monitorWait(t, m, milliseconds);
|
2007-07-28 15:28:25 -06:00
|
|
|
|
|
|
|
if (interrupted) {
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
if (t->m->alive or (t->flags & Thread::DaemonFlag) == 0) {
|
2012-05-02 11:41:36 -06:00
|
|
|
t->m->classpath->clearInterrupted(t);
|
2014-05-28 22:17:25 -06:00
|
|
|
throwNew(t, GcInterruptedException::Type);
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
} else {
|
2014-06-28 17:24:24 -06:00
|
|
|
throw_(t, cast<GcThrowable>(t, root(t, Machine::Shutdown)));
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
}
|
2007-07-28 15:28:25 -06:00
|
|
|
}
|
2007-07-07 17:47:35 -06:00
|
|
|
} else {
|
2014-05-28 22:17:25 -06:00
|
|
|
throwNew(t, GcIllegalMonitorStateException::Type);
|
2007-07-07 17:47:35 -06:00
|
|
|
}
|
2007-07-10 22:19:26 -06:00
|
|
|
|
|
|
|
if (DebugMonitors) {
|
|
|
|
fprintf(stderr, "thread %p wakes up on %p for %x\n",
|
2008-01-19 13:12:16 -07:00
|
|
|
t, m, hash);
|
2007-07-10 22:19:26 -06:00
|
|
|
}
|
2007-07-19 17:45:44 -06:00
|
|
|
|
|
|
|
stress(t);
|
2007-07-07 17:47:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
notify(Thread* t, object o)
|
|
|
|
{
|
2008-01-19 13:12:16 -07:00
|
|
|
unsigned hash;
|
|
|
|
if (DebugMonitors) {
|
|
|
|
hash = objectHash(t, o);
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
2007-07-10 22:19:26 -06:00
|
|
|
|
|
|
|
if (DebugMonitors) {
|
|
|
|
fprintf(stderr, "thread %p notifies on %p for %x\n",
|
2008-01-19 13:12:16 -07:00
|
|
|
t, m, hash);
|
2007-07-10 22:19:26 -06:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (m and m->owner() == t) {
|
2010-02-04 17:56:21 -07:00
|
|
|
monitorNotify(t, m);
|
2007-07-07 17:47:35 -06:00
|
|
|
} else {
|
2014-05-28 22:17:25 -06:00
|
|
|
throwNew(t, GcIllegalMonitorStateException::Type);
|
2007-07-07 17:47:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
notifyAll(Thread* t, object o)
|
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMonitor* m = objectMonitor(t, o, false);
|
2007-07-10 22:19:26 -06:00
|
|
|
|
|
|
|
if (DebugMonitors) {
|
|
|
|
fprintf(stderr, "thread %p notifies all on %p for %x\n",
|
|
|
|
t, m, objectHash(t, o));
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
if (m and m->owner() == t) {
|
2010-02-04 17:56:21 -07:00
|
|
|
monitorNotifyAll(t, m);
|
2007-07-07 17:47:35 -06:00
|
|
|
} else {
|
2014-05-28 22:17:25 -06:00
|
|
|
throwNew(t, GcIllegalMonitorStateException::Type);
|
2007-07-07 17:47:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-16 10:50:19 -07:00
|
|
|
inline void
|
|
|
|
interrupt(Thread* t, Thread* target)
|
|
|
|
{
|
|
|
|
if (acquireSystem(t, target)) {
|
2010-11-09 15:46:16 -07:00
|
|
|
target->systemThread->interrupt();
|
2010-11-16 10:50:19 -07:00
|
|
|
releaseSystem(t, target);
|
2010-11-09 15:46:16 -07:00
|
|
|
}
|
2007-07-20 08:36:31 -06:00
|
|
|
}
|
|
|
|
|
2011-07-12 14:15:43 -06:00
|
|
|
inline bool
|
|
|
|
getAndClearInterrupted(Thread* t, Thread* target)
|
|
|
|
{
|
|
|
|
if (acquireSystem(t, target)) {
|
|
|
|
bool result = target->systemThread->getAndClearInterrupted();
|
|
|
|
releaseSystem(t, target);
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
inline bool
|
2014-06-28 17:24:24 -06:00
|
|
|
exceptionMatch(Thread* t, GcClass* type, GcThrowable* exception)
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
{
|
|
|
|
return type == 0
|
2014-06-28 17:24:24 -06:00
|
|
|
or (reinterpret_cast<object>(exception) != root(t, Machine::Shutdown)
|
|
|
|
and instanceOf(t, type, reinterpret_cast<object>(t->exception)));
|
support multiple sequential VM instances with bootimage build
Until now, the bootimage build hasn't supported using the Java
invocation API to create a VM, destroy it, and create another in the
same process. Ideally, we would be able to create multiple VMs
simultaneously without any interference between them. In fact, Avian
is designed to support this for the most part, but there are a few
places we use global, mutable state which prevent this from working.
Most notably, the bootimage is modified in-place at runtime, so the
best we can do without extensive changes is to clean up the bootimage
when the VM is destroyed so it's ready for later instances. Hence
this commit.
Ultimately, we can move towards a fully reentrant VM by making the
bootimage immutable, but this will require some care to avoid
performance regressions. Another challenge is our Posix signal
handlers, which currently rely on a global handle to the VM, since you
can't, to my knowledge, pass a context pointer when registering a
signal handler. Thread local variables won't necessarily help, since
a thread might attatch to more than one VM at a time.
2011-11-10 13:10:53 -07:00
|
|
|
}
|
|
|
|
|
2007-07-28 18:02:32 -06:00
|
|
|
object
|
|
|
|
intern(Thread* t, object s);
|
|
|
|
|
2008-11-21 16:20:35 -07:00
|
|
|
void
|
|
|
|
walk(Thread* t, Heap::Walker* w, object o, unsigned start);
|
|
|
|
|
2008-11-11 08:20:49 -07:00
|
|
|
int
|
|
|
|
walkNext(Thread* t, object o, int previous);
|
|
|
|
|
|
|
|
void
|
|
|
|
visitRoots(Machine* m, Heap::Visitor* v);
|
|
|
|
|
2007-09-24 07:46:48 -06:00
|
|
|
inline jobject
|
|
|
|
makeLocalReference(Thread* t, object o)
|
|
|
|
{
|
|
|
|
return t->m->processor->makeLocalReference(t, o);
|
|
|
|
}
|
2007-09-23 19:39:03 -06:00
|
|
|
|
2007-09-24 07:46:48 -06:00
|
|
|
inline void
|
|
|
|
disposeLocalReference(Thread* t, jobject r)
|
|
|
|
{
|
|
|
|
t->m->processor->disposeLocalReference(t, r);
|
|
|
|
}
|
2007-09-23 19:39:03 -06:00
|
|
|
|
2007-11-05 07:28:46 -07:00
|
|
|
inline bool
|
2014-06-20 22:16:33 -06:00
|
|
|
methodVirtual(Thread* t UNUSED, GcMethod* method)
|
2007-11-05 07:28:46 -07:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
return (method->flags() & (ACC_STATIC | ACC_PRIVATE)) == 0
|
2014-06-20 22:16:33 -06:00
|
|
|
and method->name()->body()[0] != '<';
|
2007-11-05 07:28:46 -07:00
|
|
|
}
|
|
|
|
|
2007-11-05 14:40:17 -07:00
|
|
|
inline unsigned
|
2011-08-29 19:00:17 -06:00
|
|
|
singletonMaskSize(unsigned count, unsigned bitsPerWord)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
|
|
|
if (count) {
|
2013-02-10 18:06:15 -07:00
|
|
|
return ceilingDivide(count + 2, bitsPerWord);
|
2007-11-05 14:40:17 -07:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-29 19:00:17 -06:00
|
|
|
inline unsigned
|
|
|
|
singletonMaskSize(unsigned count)
|
|
|
|
{
|
|
|
|
return singletonMaskSize(count, BitsPerWord);
|
|
|
|
}
|
|
|
|
|
2007-11-05 14:40:17 -07:00
|
|
|
inline unsigned
|
2014-05-28 22:17:25 -06:00
|
|
|
singletonMaskSize(Thread* t UNUSED, GcSingleton* singleton)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
unsigned length = singleton->length();
|
2007-11-05 14:40:17 -07:00
|
|
|
if (length) {
|
2013-02-10 18:06:15 -07:00
|
|
|
return ceilingDivide(length + 2, BitsPerWord + 1);
|
2007-11-05 14:40:17 -07:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
2014-05-28 22:17:25 -06:00
|
|
|
singletonCount(Thread* t, GcSingleton* singleton)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
return singleton->length() - singletonMaskSize(t, singleton);
|
2007-11-05 14:40:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline uint32_t*
|
2014-06-28 17:24:24 -06:00
|
|
|
singletonMask(Thread* t UNUSED, GcSingleton* singleton)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, singleton->length());
|
2007-11-05 14:40:17 -07:00
|
|
|
return reinterpret_cast<uint32_t*>
|
2014-06-28 17:24:24 -06:00
|
|
|
(&singleton->body()[singletonCount(t, singleton)]);
|
2007-11-05 14:40:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2011-08-29 19:00:17 -06:00
|
|
|
singletonMarkObject(uint32_t* mask, unsigned index)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2011-08-29 19:00:17 -06:00
|
|
|
mask[(index + 2) / 32]
|
2007-11-05 14:40:17 -07:00
|
|
|
|= (static_cast<uint32_t>(1) << ((index + 2) % 32));
|
|
|
|
}
|
|
|
|
|
2011-08-29 19:00:17 -06:00
|
|
|
inline void
|
2014-05-28 22:17:25 -06:00
|
|
|
singletonMarkObject(Thread* t, GcSingleton* singleton, unsigned index)
|
2011-08-29 19:00:17 -06:00
|
|
|
{
|
|
|
|
singletonMarkObject(singletonMask(t, singleton), index);
|
|
|
|
}
|
|
|
|
|
2007-11-05 14:40:17 -07:00
|
|
|
inline bool
|
2014-05-28 22:17:25 -06:00
|
|
|
singletonIsObject(Thread* t, GcSingleton* singleton, unsigned index)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, index < singletonCount(t, singleton));
|
2007-11-05 14:40:17 -07:00
|
|
|
|
|
|
|
return (singletonMask(t, singleton)[(index + 2) / 32]
|
|
|
|
& (static_cast<uint32_t>(1) << ((index + 2) % 32))) != 0;
|
|
|
|
}
|
|
|
|
|
2007-11-06 08:29:05 -07:00
|
|
|
inline object&
|
2014-06-28 17:24:24 -06:00
|
|
|
singletonObject(Thread* t UNUSED, GcSingleton* singleton, unsigned index)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, singletonIsObject(t, singleton, index));
|
2014-06-28 17:24:24 -06:00
|
|
|
return reinterpret_cast<object&>(singleton->body()[index]);
|
2007-11-05 14:40:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline uintptr_t&
|
2014-06-28 17:24:24 -06:00
|
|
|
singletonValue(Thread* t UNUSED, GcSingleton* singleton, unsigned index)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, not singletonIsObject(t, singleton, index));
|
2014-06-28 17:24:24 -06:00
|
|
|
return singleton->body()[index];
|
2007-11-05 14:40:17 -07:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcSingleton*
|
2009-03-03 20:05:48 -07:00
|
|
|
makeSingletonOfSize(Thread* t, unsigned count)
|
2007-11-05 14:40:17 -07:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
GcSingleton* o = makeSingleton(t, count + singletonMaskSize(count));
|
2014-06-03 19:52:01 -06:00
|
|
|
assertT(t, o->length() == count + singletonMaskSize(t, o));
|
2007-11-05 14:40:17 -07:00
|
|
|
if (count) {
|
|
|
|
singletonMask(t, o)[0] = 1;
|
|
|
|
}
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2009-08-10 13:20:23 -06:00
|
|
|
inline void
|
2014-05-28 22:17:25 -06:00
|
|
|
singletonSetBit(Thread* t, GcSingleton* singleton, unsigned start, unsigned index)
|
2009-08-10 13:20:23 -06:00
|
|
|
{
|
2009-10-17 20:11:03 -06:00
|
|
|
singletonValue(t, singleton, start + (index / BitsPerWord))
|
|
|
|
|= static_cast<uintptr_t>(1) << (index % BitsPerWord);
|
2009-08-10 13:20:23 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2014-05-28 22:17:25 -06:00
|
|
|
singletonBit(Thread* t, GcSingleton* singleton, unsigned start, unsigned index)
|
2009-08-10 13:20:23 -06:00
|
|
|
{
|
2009-10-17 20:11:03 -06:00
|
|
|
return (singletonValue(t, singleton, start + (index / BitsPerWord))
|
|
|
|
& (static_cast<uintptr_t>(1) << (index % BitsPerWord))) != 0;
|
2009-08-10 13:20:23 -06:00
|
|
|
}
|
|
|
|
|
2011-08-29 19:00:17 -06:00
|
|
|
inline unsigned
|
|
|
|
poolMaskSize(unsigned count, unsigned bitsPerWord)
|
|
|
|
{
|
2013-02-10 18:06:15 -07:00
|
|
|
return ceilingDivide(count, bitsPerWord);
|
2011-08-29 19:00:17 -06:00
|
|
|
}
|
|
|
|
|
2009-10-17 20:11:03 -06:00
|
|
|
inline unsigned
|
|
|
|
poolMaskSize(unsigned count)
|
|
|
|
{
|
2011-08-29 19:00:17 -06:00
|
|
|
return poolMaskSize(count, BitsPerWord);
|
2009-10-17 20:11:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
2014-05-28 22:17:25 -06:00
|
|
|
poolMaskSize(Thread* t, GcSingleton* pool)
|
2009-10-17 20:11:03 -06:00
|
|
|
{
|
2013-02-10 18:06:15 -07:00
|
|
|
return ceilingDivide(singletonCount(t, pool), BitsPerWord + 1);
|
2009-10-17 20:11:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
2014-05-28 22:17:25 -06:00
|
|
|
poolSize(Thread* t, GcSingleton* pool)
|
2009-08-10 13:20:23 -06:00
|
|
|
{
|
2009-10-17 20:11:03 -06:00
|
|
|
return singletonCount(t, pool) - poolMaskSize(t, pool);
|
2009-08-10 13:20:23 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveClassInObject(Thread* t, GcClassLoader* loader, object container,
|
2011-03-15 17:52:02 -06:00
|
|
|
unsigned classOffset, bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
2013-02-10 17:38:51 -07:00
|
|
|
object o = fieldAtOffset<object>(container, classOffset);
|
2011-03-15 17:52:02 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
loadMemoryBarrier();
|
2011-03-15 17:52:02 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
if (objectClass(t, o) == type(t, GcByteArray::Type)) {
|
2014-06-28 17:24:24 -06:00
|
|
|
GcByteArray* name = cast<GcByteArray>(t, o);
|
2009-08-18 14:26:28 -06:00
|
|
|
PROTECT(t, container);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcClass* c = resolveClass(t, loader, name, throw_);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
|
|
|
if (c) {
|
2011-03-15 17:52:02 -06:00
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
set(t, container, classOffset, reinterpret_cast<object>(c));
|
2011-03-15 17:52:02 -06:00
|
|
|
}
|
2014-05-28 22:17:25 -06:00
|
|
|
|
|
|
|
return c;
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
2014-05-28 22:17:25 -06:00
|
|
|
return cast<GcClass>(t, o);
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveClassInPool(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
2011-03-15 17:52:02 -06:00
|
|
|
bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
2014-06-27 22:00:05 -06:00
|
|
|
object o = singletonObject(t, method->code()->pool(), index);
|
2011-03-15 17:52:02 -06:00
|
|
|
|
|
|
|
loadMemoryBarrier();
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
if (objectClass(t, o) == type(t, GcReference::Type)) {
|
2009-08-18 14:26:28 -06:00
|
|
|
PROTECT(t, method);
|
|
|
|
|
2014-06-28 21:18:22 -06:00
|
|
|
GcClass* c = resolveClass(t, loader, cast<GcReference>(t, o)->name(), throw_);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
|
|
|
if (c) {
|
2011-03-15 17:52:02 -06:00
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
set(t, method->code()->pool(),
|
|
|
|
SingletonBody + (index * BytesPerWord), c);
|
2011-03-15 17:52:02 -06:00
|
|
|
}
|
2014-05-28 22:17:25 -06:00
|
|
|
return c;
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
2014-05-28 22:17:25 -06:00
|
|
|
return cast<GcClass>(t, o);
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
|
|
|
resolveClassInPool(Thread* t, GcMethod* method, unsigned index,
|
2011-03-15 17:52:02 -06:00
|
|
|
bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
2014-06-28 15:11:31 -06:00
|
|
|
return resolveClassInPool(t, method->class_()->loader(),
|
2011-03-15 17:52:02 -06:00
|
|
|
method, index, throw_);
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline object
|
2014-06-28 15:11:31 -06:00
|
|
|
resolve(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
2014-06-28 17:24:24 -06:00
|
|
|
object (*find)(vm::Thread*, GcClass*, GcByteArray*, GcByteArray*),
|
2014-05-28 22:17:25 -06:00
|
|
|
Gc::Type errorType, bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
2014-06-27 22:00:05 -06:00
|
|
|
object o = singletonObject(t, method->code()->pool(), index);
|
2011-03-15 17:52:02 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
loadMemoryBarrier();
|
2011-03-15 17:52:02 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
if (objectClass(t, o) == type(t, GcReference::Type)) {
|
2009-08-18 14:26:28 -06:00
|
|
|
PROTECT(t, method);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcReference* reference = cast<GcReference>(t, o);
|
2009-08-18 14:26:28 -06:00
|
|
|
PROTECT(t, reference);
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcClass* class_ = resolveClassInObject(t, loader, o, ReferenceClass, throw_);
|
|
|
|
|
2011-03-15 17:52:02 -06:00
|
|
|
if (class_) {
|
|
|
|
o = findInHierarchy
|
2014-06-28 21:18:22 -06:00
|
|
|
(t, class_, reference->name(), reference->spec(),
|
2011-03-15 17:52:02 -06:00
|
|
|
find, errorType, throw_);
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2011-03-15 17:52:02 -06:00
|
|
|
if (o) {
|
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
|
2014-06-27 22:00:05 -06:00
|
|
|
set(t, reinterpret_cast<object>(method->code()->pool()),
|
2011-03-15 17:52:02 -06:00
|
|
|
SingletonBody + (index * BytesPerWord), o);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
o = 0;
|
|
|
|
}
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcField*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveField(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
2011-03-15 17:52:02 -06:00
|
|
|
bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcField>(t, resolve(t, loader, method, index, findFieldInClass,
|
|
|
|
GcNoSuchFieldError::Type, throw_));
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcField*
|
2014-05-28 22:17:25 -06:00
|
|
|
resolveField(Thread* t, GcMethod* method, unsigned index, bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
|
|
|
return resolveField
|
2014-06-28 15:11:31 -06:00
|
|
|
(t, method->class_()->loader(), method, index, throw_);
|
2011-03-15 17:52:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
acquireFieldForRead(Thread* t, GcField* field)
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if (UNLIKELY((field->flags() & ACC_VOLATILE)
|
2011-03-15 17:52:02 -06:00
|
|
|
and BytesPerWord == 4
|
2014-06-28 17:24:24 -06:00
|
|
|
and (field->code() == DoubleField
|
|
|
|
or field->code() == LongField)))
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
acquire(t, reinterpret_cast<object>(field));
|
2011-03-15 17:52:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
releaseFieldForRead(Thread* t, GcField* field)
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if (UNLIKELY(field->flags() & ACC_VOLATILE)) {
|
2011-03-15 17:52:02 -06:00
|
|
|
if (BytesPerWord == 4
|
2014-06-28 17:24:24 -06:00
|
|
|
and (field->code() == DoubleField
|
|
|
|
or field->code() == LongField))
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
release(t, reinterpret_cast<object>(field));
|
2011-03-15 17:52:02 -06:00
|
|
|
} else {
|
|
|
|
loadMemoryBarrier();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class FieldReadResource {
|
|
|
|
public:
|
2014-06-28 17:24:24 -06:00
|
|
|
FieldReadResource(Thread* t, GcField* o): o(o), protector(t, &(this->o)) {
|
2011-03-15 17:52:02 -06:00
|
|
|
acquireFieldForRead(protector.t, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
~FieldReadResource() {
|
|
|
|
releaseFieldForRead(protector.t, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2014-06-28 17:24:24 -06:00
|
|
|
GcField* o;
|
2011-03-15 17:52:02 -06:00
|
|
|
Thread::SingleProtector protector;
|
|
|
|
};
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
acquireFieldForWrite(Thread* t, GcField* field)
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if (UNLIKELY(field->flags() & ACC_VOLATILE)) {
|
2011-03-15 17:52:02 -06:00
|
|
|
if (BytesPerWord == 4
|
2014-06-28 17:24:24 -06:00
|
|
|
and (field->code() == DoubleField
|
|
|
|
or field->code() == LongField))
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
acquire(t, reinterpret_cast<object>(field));
|
2011-03-15 17:52:02 -06:00
|
|
|
} else {
|
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
}
|
|
|
|
}
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
2011-03-15 17:52:02 -06:00
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
releaseFieldForWrite(Thread* t, GcField* field)
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if (UNLIKELY(field->flags() & ACC_VOLATILE)) {
|
2011-03-15 17:52:02 -06:00
|
|
|
if (BytesPerWord == 4
|
2014-06-28 17:24:24 -06:00
|
|
|
and (field->code() == DoubleField
|
|
|
|
or field->code() == LongField))
|
2011-03-15 17:52:02 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
release(t, reinterpret_cast<object>(field));
|
2011-03-15 17:52:02 -06:00
|
|
|
} else {
|
|
|
|
storeLoadMemoryBarrier();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class FieldWriteResource {
|
|
|
|
public:
|
2014-06-28 17:24:24 -06:00
|
|
|
FieldWriteResource(Thread* t, GcField* o): o(o), protector(t, &(this->o)) {
|
2011-03-15 17:52:02 -06:00
|
|
|
acquireFieldForWrite(protector.t, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
~FieldWriteResource() {
|
|
|
|
releaseFieldForWrite(protector.t, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2014-06-28 17:24:24 -06:00
|
|
|
GcField* o;
|
2011-03-15 17:52:02 -06:00
|
|
|
Thread::SingleProtector protector;
|
|
|
|
};
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
2014-06-28 15:11:31 -06:00
|
|
|
resolveMethod(Thread* t, GcClassLoader* loader, GcMethod* method, unsigned index,
|
2011-03-15 17:52:02 -06:00
|
|
|
bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
return cast<GcMethod>(t, resolve(t, loader, method, index, findMethodInClass,
|
|
|
|
GcNoSuchMethodError::Type, throw_));
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
|
|
|
resolveMethod(Thread* t, GcMethod* method, unsigned index, bool throw_ = true)
|
2009-08-18 14:26:28 -06:00
|
|
|
{
|
|
|
|
return resolveMethod
|
2014-06-28 15:11:31 -06:00
|
|
|
(t, method->class_()->loader(), method, index, throw_);
|
2009-08-18 14:26:28 -06:00
|
|
|
}
|
|
|
|
|
2014-06-28 14:41:27 -06:00
|
|
|
GcVector*
|
|
|
|
vectorAppend(Thread*, GcVector*, object);
|
2010-11-26 12:41:31 -07:00
|
|
|
|
2014-06-26 18:17:46 -06:00
|
|
|
inline GcClassRuntimeData*
|
2014-06-28 17:24:24 -06:00
|
|
|
getClassRuntimeDataIfExists(Thread* t, GcClass* c)
|
2010-11-27 11:25:02 -07:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
if (c->runtimeDataIndex()) {
|
2014-06-26 18:17:46 -06:00
|
|
|
return cast<GcClassRuntimeData>(t, cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable))->body()[c->runtimeDataIndex() - 1]);
|
2010-11-27 11:25:02 -07:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcClassRuntimeData*
|
2014-05-28 22:17:25 -06:00
|
|
|
getClassRuntimeData(Thread* t, GcClass* c)
|
2010-09-01 10:13:52 -06:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
if (c->runtimeDataIndex() == 0) {
|
2010-09-10 15:05:29 -06:00
|
|
|
PROTECT(t, c);
|
|
|
|
|
2010-09-01 10:13:52 -06:00
|
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
if (c->runtimeDataIndex() == 0) {
|
|
|
|
object runtimeData = reinterpret_cast<object>(makeClassRuntimeData(t, 0, 0, 0, 0));
|
2010-11-26 12:41:31 -07:00
|
|
|
|
2014-06-28 14:41:27 -06:00
|
|
|
setRoot(t, Machine::ClassRuntimeDataTable, reinterpret_cast<object>(vectorAppend
|
|
|
|
(t, cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable)), runtimeData)));
|
2010-11-26 12:41:31 -07:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
c->runtimeDataIndex() = cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable))->size();
|
2010-11-26 12:41:31 -07:00
|
|
|
}
|
2010-09-01 10:13:52 -06:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcClassRuntimeData>(
|
|
|
|
t,
|
|
|
|
cast<GcVector>(t, root(t, Machine::ClassRuntimeDataTable))
|
|
|
|
->body()[c->runtimeDataIndex() - 1]);
|
2010-11-26 12:41:31 -07:00
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
inline GcMethodRuntimeData*
|
2014-05-28 22:17:25 -06:00
|
|
|
getMethodRuntimeData(Thread* t, GcMethod* method)
|
2010-11-26 12:41:31 -07:00
|
|
|
{
|
2014-05-28 22:17:25 -06:00
|
|
|
int index = method->runtimeDataIndex();
|
2011-04-10 14:46:53 -06:00
|
|
|
|
|
|
|
loadMemoryBarrier();
|
|
|
|
|
|
|
|
if (index == 0) {
|
2010-11-26 12:41:31 -07:00
|
|
|
PROTECT(t, method);
|
|
|
|
|
|
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
if (method->runtimeDataIndex() == 0) {
|
|
|
|
object runtimeData = reinterpret_cast<object>(makeMethodRuntimeData(t, 0));
|
2010-11-26 12:41:31 -07:00
|
|
|
|
2014-06-28 14:41:27 -06:00
|
|
|
setRoot(t, Machine::MethodRuntimeDataTable, reinterpret_cast<object>(vectorAppend
|
|
|
|
(t, cast<GcVector>(t, root(t, Machine::MethodRuntimeDataTable)), runtimeData)));
|
2010-11-26 12:41:31 -07:00
|
|
|
|
2011-04-10 14:46:53 -06:00
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
method->runtimeDataIndex() = cast<GcVector>(t, root(t, Machine::MethodRuntimeDataTable))->size();
|
2010-11-26 12:41:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
return cast<GcMethodRuntimeData>(
|
|
|
|
t,
|
|
|
|
cast<GcVector>(t, root(t, Machine::MethodRuntimeDataTable))
|
|
|
|
->body()[method->runtimeDataIndex() - 1]);
|
2010-11-26 12:41:31 -07:00
|
|
|
}
|
|
|
|
|
2014-06-20 22:16:33 -06:00
|
|
|
inline GcJclass*
|
2014-05-28 22:17:25 -06:00
|
|
|
getJClass(Thread* t, GcClass* c)
|
2010-11-26 12:41:31 -07:00
|
|
|
{
|
2010-12-09 22:17:57 -07:00
|
|
|
PROTECT(t, c);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcJclass* jclass = cast<GcJclass>(t, getClassRuntimeData(t, c)->jclass());
|
2011-04-10 14:46:53 -06:00
|
|
|
|
|
|
|
loadMemoryBarrier();
|
|
|
|
|
2010-09-01 10:13:52 -06:00
|
|
|
if (jclass == 0) {
|
|
|
|
ACQUIRE(t, t->m->classLock);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
jclass = cast<GcJclass>(t, getClassRuntimeData(t, c)->jclass());
|
2010-11-26 12:41:31 -07:00
|
|
|
if (jclass == 0) {
|
2014-06-20 22:16:33 -06:00
|
|
|
jclass = cast<GcJclass>(t, t->m->classpath->makeJclass(t, c));
|
2011-04-10 14:46:53 -06:00
|
|
|
|
|
|
|
storeStoreMemoryBarrier();
|
2014-05-28 22:17:25 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
set(t, getClassRuntimeData(t, c), ClassRuntimeDataJclass, jclass);
|
2010-11-26 12:41:31 -07:00
|
|
|
}
|
2010-09-01 10:13:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return jclass;
|
|
|
|
}
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcClass*
|
2010-09-10 15:05:29 -06:00
|
|
|
primitiveClass(Thread* t, char name)
|
|
|
|
{
|
|
|
|
switch (name) {
|
2014-05-28 22:17:25 -06:00
|
|
|
case 'B': return type(t, GcJbyte::Type);
|
|
|
|
case 'C': return type(t, GcJchar::Type);
|
|
|
|
case 'D': return type(t, GcJdouble::Type);
|
|
|
|
case 'F': return type(t, GcJfloat::Type);
|
|
|
|
case 'I': return type(t, GcJint::Type);
|
|
|
|
case 'J': return type(t, GcJlong::Type);
|
|
|
|
case 'S': return type(t, GcJshort::Type);
|
|
|
|
case 'V': return type(t, GcJvoid::Type);
|
|
|
|
case 'Z': return type(t, GcJboolean::Type);
|
|
|
|
default: throwNew(t, GcIllegalArgumentException::Type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-14 10:49:41 -06:00
|
|
|
inline void
|
2014-05-28 22:17:25 -06:00
|
|
|
registerNative(Thread* t, GcMethod* method, void* function)
|
2010-09-14 10:49:41 -06:00
|
|
|
{
|
|
|
|
PROTECT(t, method);
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
expect(t, method->flags() & ACC_NATIVE);
|
2010-09-14 10:49:41 -06:00
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcNative* native = makeNative(t, function, false);
|
2010-11-26 12:41:31 -07:00
|
|
|
PROTECT(t, native);
|
|
|
|
|
2014-06-28 17:24:24 -06:00
|
|
|
GcMethodRuntimeData* runtimeData = getMethodRuntimeData(t, method);
|
2010-09-14 10:49:41 -06:00
|
|
|
|
2010-11-26 12:41:31 -07:00
|
|
|
// ensure other threads only see the methodRuntimeDataNative field
|
|
|
|
// populated once the object it points to has been populated:
|
2010-09-14 10:49:41 -06:00
|
|
|
storeStoreMemoryBarrier();
|
|
|
|
|
2010-11-26 12:41:31 -07:00
|
|
|
set(t, runtimeData, MethodRuntimeDataNative, native);
|
2010-09-14 10:49:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2014-06-28 17:24:24 -06:00
|
|
|
unregisterNatives(Thread* t, GcClass* c)
|
2010-09-25 15:54:01 -06:00
|
|
|
{
|
2014-06-28 17:24:24 -06:00
|
|
|
GcArray* table = cast<GcArray>(t, c->methodTable());
|
|
|
|
if (table) {
|
|
|
|
for (unsigned i = 0; i < table->length(); ++i) {
|
|
|
|
GcMethod* method = cast<GcMethod>(t, table->body()[i]);
|
2014-05-28 22:17:25 -06:00
|
|
|
if (method->flags() & ACC_NATIVE) {
|
2010-11-26 12:41:31 -07:00
|
|
|
set(t, getMethodRuntimeData(t, method), MethodRuntimeDataNative, 0);
|
2010-09-14 10:49:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-09-10 15:05:29 -06:00
|
|
|
|
2011-03-26 11:15:52 -06:00
|
|
|
void
|
|
|
|
populateMultiArray(Thread* t, object array, int32_t* counts,
|
|
|
|
unsigned index, unsigned dimensions);
|
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
GcMethod*
|
2012-09-22 20:22:33 -06:00
|
|
|
getCaller(Thread* t, unsigned target, bool skipMethodInvoke = false);
|
2010-09-25 15:54:01 -06:00
|
|
|
|
2010-09-10 15:05:29 -06:00
|
|
|
object
|
2014-06-28 15:11:31 -06:00
|
|
|
defineClass(Thread* t, GcClassLoader* loader, const uint8_t* buffer, unsigned length);
|
2010-09-10 15:05:29 -06:00
|
|
|
|
2014-05-28 22:17:25 -06:00
|
|
|
inline GcMethod*
|
|
|
|
methodClone(Thread* t, GcMethod* method)
|
2010-11-04 11:02:09 -06:00
|
|
|
{
|
|
|
|
return makeMethod
|
2014-05-28 22:17:25 -06:00
|
|
|
(t, method->vmFlags(),
|
|
|
|
method->returnCode(),
|
|
|
|
method->parameterCount(),
|
|
|
|
method->parameterFootprint(),
|
|
|
|
method->flags(),
|
|
|
|
method->offset(),
|
|
|
|
method->nativeID(),
|
|
|
|
method->runtimeDataIndex(),
|
|
|
|
method->name(),
|
|
|
|
method->spec(),
|
|
|
|
method->addendum(),
|
|
|
|
method->class_(),
|
|
|
|
method->code());
|
2010-11-04 11:02:09 -06:00
|
|
|
}
|
|
|
|
|
2011-08-29 19:00:17 -06:00
|
|
|
inline uint64_t
|
|
|
|
exceptionHandler(uint64_t start, uint64_t end, uint64_t ip, uint64_t catchType)
|
|
|
|
{
|
|
|
|
return (start << 48) | (end << 32) | (ip << 16) | catchType;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
exceptionHandlerStart(uint64_t eh)
|
|
|
|
{
|
|
|
|
return eh >> 48;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
exceptionHandlerEnd(uint64_t eh)
|
|
|
|
{
|
|
|
|
return (eh >> 32) & 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
exceptionHandlerIp(uint64_t eh)
|
|
|
|
{
|
|
|
|
return (eh >> 16) & 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
exceptionHandlerCatchType(uint64_t eh)
|
|
|
|
{
|
|
|
|
return eh & 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline uint64_t
|
|
|
|
lineNumber(uint64_t ip, uint64_t line)
|
|
|
|
{
|
|
|
|
return (ip << 32) | line;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
lineNumberIp(uint64_t ln)
|
|
|
|
{
|
|
|
|
return ln >> 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
lineNumberLine(uint64_t ln)
|
|
|
|
{
|
|
|
|
return ln & 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
2013-04-23 13:47:15 -06:00
|
|
|
object
|
2014-06-28 17:24:24 -06:00
|
|
|
interruptLock(Thread* t, GcThread* thread);
|
2013-04-23 13:47:15 -06:00
|
|
|
|
|
|
|
void
|
|
|
|
clearInterrupted(Thread* t);
|
|
|
|
|
|
|
|
void
|
2014-06-28 17:24:24 -06:00
|
|
|
threadInterrupt(Thread* t, GcThread* thread);
|
2013-04-23 13:47:15 -06:00
|
|
|
|
|
|
|
bool
|
2014-06-28 17:24:24 -06:00
|
|
|
threadIsInterrupted(Thread* t, GcThread* thread, bool clear);
|
2013-04-23 13:47:15 -06:00
|
|
|
|
2011-03-15 17:27:17 -06:00
|
|
|
inline FILE*
|
|
|
|
errorLog(Thread* t)
|
|
|
|
{
|
|
|
|
if (t->m->errorLog == 0) {
|
|
|
|
const char* path = findProperty(t, "avian.error.log");
|
|
|
|
if (path) {
|
|
|
|
t->m->errorLog = vm::fopen(path, "wb");
|
|
|
|
} else {
|
|
|
|
t->m->errorLog = stderr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return t->m->errorLog;
|
|
|
|
}
|
|
|
|
|
2007-06-24 20:02:24 -06:00
|
|
|
} // namespace vm
|
|
|
|
|
2013-11-08 08:35:10 -07:00
|
|
|
AVIAN_EXPORT void*
|
2009-06-11 09:45:35 -06:00
|
|
|
vmAddressFromLine(vm::Thread* t, vm::object m, unsigned line);
|
|
|
|
|
2007-07-06 17:50:26 -06:00
|
|
|
#endif//MACHINE_H
|