1 /*
   2  * Copyright (c) 2011, 2015, 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 /*
  27  * Must include this before JavaNativeFoundation.h to get jni.h from build
  28  */
  29 #include "jni.h"
  30 #include "jni_util.h"
  31 
  32 #import "com_apple_concurrent_LibDispatchNative.h"
  33 
  34 #import <dispatch/dispatch.h>
  35 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  36 
  37 /*
  38  * Declare library specific JNI_Onload entry if static build
  39  */
  40 DEF_STATIC_JNI_OnLoad
  41 
  42 /*
  43  * Class:     com_apple_concurrent_LibDispatchNative
  44  * Method:    nativeIsDispatchSupported
  45  * Signature: ()Z
  46  */
  47 JNIEXPORT jboolean JNICALL Java_com_apple_concurrent_LibDispatchNative_nativeIsDispatchSupported
  48 (JNIEnv *env, jclass clazz)
  49 {
  50         return JNI_TRUE;
  51 }
  52 
  53 
  54 /*
  55  * Class:     com_apple_concurrent_LibDispatchNative
  56  * Method:    nativeGetMainQueue
  57  * Signature: ()J
  58  */
  59 JNIEXPORT jlong JNICALL Java_com_apple_concurrent_LibDispatchNative_nativeGetMainQueue
  60 (JNIEnv *env, jclass clazz)
  61 {
  62         dispatch_queue_t queue = dispatch_get_main_queue();
  63         return ptr_to_jlong(queue);
  64 }
  65 
  66 
  67 /*
  68  * Class:     com_apple_concurrent_LibDispatchNative
  69  * Method:    nativeCreateConcurrentQueue
  70  * Signature: (I)J
  71  */
  72 JNIEXPORT jlong JNICALL Java_com_apple_concurrent_LibDispatchNative_nativeCreateConcurrentQueue
  73 (JNIEnv *env, jclass clazz, jint priority)
  74 {
  75         dispatch_queue_t queue = dispatch_get_global_queue((long)priority, 0);
  76         return ptr_to_jlong(queue);
  77 }
  78 
  79 
  80 /*
  81  * Class:     com_apple_concurrent_LibDispatchNative
  82  * Method:    nativeCreateSerialQueue
  83  * Signature: (Ljava/lang/String;)J
  84  */
  85 JNIEXPORT jlong JNICALL Java_com_apple_concurrent_LibDispatchNative_nativeCreateSerialQueue
  86 (JNIEnv *env, jclass clazz, jstring name)
  87 {
  88         if (name == NULL) return 0L;
  89 
  90         jboolean isCopy;
  91         const char *queue_name = (*env)->GetStringUTFChars(env, name, &isCopy);
  92         dispatch_queue_t queue = dispatch_queue_create(queue_name, NULL);
  93         (*env)->ReleaseStringUTFChars(env, name, queue_name);
  94 
  95         return ptr_to_jlong(queue);
  96 }
  97 
  98 
  99 /*
 100  * Class:     com_apple_concurrent_LibDispatchNative
 101  * Method:    nativeReleaseQueue
 102  * Signature: (J)V
 103  */
 104 JNIEXPORT void JNICALL Java_com_apple_concurrent_LibDispatchNative_nativeReleaseQueue
 105 (JNIEnv *env, jclass clazz, jlong nativeQueue)
 106 {
 107         if (nativeQueue == 0L) return;
 108         dispatch_release((dispatch_queue_t)jlong_to_ptr(nativeQueue));
 109 }
 110 
 111 
 112 static JNF_CLASS_CACHE(jc_Runnable, "java/lang/Runnable");
 113 static JNF_MEMBER_CACHE(jm_run, jc_Runnable, "run", "()V");
 114 
 115 static void perform_dispatch(JNIEnv *env, jlong nativeQueue, jobject runnable, void (*dispatch_fxn)(dispatch_queue_t, dispatch_block_t))
 116 {
 117 JNF_COCOA_ENTER(env);
 118         dispatch_queue_t queue = (dispatch_queue_t)jlong_to_ptr(nativeQueue);
 119         if (queue == NULL) return; // shouldn't happen
 120 
 121         // create a global-ref around the Runnable, so it can be safely passed to the dispatch thread
 122         JNFJObjectWrapper *wrappedRunnable = [[JNFJObjectWrapper alloc] initWithJObject:runnable withEnv:env];
 123 
 124         dispatch_fxn(queue, ^{
 125                 // attach the dispatch thread to the JVM if necessary, and get an env
 126                 JNFThreadContext ctx = JNFThreadDetachOnThreadDeath | JNFThreadSetSystemClassLoaderOnAttach | JNFThreadAttachAsDaemon;
 127                 JNIEnv *blockEnv = JNFObtainEnv(&ctx);
 128 
 129         JNF_COCOA_ENTER(blockEnv);
 130 
 131                 // call the user's runnable
 132                 JNFCallObjectMethod(blockEnv, [wrappedRunnable jObject], jm_run);
 133 
 134                 // explicitly clear object while we have an env (it's cheaper that way)
 135                 [wrappedRunnable setJObject:NULL withEnv:blockEnv];
 136 
 137         JNF_COCOA_EXIT(blockEnv);
 138 
 139                 // let the env go, but leave the thread attached as a daemon
 140                 JNFReleaseEnv(blockEnv, &ctx);
 141         });
 142 
 143         // release this thread's interest in the Runnable, the block
 144         // will have retained the it's own interest above
 145         [wrappedRunnable release];
 146 
 147 JNF_COCOA_EXIT(env);
 148 }
 149 
 150 
 151 /*
 152  * Class:     com_apple_concurrent_LibDispatchNative
 153  * Method:    nativeExecuteAsync
 154  * Signature: (JLjava/lang/Runnable;)V
 155  */
 156 JNIEXPORT void JNICALL Java_com_apple_concurrent_LibDispatchNative_nativeExecuteAsync
 157 (JNIEnv *env, jclass clazz, jlong nativeQueue, jobject runnable)
 158 {
 159         // enqueues and returns
 160         perform_dispatch(env, nativeQueue, runnable, dispatch_async);
 161 }
 162 
 163 
 164 /*
 165  * Class:     com_apple_concurrent_LibDispatchNative
 166  * Method:    nativeExecuteSync
 167  * Signature: (JLjava/lang/Runnable;)V
 168  */
 169 JNIEXPORT void JNICALL Java_com_apple_concurrent_LibDispatchNative_nativeExecuteSync
 170 (JNIEnv *env, jclass clazz, jlong nativeQueue, jobject runnable)
 171 {
 172         // blocks until the Runnable completes
 173         perform_dispatch(env, nativeQueue, runnable, dispatch_sync);
 174 }