/*----------------------------------------------------------------------------*/ /* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "HAL/Interrupts.h" #include "HAL/cpp/Log.h" #include "HALUtil.h" #include "edu_wpi_first_wpilibj_hal_InterruptJNI.h" using namespace frc; TLogLevel interruptJNILogLevel = logERROR; #define INTERRUPTJNI_LOG(level) \ if (level > interruptJNILogLevel) \ ; \ else \ Log().Get(level) // Thread where callbacks are actually performed. // // JNI's AttachCurrentThread() creates a Java Thread object on every // invocation, which is both time inefficient and causes issues with Eclipse // (which tries to keep a thread list up-to-date and thus gets swamped). // // Instead, this class attaches just once. When a hardware notification // occurs, a condition variable wakes up this thread and this thread actually // makes the call into Java. // // We don't want to use a FIFO here. If the user code takes too long to // process, we will just ignore the redundant wakeup. class InterruptThreadJNI : public wpi::SafeThread { public: void Main(); bool m_notify = false; uint32_t m_mask = 0; jobject m_func = nullptr; jmethodID m_mid; jobject m_param = nullptr; }; class InterruptJNI : public wpi::SafeThreadOwner { public: void SetFunc(JNIEnv* env, jobject func, jmethodID mid, jobject param); void Notify(uint32_t mask) { auto thr = GetThread(); if (!thr) return; thr->m_notify = true; thr->m_mask = mask; thr->m_cond.notify_one(); } }; void InterruptJNI::SetFunc(JNIEnv* env, jobject func, jmethodID mid, jobject param) { auto thr = GetThread(); if (!thr) return; // free global references if (thr->m_func) env->DeleteGlobalRef(thr->m_func); if (thr->m_param) env->DeleteGlobalRef(thr->m_param); // create global references thr->m_func = env->NewGlobalRef(func); thr->m_param = param ? env->NewGlobalRef(param) : nullptr; thr->m_mid = mid; } void InterruptThreadJNI::Main() { JNIEnv* env; JavaVMAttachArgs args; args.version = JNI_VERSION_1_2; args.name = const_cast("Interrupt"); args.group = nullptr; jint rs = GetJVM()->AttachCurrentThreadAsDaemon( reinterpret_cast(&env), &args); if (rs != JNI_OK) return; std::unique_lock lock(m_mutex); while (m_active) { m_cond.wait(lock, [&] { return !m_active || m_notify; }); if (!m_active) break; m_notify = false; if (!m_func) continue; jobject func = m_func; jmethodID mid = m_mid; uint32_t mask = m_mask; jobject param = m_param; lock.unlock(); // don't hold mutex during callback execution env->CallVoidMethod(func, mid, static_cast(mask), param); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } lock.lock(); } // free global references if (m_func) env->DeleteGlobalRef(m_func); if (m_param) env->DeleteGlobalRef(m_param); GetJVM()->DetachCurrentThread(); } void interruptHandler(uint32_t mask, void* param) { static_cast(param)->Notify(mask); } extern "C" { /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: initializeInterrupts * Signature: (Z)I */ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_initializeInterrupts (JNIEnv* env, jclass, jboolean watcher) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI initializeInterrupts"; INTERRUPTJNI_LOG(logDEBUG) << "watcher = " << static_cast(watcher); int32_t status = 0; HAL_InterruptHandle interrupt = HAL_InitializeInterrupts(watcher, &status); INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << interrupt; INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatusForceThrow(env, status); return (jint)interrupt; } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: cleanInterrupts * Signature: (I)V */ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_cleanInterrupts (JNIEnv* env, jclass, jint interruptHandle) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI cleanInterrupts"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; int32_t status = 0; auto param = HAL_CleanInterrupts((HAL_InterruptHandle)interruptHandle, &status); if (param) { delete static_cast(param); } INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; // ignore status, as an invalid handle just needs to be ignored. } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: waitForInterrupt * Signature: (IDZ)I */ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_waitForInterrupt (JNIEnv* env, jclass, jint interruptHandle, jdouble timeout, jboolean ignorePrevious) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI waitForInterrupt"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; int32_t status = 0; int32_t result = HAL_WaitForInterrupt((HAL_InterruptHandle)interruptHandle, timeout, ignorePrevious, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); return result; } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: enableInterrupts * Signature: (I)V */ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_enableInterrupts (JNIEnv* env, jclass, jint interruptHandle) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI enableInterrupts"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; int32_t status = 0; HAL_EnableInterrupts((HAL_InterruptHandle)interruptHandle, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: disableInterrupts * Signature: (I)V */ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_disableInterrupts (JNIEnv* env, jclass, jint interruptHandle) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI disableInterrupts"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; int32_t status = 0; HAL_DisableInterrupts((HAL_InterruptHandle)interruptHandle, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: readInterruptRisingTimestamp * Signature: (I)D */ JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_readInterruptRisingTimestamp (JNIEnv* env, jclass, jint interruptHandle) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI readInterruptRisingTimestamp"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; int32_t status = 0; jdouble timeStamp = HAL_ReadInterruptRisingTimestamp( (HAL_InterruptHandle)interruptHandle, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); return timeStamp; } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: readInterruptFallingTimestamp * Signature: (I)D */ JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_readInterruptFallingTimestamp (JNIEnv* env, jclass, jint interruptHandle) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI readInterruptFallingTimestamp"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; int32_t status = 0; jdouble timeStamp = HAL_ReadInterruptFallingTimestamp( (HAL_InterruptHandle)interruptHandle, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); return timeStamp; } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: requestInterrupts * Signature: (III)V */ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_requestInterrupts (JNIEnv* env, jclass, jint interruptHandle, jint digitalSourceHandle, jint analogTriggerType) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI requestInterrupts"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; INTERRUPTJNI_LOG(logDEBUG) << "digitalSourceHandle = " << digitalSourceHandle; INTERRUPTJNI_LOG(logDEBUG) << "analogTriggerType = " << analogTriggerType; int32_t status = 0; HAL_RequestInterrupts((HAL_InterruptHandle)interruptHandle, (HAL_Handle)digitalSourceHandle, (HAL_AnalogTriggerType)analogTriggerType, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: attachInterruptHandler * Signature: (ILjava/lang/Object;Ljava/lang/Object;)V */ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_attachInterruptHandler (JNIEnv* env, jclass, jint interruptHandle, jobject handler, jobject param) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI attachInterruptHandler"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; jclass cls = env->GetObjectClass(handler); INTERRUPTJNI_LOG(logDEBUG) << "class = " << cls; if (cls == 0) { INTERRUPTJNI_LOG(logERROR) << "Error getting java class"; assert(false); return; } jmethodID mid = env->GetMethodID(cls, "apply", "(ILjava/lang/Object;)V"); INTERRUPTJNI_LOG(logDEBUG) << "method = " << mid; if (mid == 0) { INTERRUPTJNI_LOG(logERROR) << "Error getting java method ID"; assert(false); return; } InterruptJNI* intr = new InterruptJNI; intr->Start(); intr->SetFunc(env, handler, mid, param); INTERRUPTJNI_LOG(logDEBUG) << "InterruptThreadJNI Ptr = " << intr; int32_t status = 0; HAL_AttachInterruptHandler((HAL_InterruptHandle)interruptHandle, interruptHandler, intr, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); } /* * Class: edu_wpi_first_wpilibj_hal_InterruptJNI * Method: setInterruptUpSourceEdge * Signature: (IZZ)V */ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_setInterruptUpSourceEdge (JNIEnv* env, jclass, jint interruptHandle, jboolean risingEdge, jboolean fallingEdge) { INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI setInterruptUpSourceEdge"; INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Handle = " << (HAL_InterruptHandle)interruptHandle; INTERRUPTJNI_LOG(logDEBUG) << "Rising Edge = " << static_cast(risingEdge); INTERRUPTJNI_LOG(logDEBUG) << "Falling Edge = " << static_cast(fallingEdge); int32_t status = 0; HAL_SetInterruptUpSourceEdge((HAL_InterruptHandle)interruptHandle, risingEdge, fallingEdge, &status); INTERRUPTJNI_LOG(logDEBUG) << "Status = " << status; CheckStatus(env, status); } } // extern "C"