Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of git://scm.gforge.inria.fr/simgrid/simgrid-java
[simgrid.git] / src / smx_context_cojava.c
1 /* context_cojava - implementation of context switching for java coroutines */
2
3 /* Copyright 2012. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9
10 #include <xbt/function_types.h>
11 #include <simgrid/simix.h>
12 #include <xbt/ex.h>
13 #include "smx_context_cojava.h"
14 #include "jxbt_utilities.h"
15 #include "xbt/dynar.h"
16
17
18 //Coroutine methodID/class cache.
19 jclass coclass;
20 jmethodID coroutine_init;
21 jmethodID coroutine_yield;
22 jmethodID coroutine_yieldTo;
23 jmethodID coroutine_stop;
24 //Maestro java coroutine
25 jobject cojava_maestro_coroutine;
26
27 JNIEnv *global_env;
28
29 static smx_context_t my_current_context = NULL;
30 static smx_context_t maestro_context = NULL;
31
32
33 xbt_dynar_t cojava_processes;
34 static unsigned long int cojava_process_index = 0;
35
36 static JavaVM *get_current_vm(void);
37 static JavaVM *get_current_vm(void)
38 {
39         JavaVM *jvm;
40         JNI_GetCreatedJavaVMs(&jvm,1,NULL);
41   return jvm;
42 }
43
44 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(jmsg);
45
46
47 static smx_context_t
48 smx_ctx_cojava_factory_create_context(xbt_main_func_t code, int argc,
49                                     char **argv,
50                                     void_pfn_smxprocess_t cleanup_func,
51                                     void *data);
52
53 static void smx_ctx_cojava_free(smx_context_t context);
54 static void smx_ctx_cojava_start(smx_context_t context);
55 static void smx_ctx_cojava_suspend(smx_context_t context);
56 static void smx_ctx_cojava_resume(smx_context_t new_context);
57 static void smx_ctx_cojava_runall(void);
58 static void* smx_ctx_cojava_run(void *data);
59 static void smx_ctx_cojava_create_coroutine(smx_ctx_cojava_t context);
60 void SIMIX_ctx_cojava_factory_init(smx_context_factory_t * factory)
61 {
62   /* instantiate the context factory */
63   smx_ctx_base_factory_init(factory);
64
65   (*factory)->create_context = smx_ctx_cojava_factory_create_context;
66   /* Leave default behavior of (*factory)->finalize */
67   (*factory)->free = smx_ctx_cojava_free;
68   (*factory)->stop = smx_ctx_cojava_stop;
69   (*factory)->suspend = smx_ctx_cojava_suspend;
70   (*factory)->runall = smx_ctx_cojava_runall;
71   (*factory)->name = "ctx_cojava_factory";
72   //(*factory)->finalize = smx_ctx_base_factory_finalize;
73   (*factory)->self = smx_ctx_cojava_self;
74   (*factory)->get_data = smx_ctx_base_get_data;
75
76   global_env = get_current_thread_env();
77
78   coclass = (*global_env)->FindClass(global_env, "java/dyn/Coroutine");
79   xbt_assert((coclass != NULL), "Can't find java.dyn.Coroutine class.");
80   //Cache the method id we are going to use
81   coroutine_init = (*global_env)->GetMethodID(global_env, coclass, "<init>", "(Ljava/lang/Runnable;)V");
82   xbt_assert((coroutine_init != NULL), "Can't find <init>");
83   coroutine_stop = (*global_env)->GetMethodID(global_env, coclass, "stop", "()V");
84   xbt_assert((coroutine_stop != NULL), "Method not found...");
85   coroutine_yield = (*global_env)->GetStaticMethodID(global_env, coclass, "yield", "()V");
86         xbt_assert((coroutine_yield != NULL), "Method yield not found.");
87         coroutine_yieldTo = (*global_env)->GetStaticMethodID(global_env, coclass, "yieldTo", "(Ljava/dyn/Coroutine;)V");
88         xbt_assert((coroutine_yieldTo != NULL), "Method yieldTo not found.");
89
90         jclass class_thread = (*global_env)->FindClass(global_env, "java/lang/Thread");
91         xbt_assert((class_thread != NULL), "Can't find java.lang.Thread class");
92         jclass class_coroutine_support = (*global_env)->FindClass(global_env, "java/dyn/CoroutineSupport");
93         xbt_assert((class_coroutine_support != NULL), "Can't find java.dyn.CoroutineSupport class");
94         jmethodID thread_get_current = (*global_env)->GetStaticMethodID(global_env, class_thread, "currentThread", "()Ljava/lang/Thread;");
95         xbt_assert((thread_get_current != NULL), "Can't find Thread.currentThread() method.");
96
97         /**
98          * Retrieve maetro coroutine object
99          */
100         jobject jthread;
101         jthread = (*global_env)->CallStaticObjectMethod(global_env, class_thread, thread_get_current);
102         xbt_assert((jthread != NULL), "Can't find current thread.");
103
104         jmethodID thread_get_coroutine_support = (*global_env)->GetMethodID(global_env, class_thread, "getCoroutineSupport", "()Ljava/dyn/CoroutineSupport;");
105         xbt_assert((thread_get_coroutine_support != NULL), "Can't find Thread.getCoroutineSupport method");
106
107         jobject jcoroutine_support;
108         jcoroutine_support = (*global_env)->CallObjectMethod(global_env, jthread, thread_get_coroutine_support);
109         xbt_assert((jcoroutine_support != NULL), "Can't find coroutine support object");
110         //FIXME ? Be careful, might change in the implementation (we are relying on private fields, so...).
111         jfieldID coroutine_support_thread_coroutine = (*global_env)->GetFieldID(global_env, class_coroutine_support, "threadCoroutine", "Ljava/dyn/Coroutine;");
112         xbt_assert((coroutine_support_thread_coroutine != NULL), "Can't find threadCoroutine field");
113         cojava_maestro_coroutine = (jobject)(*global_env)->GetObjectField(global_env, jcoroutine_support, coroutine_support_thread_coroutine);
114         xbt_assert((cojava_maestro_coroutine != NULL), "Can't find the thread coroutine.");
115         cojava_maestro_coroutine = (*global_env)->NewGlobalRef(global_env, cojava_maestro_coroutine);
116         xbt_assert((cojava_maestro_coroutine != NULL), "Can't get a global reference to the thread coroutine.");
117 }
118 smx_context_t smx_ctx_cojava_self(void)
119 {
120         return my_current_context;
121 }
122
123 static smx_context_t
124 smx_ctx_cojava_factory_create_context(xbt_main_func_t code, int argc,
125                                     char **argv,
126                                     void_pfn_smxprocess_t cleanup_func,
127                                     void* data)
128 {
129         smx_ctx_cojava_t context = xbt_new0(s_smx_ctx_cojava_t, 1);
130   /* If the user provided a function for the process then use it
131      otherwise is the context for maestro */
132   if (code) {
133                 if (argc == 0) {
134                         context->jprocess = (jobject) code;
135                 }
136                 else {
137                         context->jprocess = NULL;
138                 }
139                 context->super.cleanup_func = cleanup_func;
140
141                 context->super.argc = argc;
142                 context->super.argv = argv;
143                 context->super.code = code;
144
145                 smx_ctx_cojava_run(context);
146   }
147   else {
148         context->jcoroutine = NULL;
149         my_current_context = (smx_context_t)context;
150         maestro_context = (smx_context_t)context;
151   }
152   context->bound = 0;
153   context->super.data = data;
154   return (smx_context_t) context;
155 }
156
157 static void* smx_ctx_cojava_run(void *data) {
158         smx_ctx_cojava_t context = (smx_ctx_cojava_t)data;
159         my_current_context = (smx_context_t)context;
160         //Create the "Process" object if needed.
161         if (context->super.argc <= 0) {
162                 smx_ctx_cojava_create_coroutine(context);
163         }
164         my_current_context = maestro_context;
165   return NULL;
166 }
167 static void smx_ctx_cojava_free(smx_context_t context)
168 {
169         if (context) {
170                 smx_ctx_cojava_t ctx_java = (smx_ctx_cojava_t) context;
171                 if (ctx_java->jcoroutine) { /* We are not in maestro context */
172                         JNIEnv *env = get_current_thread_env();
173                         (*env)->DeleteGlobalRef(env, ctx_java->jcoroutine);
174                         (*env)->DeleteGlobalRef(env, ctx_java->jprocess);
175                 }
176   }
177   smx_ctx_base_free(context);
178 }
179
180
181 void smx_ctx_cojava_stop(smx_context_t context)
182 {
183         /*
184          * The java stack needs to be empty, otherwise weird stuff
185          * will happen
186          */
187         if (context->iwannadie) {
188         context->iwannadie = 0;
189         JNIEnv *env = get_current_thread_env();
190         jxbt_throw_by_name(env, "org/simgrid/msg/ProcessKilledError", bprintf("Process killed :)"));
191         THROWF(cancel_error, 0, "process cancelled");
192   }
193         else {
194                 smx_ctx_base_stop(context);
195                 smx_ctx_cojava_suspend(context);
196         }
197 }
198
199 static void smx_ctx_cojava_suspend(smx_context_t context)
200 {
201         smx_context_t previous_context = context;
202         unsigned long int i = cojava_process_index++;
203         jobject next_coroutine;
204
205         if (i < xbt_dynar_length(cojava_processes)) {
206                 smx_context_t next_context = SIMIX_process_get_context(xbt_dynar_get_as(
207                                 cojava_processes,i, smx_process_t));
208                 my_current_context = next_context;
209                 XBT_DEBUG("Switching to %p",my_current_context);
210                 smx_ctx_cojava_t java_context = (smx_ctx_cojava_t)(next_context);
211
212                 if (!java_context->jprocess) {
213                         (*(java_context->super.code))(java_context->super.argc, java_context->super.argv);
214                         smx_ctx_cojava_create_coroutine(java_context);
215                 }
216                 else if (!java_context->bound) {
217                         java_context->bound = 1;
218                         smx_process_t process = SIMIX_process_self();
219                         (*global_env)->SetLongField(global_env, java_context->jprocess, jprocess_field_Process_bind, (jlong)process);
220                 }
221
222                 next_coroutine = java_context->jcoroutine;
223         }
224         else {
225                 //Give maestro the control back.
226                 next_coroutine = cojava_maestro_coroutine;
227                 my_current_context = maestro_context;
228         }
229   (*global_env)->CallStaticVoidMethod(global_env, coclass, coroutine_yieldTo, next_coroutine);
230   my_current_context = previous_context;
231 }
232
233 static void smx_ctx_cojava_resume(smx_context_t new_context) {
234         my_current_context = new_context;
235         smx_ctx_cojava_t java_context = (smx_ctx_cojava_t)(new_context);
236
237         if (!java_context->jprocess) {
238                 (*(java_context->super.code))(java_context->super.argc, java_context->super.argv);
239                 smx_ctx_cojava_create_coroutine(java_context);
240                 java_context->bound = 1;
241         }
242         else if (!java_context->bound) {
243                 java_context->bound = 1;
244                 smx_process_t process = SIMIX_process_self();
245                 (*global_env)->SetLongField(global_env, java_context->jprocess, jprocess_field_Process_bind, (jlong)process);
246         }
247   (*global_env)->CallStaticVoidMethod(global_env, coclass, coroutine_yieldTo, java_context->jcoroutine);
248 }
249
250 static void smx_ctx_cojava_runall(void)
251 {
252   cojava_processes = SIMIX_process_get_runnable();
253         smx_process_t process;
254         process = xbt_dynar_get_as(cojava_processes, 0, smx_process_t);
255         cojava_process_index = 1;
256         /* Execute the first process */
257         smx_ctx_cojava_resume(SIMIX_process_get_context(process));
258 }
259
260 static void smx_ctx_cojava_create_coroutine(smx_ctx_cojava_t context) {
261         JNIEnv *env = get_current_thread_env();
262         jclass coclass = (*env)->FindClass(env, "java/dyn/Coroutine");
263   xbt_assert((coclass != NULL), "Can't find coroutine class ! :(");
264   jobject jcoroutine = (*env)->NewObject(env, coclass, coroutine_init, context->jprocess);
265   xbt_assert((jcoroutine != NULL), "Can't create coroutine object.");
266   jcoroutine = (*env)->NewGlobalRef(env, jcoroutine);
267   context->jcoroutine = jcoroutine;
268 }