#include "jagex3_jagmisc_jagmisc.h" #include #include #include #include #include #define WAIT_OBJECT_1 (WAIT_OBJECT_0 + 1) #define JAGMISC_SPIN_COUNT 4000 #define JAGMISC_NANOS_PER_SEC 1000000000LL // TODO(gpe): do any of these need to be volatile? static bool jagmisc_unpinned; static LARGE_INTEGER jagmisc_freq; static CRITICAL_SECTION jagmisc_lock; static HANDLE jagmisc_start_event; static HANDLE jagmisc_time_request_event; static HANDLE jagmisc_time_ready_event; static HANDLE jagmisc_stop_event; static HANDLE jagmisc_thread; static int64_t jagmisc_time; static inline int64_t jagmisc_to_nanos(LARGE_INTEGER count) { return count.QuadPart * JAGMISC_NANOS_PER_SEC / jagmisc_freq.QuadPart; } static DWORD WINAPI jagmisc_run(LPVOID lpParameter) { // pin this thread to the system's first processor HANDLE thread = GetCurrentThread(); DWORD_PTR affinity_mask = 0x1; bool success = SetThreadAffinityMask(thread, affinity_mask); CloseHandle(thread); if (!success) { return EXIT_FAILURE; } // notify Java_jagex3_jagmisc_jagmisc_init() that the thread has started successfully if (!SetEvent(jagmisc_start_event)) { return EXIT_FAILURE; } // serve time request and stop events const HANDLE handles[] = { jagmisc_time_request_event, jagmisc_stop_event }; for (;;) { switch (WaitForMultipleObjects(2, handles, FALSE, INFINITE)) { case WAIT_OBJECT_0: LARGE_INTEGER count; if (QueryPerformanceCounter(&count)) { jagmisc_time = jagmisc_to_nanos(count); } else { jagmisc_time = 0; } if (!SetEvent(jagmisc_time_ready_event)) { return EXIT_FAILURE; } break; case WAIT_OBJECT_1: return EXIT_SUCCESS; default: return EXIT_FAILURE; } } } static void jagmisc_stop(void) { // TODO(gpe): think about what to do if we fail to stop the thread. We // could call TerminateThread (which sounds dangerous based on the // documentation) or throw a Java exception. The current behaviour of // ignoring the failure is okay as long as jagmisc.init() is never called // again in the same JVM. if (SetEvent(jagmisc_stop_event)) { WaitForSingleObject(jagmisc_thread, INFINITE); } CloseHandle(jagmisc_thread); jagmisc_thread = NULL; CloseHandle(jagmisc_stop_event); jagmisc_stop_event = NULL; CloseHandle(jagmisc_time_ready_event); jagmisc_time_ready_event = NULL; CloseHandle(jagmisc_time_request_event); jagmisc_time_request_event = NULL; CloseHandle(jagmisc_start_event); jagmisc_start_event = NULL; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { /* * We only need to pin the timer thread to a single processor prior to * Windows Vista. */ jagmisc_unpinned = IsWindowsVistaOrGreater(); if (jagmisc_unpinned) { return TRUE; } if (!InitializeCriticalSectionAndSpinCount(&jagmisc_lock, JAGMISC_SPIN_COUNT)) { return FALSE; } } return TRUE; } JNIEXPORT jboolean JNICALL Java_jagex3_jagmisc_jagmisc_init(JNIEnv *env, jclass cls) { if (jagmisc_unpinned) { return (jboolean) QueryPerformanceFrequency(&jagmisc_freq); } EnterCriticalSection(&jagmisc_lock); jboolean success = JNI_FALSE; if (jagmisc_thread) { jagmisc_stop(); } if (!QueryPerformanceFrequency(&jagmisc_freq)) { goto unlock; } jagmisc_start_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!jagmisc_start_event) { goto unlock; } jagmisc_time_request_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!jagmisc_time_request_event) { goto create_time_request_event_failed; } jagmisc_time_ready_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!jagmisc_time_ready_event) { goto create_time_ready_event_failed; } jagmisc_stop_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!jagmisc_stop_event) { goto create_stop_event_failed; } jagmisc_thread = CreateThread(NULL, 0, &jagmisc_run, NULL, 0, NULL); if (!jagmisc_thread) { goto create_thread_failed; } const HANDLE handles[] = { jagmisc_start_event, jagmisc_thread }; DWORD result = WaitForMultipleObjects(2, handles, FALSE, INFINITE); if (result != WAIT_OBJECT_0) { goto start_failed; } success = JNI_TRUE; goto unlock; start_failed: CloseHandle(jagmisc_thread); jagmisc_thread = NULL; create_thread_failed: CloseHandle(jagmisc_stop_event); jagmisc_stop_event = NULL; create_stop_event_failed: CloseHandle(jagmisc_time_ready_event); jagmisc_time_ready_event = NULL; create_time_ready_event_failed: CloseHandle(jagmisc_time_request_event); jagmisc_time_request_event = NULL; create_time_request_event_failed: CloseHandle(jagmisc_start_event); jagmisc_start_event = NULL; unlock: LeaveCriticalSection(&jagmisc_lock); return success; } JNIEXPORT void JNICALL Java_jagex3_jagmisc_jagmisc_Quit0(JNIEnv *env, jclass cls) { if (jagmisc_unpinned) { return; } EnterCriticalSection(&jagmisc_lock); if (jagmisc_thread) { jagmisc_stop(); } LeaveCriticalSection(&jagmisc_lock); } JNIEXPORT jlong JNICALL Java_jagex3_jagmisc_jagmisc_nanoTime(JNIEnv *env, jclass cls) { if (jagmisc_unpinned) { LARGE_INTEGER count; if (!QueryPerformanceCounter(&count)) { return 0; } return jagmisc_to_nanos(count); } EnterCriticalSection(&jagmisc_lock); jlong time = 0; if (!jagmisc_thread) { goto unlock; } if (!SetEvent(jagmisc_time_request_event)) { goto unlock; } const HANDLE handles[] = { jagmisc_time_ready_event, jagmisc_thread }; DWORD result = WaitForMultipleObjects(2, handles, FALSE, INFINITE); if (result != WAIT_OBJECT_0) { goto unlock; } time = jagmisc_time; unlock: LeaveCriticalSection(&jagmisc_lock); return time; }