Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
java: obey our coding standard
[simgrid.git] / src / bindings / java / JavaContext.cpp
index cce9499..7796bda 100644 (file)
-/* context_java - implementation of context switching for java threads */
+/* Context switching within the JVM.                                        */
 
-/* Copyright (c) 2009-2010, 2012-2014. The SimGrid Team.
- * All rights reserved.                                                     */
+/* Copyright (c) 2009-2018. The SimGrid Team. All rights reserved.          */
 
 /* This program is free software; you can redistribute it and/or modify it
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
+#include "JavaContext.hpp"
+#include "jxbt_utilities.hpp"
+#include "simgrid/Exception.hpp"
+#include "src/simix/smx_private.hpp"
+
 #include <functional>
 #include <utility>
 
-#include <xbt/function_types.h>
-#include <simgrid/simix.h>
-#include <xbt/ex.h>
-#include "JavaContext.hpp"
-#include "jxbt_utilities.h"
-#include "xbt/dynar.h"
-#include "../../simix/smx_private.h"
-extern JavaVM *__java_vm;
+extern JavaVM* __java_vm;
 
-XBT_LOG_NEW_DEFAULT_CATEGORY(jmsg, "MSG for Java(TM)");
+XBT_LOG_NEW_DEFAULT_CATEGORY(java, "MSG for Java(TM)");
 
 namespace simgrid {
-namespace java {
+namespace kernel {
+namespace context {
 
-simgrid::simix::ContextFactory* java_factory()
+ContextFactory* java_factory()
 {
   XBT_INFO("Using regular java threads.");
   return new JavaContextFactory();
 }
 
-JavaContextFactory::JavaContextFactory()
-  : ContextFactory("JavaContextFactory")
+JavaContextFactory::JavaContextFactory(): ContextFactory("JavaContextFactory")
 {
 }
 
-JavaContextFactory::~JavaContextFactory()
-{
-}
+JavaContextFactory::~JavaContextFactory()=default;
 
 JavaContext* JavaContextFactory::self()
 {
-  return (JavaContext*) xbt_os_thread_get_extra_data();
+  return static_cast<JavaContext*>(xbt_os_thread_get_extra_data());
 }
 
-JavaContext* JavaContextFactory::create_context(
-  std::function<void()> code,
-  void_pfn_smxprocess_t cleanup, smx_process_t process)
+JavaContext* JavaContextFactory::create_context(std::function<void()> code, void_pfn_smxprocess_t cleanup_fun,
+                                                smx_actor_t actor)
 {
-  return this->new_context<JavaContext>(std::move(code), cleanup, process);
+  return this->new_context<JavaContext>(std::move(code), cleanup_fun, actor);
 }
 
 void JavaContextFactory::run_all()
 {
-  xbt_dynar_t processes = SIMIX_process_get_runnable();
-  smx_process_t process;
-  unsigned int cursor;
-  xbt_dynar_foreach(processes, cursor, process) {
-    static_cast<JavaContext*>(SIMIX_process_get_context(process))->resume();
+  for (smx_actor_t const& process : simgrid::simix::process_get_runnable()) {
+    static_cast<JavaContext*>(process->context_)->resume();
   }
 }
 
-
 JavaContext::JavaContext(std::function<void()> code,
         void_pfn_smxprocess_t cleanup_func,
-        smx_process_t process)
+        smx_actor_t process)
   : Context(std::move(code), cleanup_func, process)
 {
-  static int thread_amount=0;
-  thread_amount++;
-
-  /* If the user provided a function for the process then use it
-     otherwise is the context for maestro */
+  /* If the user provided a function for the process then use it. Otherwise is the context for maestro */
   if (has_code()) {
-    this->jprocess = nullptr;
-    this->begin = xbt_os_sem_init(0);
-    this->end = xbt_os_sem_init(0);
-
-    TRY {
-       this->thread = xbt_os_thread_create(
-         nullptr, JavaContext::wrapper, this, nullptr);
-    }
-    CATCH_ANONYMOUS {
-      RETHROWF("Failed to create context #%d. You may want to switch to Java coroutines to increase your limits (error: %s)."
-               "See the Install section of simgrid-java documentation (in doc/install.html) for more on coroutines.",
-               thread_amount);
-    }
+    this->begin_ = xbt_os_sem_init(0);
+    this->end_   = xbt_os_sem_init(0);
+
+    this->thread_ = xbt_os_thread_create(nullptr, JavaContext::wrapper, this, nullptr);
   } else {
-    this->thread = nullptr;
     xbt_os_thread_set_extra_data(this);
   }
 }
 
 JavaContext::~JavaContext()
 {
-  if (this->thread) {
+  if (this->thread_) {
     // We are not in maestro context
-    xbt_os_thread_join(this->thread, nullptr);
-    xbt_os_sem_destroy(this->begin);
-    xbt_os_sem_destroy(this->end);
+    xbt_os_thread_join(this->thread_, nullptr);
+    xbt_os_sem_destroy(this->begin_);
+    xbt_os_sem_destroy(this->end_);
   }
 }
 
 void* JavaContext::wrapper(void *data)
 {
-  JavaContext* context = (JavaContext*)data;
+  JavaContext* context = static_cast<JavaContext*>(data);
   xbt_os_thread_set_extra_data(context);
   //Attach the thread to the JVM
 
   JNIEnv *env;
-  XBT_ATTRIB_UNUSED jint error =
-    __java_vm->AttachCurrentThread((void **) &env, NULL);
+  XBT_ATTRIB_UNUSED jint error = __java_vm->AttachCurrentThread((void**)&env, nullptr);
   xbt_assert((error == JNI_OK), "The thread could not be attached to the JVM");
-  context->jenv = get_current_thread_env();
+  context->jenv_ = env;
   //Wait for the first scheduling round to happen.
-  xbt_os_sem_acquire(context->begin);
+  xbt_os_sem_acquire(context->begin_);
   //Create the "Process" object if needed.
   (*context)();
   context->stop();
@@ -122,45 +97,68 @@ void* JavaContext::wrapper(void *data)
 
 void JavaContext::stop()
 {
-  /* I am the current process and I am dying */
+  /* I was asked to die (either with kill() or because of a failed element) */
   if (this->iwannadie) {
     this->iwannadie = 0;
     JNIEnv *env = get_current_thread_env();
     XBT_DEBUG("Gonna launch Killed Error");
-    // TODO Adrien, if the process has not been created at the java layer, why should we raise the exception/error at the java level (this happens
-    // for instance during the migration process that creates at the C level two processes: one on the SRC node and one on the DST node, if the DST process is killed.
-    // it is not required to raise an exception at the JAVA level, the low level should be able to manage such an issue correctly but this is not the case right now unfortunately ...
-    // TODO it will be nice to have the name of the process to help the end-user to know which Process has been killed
-   // jxbt_throw_by_name(env, "org/simgrid/msg/ProcessKilledError", bprintf("Process %s killed :) (file smx_context_java.c)", MSG_process_get_name( (msg_process_t)context) ));
+    // When the process wants to stop before its regular end, we should cut its call stack quickly.
+    // The easiest way to do so is to raise an exception that will be catched in its top calling level.
+    //
+    // For that, we raise a ProcessKilledError that is catched in Process::run() (in msg/Process.java)
+    //
+    // Throwing a Java exception to stop the actor may be an issue for pure C actors
+    // (as the ones created for the VM migration). The Java exception will not be catched anywhere.
+    // Bad things happen currently if these actors get killed, unfortunately.
     jxbt_throw_by_name(env, "org/simgrid/msg/ProcessKilledError",
-      bprintf("Process %s killed :) (file JavaContext.cpp)",
-      simcall_process_get_name((smx_process_t) SIMIX_context_get_process(this))) );
+                       std::string("Process ") + this->process()->get_cname() + " killed from file JavaContext.cpp");
+
+    // (remember that throwing a java exception from C does not break the C execution path.
+    //  Instead, it marks the exception to be raised when returning to the Java world and
+    //  continues to execute the C function until it ends or returns).
+
+    // Once the Java stack is marked to be unrolled, a C cancel_error is raised to kill the simcall
+    //  on which the killed actor is blocked (if any).
+    // Not doing so would prevent the actor to notice that it's dead, leading to segfaults when it wakes up.
+    // This is dangerous: if the killed actor is not actually blocked, the cancel_error will not get catched.
+    // But it should be OK in most cases:
+    //  - If I kill myself, I must do so with Process.kill().
+    //    The binding of this function in jmsg_process.cpp adds a try/catch around the MSG_process_kill() leading to us
+    //  - If I kill someone else that is blocked, the cancel_error will unblock it.
+    //
+    // A problem remains probably if I kill a process that is ready_to_run in the same scheduling round.
+    // I guess that this will kill the whole simulation because the victim does not catch the exception.
+    // The only solution I see to that problem would be to completely rewrite the process killing sequence
+    // (also in C) so that it's based on regular C++ exceptions that would be catched anyway.
+    // In other words, we need to do in C++ what we do in Java for sake of uniformity.
+    //
+    // Plus, C++ RAII would work in that case, too.
+
     XBT_DEBUG("Trigger a cancel error at the C level");
     THROWF(cancel_error, 0, "process cancelled");
   } else {
     Context::stop();
     /* detach the thread and kills it */
-    JNIEnv *env = this->jenv;
-    env->DeleteGlobalRef(this->jprocess);
+    JNIEnv* env = this->jenv_;
+    env->DeleteGlobalRef(this->jprocess_);
     XBT_ATTRIB_UNUSED jint error = __java_vm->DetachCurrentThread();
     xbt_assert((error == JNI_OK), "The thread couldn't be detached.");
-    xbt_os_sem_release(this->end);
-    xbt_os_thread_exit(NULL);
+    xbt_os_sem_release(this->end_);
+    xbt_os_thread_exit(nullptr);
   }
 }
 
 void JavaContext::suspend()
 {
-  xbt_os_sem_release(this->end);
-  xbt_os_sem_acquire(this->begin);
+  xbt_os_sem_release(this->end_);
+  xbt_os_sem_acquire(this->begin_);
 }
 
 // FIXME: inline those functions
 void JavaContext::resume()
 {
-  xbt_os_sem_release(this->begin);
-  xbt_os_sem_acquire(this->end);
+  xbt_os_sem_release(this->begin_);
+  xbt_os_sem_acquire(this->end_);
 }
 
-}
-}
+}}} // namespace simgrid::kernel::context