1 /* 2 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import "common.h" 27 #import "com_sun_glass_ui_mac_MacTimer.h" 28 29 #import "com_sun_glass_ui_Timer.h" 30 31 #import "ProcessInfo.h" 32 #import "GlassMacros.h" 33 #import "GlassHelper.h" 34 #import "GlassTimer.h" 35 36 #include <sys/time.h> 37 38 //#define VERBOSE 39 #ifndef VERBOSE 40 #define LOG(MSG, ...) 41 #else 42 #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__); 43 #endif 44 45 #define HIGH_PRIORITY_TIMER 1 46 47 static void *_GlassTimerTask(void *data) 48 { 49 pthread_setname_np("Glass Timer Thread"); 50 51 GlassTimer *timer = (GlassTimer*)data; 52 if (timer != NULL) 53 { 54 jint error = (*MAIN_JVM)->AttachCurrentThreadAsDaemon(MAIN_JVM, (void **)&timer->_env, NULL); 55 if (error == 0) 56 { 57 timer->_running = YES; 58 while (timer->_running == YES) 59 { 60 int64_t now = getTimeMicroseconds(); 61 { 62 if (timer->_runnable != NULL) 63 { 64 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 65 { 66 (*timer->_env)->CallVoidMethod(timer->_env, timer->_runnable, jRunnableRun); 67 } 68 [pool drain]; 69 GLASS_CHECK_EXCEPTION(timer->_env); 70 } 71 } 72 int64_t duration = (getTimeMicroseconds() - now); 73 int64_t sleep = (timer->_period - duration); 74 if ((sleep < 0) || (sleep > timer->_period)) 75 { 76 #if 1 77 sched_yield(); 78 #else 79 pthread_yield_np(); 80 #endif 81 } 82 else 83 { 84 usleep((useconds_t)sleep); 85 } 86 } 87 88 if (timer->_runnable != NULL) 89 { 90 (*timer->_env)->DeleteGlobalRef(timer->_env, timer->_runnable); 91 } 92 timer->_runnable = NULL; 93 94 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 95 { 96 [timer release]; 97 } 98 [pool drain]; 99 100 (*MAIN_JVM)->DetachCurrentThread(MAIN_JVM); 101 } 102 else 103 { 104 NSLog(@"ERROR: Glass could not attach Timer _thread to VM, result:%d\n", (int)error); 105 } 106 } 107 108 return NULL; 109 } 110 111 CVReturn CVOutputCallback(CVDisplayLinkRef displayLink, 112 const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, 113 CVOptionFlags flagsIn, CVOptionFlags *flagsOut, 114 void *displayLinkContext) { 115 116 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 117 118 if (displayLinkContext != NULL) 119 { 120 GlassTimer *timer = (GlassTimer*)displayLinkContext; 121 122 // Attach the thread every time. Cocoa changes the thread when a new 123 // display resolution is selected so we need to make sure that we are 124 // able to call into Java. Attaching the same thread multiple times 125 // does nothing. 126 jint error = (*MAIN_JVM)->AttachCurrentThreadAsDaemon(MAIN_JVM, (void **)&timer->_env, NULL); 127 if (error == 0) 128 { 129 if (timer->_runnable != NULL) 130 { 131 (*timer->_env)->CallVoidMethod(timer->_env, timer->_runnable, jRunnableRun); 132 } 133 134 // Do not detach the thread - continuously attaching and detaching a thread 135 // kills some debuggers making it impossible to step through Prism. Since 136 // the thread is attached as a daemon, it will not stop Java from exiting. 137 // error = (*MAIN_JVM)->DetachCurrentThread(MAIN_JVM); 138 // if (error != JNI_OK) { 139 // NSLog(@"ERROR: Glass could not detach CVDisplayLink _thread to VM, result:%d\n", (int)error); 140 // } 141 } else { 142 NSLog(@"ERROR: Glass could not attach CVDisplayLink _thread to VM, result:%d\n", (int)error); 143 } 144 } 145 146 [pool release]; 147 return kCVReturnSuccess; 148 } 149 150 151 @implementation GlassTimer 152 153 - (id)initWithRunnable:(jobject)runnable withEnv:(JNIEnv*)env 154 { 155 self = [super init]; 156 if (self != nil) 157 { 158 CVReturn err = CVDisplayLinkSetOutputCallback(GlassDisplayLink, &CVOutputCallback, self); 159 if (err != kCVReturnSuccess) 160 { 161 NSLog(@"CVDisplayLinkSetOutputCallback error: %d", err); 162 } 163 else 164 { 165 self->_runnable = (*env)->NewGlobalRef(env, runnable); 166 } 167 } 168 return self; 169 } 170 171 - (id)initWithRunnable:(jobject)runnable withEnv:(JNIEnv*)env withPeriod:(jint)period 172 { 173 self = [super init]; 174 if (self != nil) 175 { 176 self->_thread = NULL; 177 self->_running = NO; 178 self->_runnable = NULL; 179 self->_period = (1000 * period); // ms --> us 180 181 pthread_attr_t attr; 182 pthread_attr_init(&attr); 183 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); 184 pthread_attr_setschedpolicy(&attr, SCHED_FIFO); 185 186 #if HIGH_PRIORITY_TIMER 187 struct sched_param param; 188 memset(¶m, 0x00, sizeof(param)); 189 param.sched_priority = sched_get_priority_max(SCHED_FIFO)-1; // notice: 2nd highest priority (gznote: do we really want that?) 190 pthread_attr_setschedparam(&attr, ¶m); 191 #endif 192 193 int err = pthread_create(&self->_thread, &attr, _GlassTimerTask, self); 194 if (err == 0) 195 { 196 // detach thread, so its resources can be reclaimed as soon as it's finished 197 pthread_detach(self->_thread); 198 199 self->_runnable = (*env)->NewGlobalRef(env, runnable); 200 } 201 else 202 { 203 NSLog(@"GlassTimer error: pthread_create returned %d", err); 204 205 self = nil; 206 } 207 } 208 return self; 209 } 210 211 @end 212 213 /* 214 * Class: com_sun_glass_ui_mac_MacTimer 215 * Method: _start 216 * Signature: (Ljava/lang/Runnable;I)J 217 */ 218 JNIEXPORT jlong JNICALL 219 Java_com_sun_glass_ui_mac_MacTimer__1start__Ljava_lang_Runnable_2I(JNIEnv *env, jobject jThis, 220 jobject jRunnable, jint jPeriod) 221 { 222 LOG("Java_com_sun_glass_ui_mac_MacTimer__1start__Ljava_lang_Runnable_2I"); 223 224 jlong jTimerPtr = 0L; 225 226 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 227 GLASS_POOL_ENTER; 228 { 229 jTimerPtr = ptr_to_jlong([[GlassTimer alloc] initWithRunnable:jRunnable withEnv:env withPeriod:jPeriod]); 230 } 231 GLASS_POOL_EXIT; 232 GLASS_CHECK_EXCEPTION(env); 233 234 return jTimerPtr; 235 } 236 237 /* 238 * Class: com_sun_glass_ui_mac_MacTimer 239 * Method: _start 240 * Signature: (Ljava/lang/Runnable;)J 241 */ 242 JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_mac_MacTimer__1start__Ljava_lang_Runnable_2 243 (JNIEnv *env, jobject jThis, jobject jRunnable) 244 { 245 LOG("Java_com_sun_glass_ui_mac_MacTimer__1start__Ljava_lang_Runnable_2"); 246 247 jlong jTimerPtr = 0L; 248 249 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 250 GLASS_POOL_ENTER; 251 { 252 jTimerPtr = ptr_to_jlong([[GlassTimer alloc] initWithRunnable:jRunnable withEnv:env]); 253 } 254 GLASS_POOL_EXIT; 255 GLASS_CHECK_EXCEPTION(env); 256 257 return jTimerPtr; 258 } 259 260 /* 261 * Class: com_sun_glass_ui_mac_MacTimer 262 * Method: _stop 263 * Signature: (J)V 264 */ 265 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacTimer__1stop 266 (JNIEnv * env, jobject jRunnable, jlong jTimerPtr) 267 { 268 LOG("Java_com_sun_glass_ui_mac_MacTimer__1stop"); 269 270 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 271 GLASS_POOL_ENTER; 272 { 273 GlassTimer *timer = (GlassTimer*)jlong_to_ptr(jTimerPtr); 274 if (timer->_period == 0) 275 { 276 // stop the display link 277 if (GlassDisplayLink != NULL && CVDisplayLinkIsRunning(GlassDisplayLink)) { 278 CVDisplayLinkStop(GlassDisplayLink); 279 CVDisplayLinkRelease(GlassDisplayLink); 280 } 281 } 282 else 283 { 284 timer->_running = NO; 285 } 286 } 287 GLASS_POOL_EXIT; 288 GLASS_CHECK_EXCEPTION(env); 289 } 290 291 /* 292 * Class: com_sun_glass_ui_mac_MacTimer 293 * Method: _getMinPeriod 294 * Signature: ()I 295 */ 296 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacTimer__1getMinPeriod 297 (JNIEnv * env, jclass cls) 298 { 299 LOG("Java_com_sun_glass_ui_mac_MacTimer__1getMinPeriod"); 300 301 int period = 0; 302 303 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 304 GLASS_POOL_ENTER; 305 { 306 period = 0; // in ms 307 } 308 GLASS_POOL_EXIT; 309 GLASS_CHECK_EXCEPTION(env); 310 311 return period; 312 } 313 314 /* 315 * Class: com_sun_glass_ui_mac_MacTimer 316 * Method: _getMaxPeriod 317 * Signature: ()I 318 */ 319 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacTimer__1getMaxPeriod 320 (JNIEnv * env, jclass cls) 321 { 322 LOG("Java_com_sun_glass_ui_mac_MacTimer__1getMaxPeriod"); 323 324 int period = 0; 325 326 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 327 GLASS_POOL_ENTER; 328 { 329 period = 1000000; // in ms (100 sec) 330 } 331 GLASS_POOL_EXIT; 332 GLASS_CHECK_EXCEPTION(env); 333 334 return period; 335 } 336 337 /* 338 * Class: com_sun_glass_ui_mac_MacTimer 339 * Method: _initIDs 340 * Signature: ()V 341 */ 342 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacTimer__1initIDs 343 (JNIEnv *env, jclass jClass) 344 { 345 346 initJavaIDsList(env); 347 348 if (GlassDisplayLink == NULL) 349 { 350 CVReturn err = CVDisplayLinkCreateWithActiveCGDisplays(&GlassDisplayLink); 351 if (err != kCVReturnSuccess) 352 { 353 NSLog(@"CVDisplayLinkCreateWithActiveCGDisplays error: %d", err); 354 } 355 err = CVDisplayLinkSetCurrentCGDisplay(GlassDisplayLink, kCGDirectMainDisplay); 356 if (err != kCVReturnSuccess) 357 { 358 NSLog(@"CVDisplayLinkSetCurrentCGDisplay error: %d", err); 359 } 360 /* 361 * set a null callback and start the link to prep for GlassTimer initialization 362 */ 363 err = CVDisplayLinkSetOutputCallback(GlassDisplayLink, &CVOutputCallback, NULL); 364 if (err != kCVReturnSuccess) 365 { 366 NSLog(@"CVDisplayLinkSetOutputCallback error: %d", err); 367 } 368 err = CVDisplayLinkStart(GlassDisplayLink); 369 if (err != kCVReturnSuccess) 370 { 371 NSLog(@"CVDisplayLinkStart error: %d", err); 372 } 373 } 374 } 375