Browse Source

Restructured android threading code to re-use threading code in juce_posix_Shared.h

tags/2021-05-28
hogliux 8 years ago
parent
commit
86e28d1e42
5 changed files with 102 additions and 292 deletions
  1. +0
    -28
      modules/juce_core/native/java/JuceAppActivity.java
  2. +0
    -15
      modules/juce_core/native/juce_android_JNIHelpers.h
  3. +80
    -11
      modules/juce_core/native/juce_android_SystemStats.cpp
  4. +0
    -231
      modules/juce_core/native/juce_android_Threads.cpp
  5. +22
    -7
      modules/juce_core/native/juce_posix_SharedCode.h

+ 0
- 28
modules/juce_core/native/java/JuceAppActivity.java View File

@@ -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);
}
}

+ 0
- 15
modules/juce_core/native/juce_android_JNIHelpers.h View File

@@ -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") \


+ 80
- 11
modules/juce_core/native/juce_android_SystemStats.cpp View File

@@ -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);
try
{
if (instance == nullptr)
instance = new JniEnvThreadHolder;
}
catch (...)
{
jassertfalse;
std::terminate();
}
return *instance;
}
return env;
}
static JNIEnv* getEnv()
{
JNIEnv* env = reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey));
void setEnv (JNIEnv* env) noexcept
{
androidJNIEnv.get() = env;
}
// 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);
}
extern "C" jint JNI_OnLoad (JavaVM*, void*)
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*)
{
// Huh? JNI_OnLoad was called two times!
jassert (androidJNIJavaVM == nullptr);
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);


+ 0
- 231
modules/juce_core/native/juce_android_Threads.cpp View File

@@ -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, &param) == 0;
}
JUCE_DECLARE_NON_COPYABLE (Pimpl)
};

+ 22
- 7
modules/juce_core/native/juce_posix_SharedCode.h View File

@@ -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, &param) == 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…
Cancel
Save