mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Restructured android threading code to re-use threading code in juce_posix_Shared.h
This commit is contained in:
parent
6c7bb37720
commit
86e28d1e42
5 changed files with 102 additions and 292 deletions
|
|
@ -1211,36 +1211,8 @@ public class JuceAppActivity extends Activity
|
|||
return null;
|
||||
}
|
||||
|
||||
public final int setCurrentThreadPriority (int priority)
|
||||
{
|
||||
android.os.Process.setThreadPriority (android.os.Process.myTid(), priority);
|
||||
return android.os.Process.getThreadPriority (android.os.Process.myTid());
|
||||
}
|
||||
|
||||
public final boolean hasSystemFeature (String property)
|
||||
{
|
||||
return getPackageManager().hasSystemFeature (property);
|
||||
}
|
||||
|
||||
private static class JuceThread extends Thread
|
||||
{
|
||||
public JuceThread (long host, String threadName, long threadStackSize)
|
||||
{
|
||||
super (null, null, threadName, threadStackSize);
|
||||
_this = host;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
runThread(_this);
|
||||
}
|
||||
|
||||
private native void runThread (long host);
|
||||
private long _this;
|
||||
}
|
||||
|
||||
public final Thread createNewThread(long host, String threadName, long threadStackSize)
|
||||
{
|
||||
return new JuceThread(host, threadName, threadStackSize);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,9 +293,7 @@ extern AndroidSystem android;
|
|||
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \
|
||||
METHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \
|
||||
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
|
||||
METHOD (setCurrentThreadPriority, "setCurrentThreadPriority", "(I)I") \
|
||||
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
|
||||
METHOD (createNewThread, "createNewThread", "(JLjava/lang/String;J)Ljava/lang/Thread;") \
|
||||
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \
|
||||
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
|
||||
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \
|
||||
|
|
@ -329,19 +327,6 @@ DECLARE_JNI_CLASS (Paint, "android/graphics/Paint");
|
|||
DECLARE_JNI_CLASS (Matrix, "android/graphics/Matrix");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (start, "start", "()V") \
|
||||
METHOD (stop, "stop", "()V") \
|
||||
METHOD (setName, "setName", "(Ljava/lang/String;)V") \
|
||||
METHOD (getName, "getName", "()Ljava/lang/String;") \
|
||||
METHOD (getId, "getId", "()J") \
|
||||
STATICMETHOD (currentThread, "currentThread", "()Ljava/lang/Thread;") \
|
||||
METHOD (setPriority, "setPriority", "(I)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (JuceThread, "java/lang/Thread");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "(IIII)V") \
|
||||
|
|
|
|||
|
|
@ -100,23 +100,90 @@ jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, c
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
ThreadLocalValue<JNIEnv*> androidJNIEnv;
|
||||
JavaVM* androidJNIJavaVM = nullptr;
|
||||
|
||||
JNIEnv* getEnv() noexcept
|
||||
class JniEnvThreadHolder
|
||||
{
|
||||
JNIEnv* env = androidJNIEnv.get();
|
||||
jassert (env != nullptr);
|
||||
public:
|
||||
static JniEnvThreadHolder& getInstance() noexcept
|
||||
{
|
||||
// You cann only use JNI functions AFTER JNI_OnLoad was called
|
||||
jassert (androidJNIJavaVM != nullptr);
|
||||
|
||||
return env;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (instance == nullptr)
|
||||
instance = new JniEnvThreadHolder;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
jassertfalse;
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
void setEnv (JNIEnv* env) noexcept
|
||||
return *instance;
|
||||
}
|
||||
|
||||
static JNIEnv* getEnv()
|
||||
{
|
||||
JNIEnv* env = reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey));
|
||||
|
||||
// You are trying to use a JUCE function on a thread that was not created by JUCE.
|
||||
// You need to first call setEnv on this thread before using JUCE
|
||||
jassert (env != nullptr);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
static void setEnv (JNIEnv* env)
|
||||
{
|
||||
// env must not be a nullptr
|
||||
jassert (env != nullptr);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
JNIEnv* oldenv = reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey));
|
||||
|
||||
// This thread is already attached to the JavaVM and you trying to attach
|
||||
// it to a different instance of the VM.
|
||||
jassert (oldenv == nullptr || oldenv == env);
|
||||
#endif
|
||||
|
||||
pthread_setspecific (getInstance().threadKey, env);
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_key_t threadKey;
|
||||
|
||||
static void threadDetach (void* p)
|
||||
{
|
||||
if (JNIEnv* env = reinterpret_cast<JNIEnv*> (p))
|
||||
{
|
||||
ignoreUnused (env);
|
||||
|
||||
androidJNIJavaVM->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
JniEnvThreadHolder()
|
||||
{
|
||||
pthread_key_create (&threadKey, threadDetach);
|
||||
}
|
||||
|
||||
static JniEnvThreadHolder* instance;
|
||||
};
|
||||
|
||||
JniEnvThreadHolder* JniEnvThreadHolder::instance = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
JNIEnv* getEnv() noexcept { return JniEnvThreadHolder::getEnv(); }
|
||||
void setEnv (JNIEnv* env) noexcept { JniEnvThreadHolder::setEnv (env); }
|
||||
|
||||
extern "C" jint JNI_OnLoad (JavaVM* vm, void*)
|
||||
{
|
||||
androidJNIEnv.get() = env;
|
||||
}
|
||||
// Huh? JNI_OnLoad was called two times!
|
||||
jassert (androidJNIJavaVM == nullptr);
|
||||
|
||||
extern "C" jint JNI_OnLoad (JavaVM*, void*)
|
||||
{
|
||||
androidJNIJavaVM = vm;
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
|
||||
|
|
@ -127,6 +194,8 @@ AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160)
|
|||
|
||||
void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir)
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
screenWidth = screenHeight = 0;
|
||||
dpi = 160;
|
||||
JNIClassBase::initialiseAllClasses (env);
|
||||
|
|
|
|||
|
|
@ -78,234 +78,3 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
|||
|
||||
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {}
|
||||
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {}
|
||||
|
||||
struct AndroidThreadData
|
||||
{
|
||||
AndroidThreadData (Thread* thread) noexcept
|
||||
: owner (thread), tId (0)
|
||||
{
|
||||
}
|
||||
|
||||
Thread* owner;
|
||||
Thread::ThreadID tId;
|
||||
WaitableEvent eventSet, eventGet;
|
||||
};
|
||||
|
||||
void JUCE_API juce_threadEntryPoint (void*);
|
||||
|
||||
void* threadEntryProc (AndroidThreadData* priv)
|
||||
{
|
||||
priv->tId = (Thread::ThreadID) pthread_self();
|
||||
priv->eventSet.signal();
|
||||
priv->eventGet.wait (-1);
|
||||
|
||||
juce_threadEntryPoint (priv->owner);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceThread), runThread,
|
||||
void, (JNIEnv* env, jobject /*device*/, jlong host))
|
||||
{
|
||||
// This thread does not have a JNIEnv assigned to it yet. So assign it now.
|
||||
setEnv (env);
|
||||
|
||||
if (AndroidThreadData* thread = reinterpret_cast<AndroidThreadData*> (host))
|
||||
threadEntryProc (thread);
|
||||
}
|
||||
|
||||
void Thread::launchThread()
|
||||
{
|
||||
threadHandle = 0;
|
||||
|
||||
ScopedPointer<AndroidThreadData> threadPrivateData = new AndroidThreadData (this);
|
||||
const LocalRef<jstring> jName (javaString (threadName));
|
||||
|
||||
jobject juceNewThread = android.activity.callObjectMethod (JuceAppActivity.createNewThread,
|
||||
(jlong) threadPrivateData.get(),
|
||||
jName.get(), (jlong) threadStackSize);
|
||||
|
||||
if (jobject juceThread = getEnv()->NewGlobalRef (juceNewThread))
|
||||
{
|
||||
AndroidThreadData* priv = threadPrivateData.release();
|
||||
|
||||
threadHandle = (void*) juceThread;
|
||||
getEnv()->CallVoidMethod (juceThread, JuceThread.start);
|
||||
|
||||
priv->eventSet.wait (-1);
|
||||
threadId = priv->tId;
|
||||
priv->eventGet.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::closeThreadHandle()
|
||||
{
|
||||
if (threadHandle != 0)
|
||||
{
|
||||
jobject juceThread = reinterpret_cast<jobject> (threadHandle);
|
||||
getEnv()->DeleteGlobalRef (juceThread);
|
||||
threadHandle = 0;
|
||||
}
|
||||
|
||||
threadId = 0;
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle != 0)
|
||||
{
|
||||
jobject juceThread = reinterpret_cast<jobject> (threadHandle);
|
||||
getEnv()->CallVoidMethod (juceThread, JuceThread.stop);
|
||||
}
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
|
||||
{
|
||||
LocalRef<jobject> juceThread (getEnv()->CallStaticObjectMethod (JuceThread, JuceThread.currentThread));
|
||||
|
||||
if (jobject t = juceThread.get())
|
||||
getEnv()->CallVoidMethod (t, JuceThread.setName, javaString (name).get());
|
||||
}
|
||||
|
||||
bool Thread::setThreadPriority (void* handle, int priority)
|
||||
{
|
||||
if (handle == nullptr)
|
||||
{
|
||||
LocalRef<jobject> juceThread (getEnv()->CallStaticObjectMethod (JuceThread, JuceThread.currentThread));
|
||||
|
||||
if (jobject t = juceThread.get())
|
||||
return setThreadPriority (t, priority);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
jobject juceThread = reinterpret_cast<jobject> (handle);
|
||||
|
||||
const int minPriority = 1;
|
||||
const int maxPriority = 10;
|
||||
|
||||
jint javaPriority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
|
||||
|
||||
getEnv()->CallVoidMethod (juceThread, JuceThread.setPriority, javaPriority);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct HighResolutionTimer::Pimpl
|
||||
{
|
||||
struct HighResolutionThread : public Thread
|
||||
{
|
||||
HighResolutionThread (HighResolutionTimer::Pimpl& parent)
|
||||
: Thread ("High Resolution Timer"), pimpl (parent)
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
pimpl.timerThread();
|
||||
}
|
||||
|
||||
private:
|
||||
HighResolutionTimer::Pimpl& pimpl;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Pimpl (HighResolutionTimer& t) : owner (t) {}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void start (int newPeriod)
|
||||
{
|
||||
if (periodMs != newPeriod)
|
||||
{
|
||||
if (thread.get() == nullptr
|
||||
|| thread->getThreadId() != Thread::getCurrentThreadId()
|
||||
|| thread->threadShouldExit())
|
||||
{
|
||||
stop();
|
||||
|
||||
periodMs = newPeriod;
|
||||
|
||||
thread = new HighResolutionThread (*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
periodMs = newPeriod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (thread.get() != nullptr)
|
||||
{
|
||||
thread->signalThreadShouldExit();
|
||||
|
||||
if (thread->getThreadId() != Thread::getCurrentThreadId())
|
||||
{
|
||||
thread->waitForThreadToExit (-1);
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HighResolutionTimer& owner;
|
||||
int volatile periodMs;
|
||||
|
||||
private:
|
||||
ScopedPointer<Thread> thread;
|
||||
|
||||
void timerThread()
|
||||
{
|
||||
jassert (thread.get() != nullptr);
|
||||
|
||||
int lastPeriod = periodMs;
|
||||
Clock clock (lastPeriod);
|
||||
|
||||
while (! thread->threadShouldExit())
|
||||
{
|
||||
clock.wait();
|
||||
owner.hiResTimerCallback();
|
||||
|
||||
if (lastPeriod != periodMs)
|
||||
{
|
||||
lastPeriod = periodMs;
|
||||
clock = Clock (lastPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
periodMs = 0;
|
||||
}
|
||||
|
||||
struct Clock
|
||||
{
|
||||
Clock (double millis) noexcept : delta ((uint64) (millis * 1000000))
|
||||
{
|
||||
}
|
||||
|
||||
void wait() noexcept
|
||||
{
|
||||
struct timespec t;
|
||||
t.tv_sec = (time_t) (delta / 1000000000);
|
||||
t.tv_nsec = (long) (delta % 1000000000);
|
||||
nanosleep (&t, nullptr);
|
||||
}
|
||||
|
||||
uint64 delta;
|
||||
};
|
||||
|
||||
static bool setThreadToRealtime (pthread_t thread, uint64 periodMs)
|
||||
{
|
||||
ignoreUnused (periodMs);
|
||||
struct sched_param param;
|
||||
param.sched_priority = sched_get_priority_max (SCHED_RR);
|
||||
return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -906,12 +906,25 @@ void InterProcessLock::exit()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_ANDROID
|
||||
void JUCE_API juce_threadEntryPoint (void*);
|
||||
|
||||
#if JUCE_ANDROID
|
||||
extern JavaVM* androidJNIJavaVM;
|
||||
#endif
|
||||
|
||||
extern "C" void* threadEntryProc (void*);
|
||||
extern "C" void* threadEntryProc (void* userData)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
// JNI_OnLoad was not called - make sure you load the JUCE shared library
|
||||
// using System.load inside of Java
|
||||
jassert (androidJNIJavaVM != nullptr);
|
||||
|
||||
JNIEnv* env;
|
||||
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
|
||||
setEnv (env);
|
||||
#endif
|
||||
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
juce_threadEntryPoint (userData);
|
||||
|
|
@ -999,7 +1012,6 @@ bool Thread::setThreadPriority (void* handle, int priority)
|
|||
param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
|
||||
return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId()
|
||||
{
|
||||
|
|
@ -1233,7 +1245,6 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_ANDROID
|
||||
struct HighResolutionTimer::Pimpl
|
||||
{
|
||||
Pimpl (HighResolutionTimer& t) : owner (t), thread (0), destroyThread (false), isRunning (false)
|
||||
|
|
@ -1241,7 +1252,7 @@ struct HighResolutionTimer::Pimpl
|
|||
pthread_condattr_t attr;
|
||||
pthread_condattr_init (&attr);
|
||||
|
||||
#if ! (JUCE_MAC || JUCE_IOS)
|
||||
#if JUCE_LINUX || (JUCE_ANDROID && defined(__ANDROID_API__) && __ANDROID_API__ >= 21)
|
||||
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
|
||||
#endif
|
||||
|
||||
|
|
@ -1320,7 +1331,13 @@ private:
|
|||
static void* timerThread (void* param)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
const AndroidThreadScope androidEnv;
|
||||
// JNI_OnLoad was not called - make sure you load the JUCE shared library
|
||||
// using System.load inside of Java
|
||||
jassert (androidJNIJavaVM != nullptr);
|
||||
|
||||
JNIEnv* env;
|
||||
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
|
||||
setEnv (env);
|
||||
#else
|
||||
int dummy;
|
||||
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy);
|
||||
|
|
@ -1469,5 +1486,3 @@ private:
|
|||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue