Menu

JNI - stop the execution of an infinite clips run

Help
2017-04-03
2024-03-25
  • Chaubert Jérôme

    Hi,

    I try to stop a clips run "stucked" in a loop. But every instruction send to clips make the JVM crash. Is there a way to stop clips when it is running ?

    For example the following java code :

    final Environment clips = new Environment();
    
            clips.load("./src/test/resources/boucle.clp");
    
            final ExecutorService executors = Executors.newFixedThreadPool(1);
            final Future<?> task = executors.submit(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println("**********************************************************");
                    System.out.println("THREAD : clips running");
                    System.out.println("**********************************************************");
                    clips.run();
                }
            });
            Thread.sleep(1000);
            System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            System.out.println("MAIN THREAD : clips running");
            System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            //clips.eval("(halt)");//do nothing or make the JVM crash because clips is in a while infinite loop
            clips.destroy();//JVM crash here
            System.out.println("clips destroyed");
    

    withe the following clips code for "boucle.clp"

    (defrule boucle
    =>
    (bind ?i 0)
     (while TRUE
       (bind ?i (+ ?i 1))
       (if (eq (mod ?i 100000) 0) then
         (assert (do_something ?i))
         (printout t "write something " ?i crlf)))
       )
    

    make the jvm crash when the instruction "destroy" is send

     

    Last edit: Chaubert Jérôme 2017-04-03
  • Gary Riley

    Gary Riley - 2017-04-03

    Add this to net_sf_clipsrules_jni_Environment.h:

    /*
     * Class:     net_sf_clipsrules_jni_Environment
     * Method:    setHaltExecution
     * Signature: (JZ)V
     */
    JNIEXPORT void JNICALL Java_net_sf_clipsrules_jni_Environment_setHaltExecution
      (JNIEnv *, jobject, jlong, jboolean);
    
    /*
     * Class:     net_sf_clipsrules_jni_Environment
     * Method:    setHaltRules
     * Signature: (JZ)V
     */
    JNIEXPORT void JNICALL Java_net_sf_clipsrules_jni_Environment_setHaltRules
      (JNIEnv *, jobject, jlong, jboolean);
    

    Add this to net_sf_clipsrules_jni_Environment.c:

    /*******************************************************************/
    /* Java_net_sf_clipsrules_jni_Environment_setHaltExecution: Native */ 
    /*   function for the CLIPSJNI setHaltExecution method.            */
    /*                                                                 */
    /* Class:     net_sf_clipsrules_jni_Environment                    */
    /* Method:    setHaltExecution                                     */
    /* Signature: (JZ)V                                                */
    /*******************************************************************/
    JNIEXPORT void JNICALL Java_net_sf_clipsrules_jni_Environment_setHaltExecution(
      JNIEnv *env, 
      jobject obj, 
      jlong clipsEnv,
      jboolean value)
      {
       SetHaltExecution(JLongToPointer(clipsEnv),value);
      }
    
    /***************************************************************/
    /* Java_net_sf_clipsrules_jni_Environment_setHaltRules: Native */ 
    /*   function for the CLIPSJNI setHaltRules method.            */
    /*                                                             */
    /* Class:     net_sf_clipsrules_jni_Environment                */
    /* Method:    setHaltRules                                     */
    /* Signature: (JZ)V                                            */
    /***************************************************************/
    JNIEXPORT void JNICALL Java_net_sf_clipsrules_jni_Environment_setHaltRules(
      JNIEnv *env, 
      jobject obj, 
      jlong clipsEnv,
      jboolean value)
      {
       EnvSetHaltRules(JLongToPointer(clipsEnv),value);
      }
    

    Add this to Environment.java:

       /*********************/
       /* setHaltExecution: */
       /*********************/
       private native void setHaltExecution(long env,boolean value);
    
       /*********************/
       /* setHaltExecution: */
       /*********************/
       public void setHaltExecution(
         boolean value)
         {
          setHaltExecution(theEnvironment,value);
         }
    
       /*****************/
       /* setHaltRules: */
       /*****************/
       private native void setHaltRules(long env,boolean value);
    
       /*****************/
       /* setHaltRules: */
       /*****************/
       public void setHaltRules(
         boolean value)
         {
          setHaltRules(theEnvironment,value);
         }
    

    You can then destroy the CLIPS environment with this:

      clips.setHaltExecution(true);
    
      while (! task.isDone());
    
      clips.destroy();
    
     
  • Chaubert Jérôme

    Ok thanks, I will try that.
    But, just to know, is that really different from calling

    clips.eval("(halt)");
    

    before clips.destroy() ? (I already try that)

    In other words : will the "HaltRules" flag be checked when clips is in a "loop" in a right hand side of a rule or only "between" two fires ?

     

    Last edit: Chaubert Jérôme 2017-04-05
    • Gary Riley

      Gary Riley - 2017-04-05

      The HaltRules flag stops execution after the currently executing rule has finished firing, so it won't stop a loop in the right hand side of a rule. Also, CLIPS is not reentrant for a single environment, so you can't execute a run command on one thread for a single environment and an eval on another thread for that same environment. Calling setHaltExecution is just invoking a single line of code on the CLIPS side to set a boolean flag. Calling eval invokes hundreds/thousands of lines of code that could alter data structures being used by another thread of execution for that environment.

       
  • Chaubert Jérôme

    Ok, so your suggestion will work if rules fire indefinitly (and an eval("(halt)" won't work fine because of the possible data structure alteration).

    On the other hand, your suggestion will not work if CLIPS is "stucked" in a loop in a right hand side of a rule.

    Do you have an idea of a workaround to make it work in both case ? Is it even possible ?

     
    • Gary Riley

      Gary Riley - 2017-04-07

      Calling setHaltExecution terminates the execution of rules regardless of whether the rule is stuck in RHS loop. Calling setHaltRules terminates the execution of rules after the currently executing rule finishes RHS actions (so it will not terminate if the RHS is stuck in a loop).

       
  • Chaubert Jérôme

    Thanks !
    That's perfect.

    I will try that as soon as possible and tell you when it is done.

     
  • Chaubert Jérôme

    It works fine! Thank you.

    Will you integrate these functions in the next version of clips JNI ?

     
    • Gary Riley

      Gary Riley - 2017-04-07

      Yes. It was already in the next version I'm working on.

       
  • Chaubert Jérôme

    Hi,

    The problem described above seems to apply once again to clips_jni_641.

    It now seems to be trying to delete non-existent routers when destroying.

    With the Java and Clips code above, using 6.4.1 I get an exception:

    java.lang.IllegalArgumentException: Router named 'BaseRouter0' does not exist.

    at net.sf.clipsrules.jni.Environment.deleteRouter(Environment.java:857)
    at net.sf.clipsrules.jni.Environment.destroy(Environment.java:1821)
    at net.sf.clipsrules.jni.test.LoadClipsTest.destroyRunningEnvironment(LoadClipsTest.java:451)

    Should I catch and ignore this exception in the case of an abrupt destroy?

     
    • Gary Riley

      Gary Riley - 2024-03-22

      I can reproduce the issue with the code at the beginning of this thread, so I'll take a look and try to figure out what's going on.

       
    • Gary Riley

      Gary Riley - 2024-03-23

      I checked in a fix to the svn repository. There's a router that get created when commands are executed to capture any error output. It wasn't getting removed from a HashMap that kept track of existing routers. The change was made in Environment.java to the method deleteRouter.

         public void deleteRouter(
           Router theRouter)
           {
            if (theRouter == null)
              { throw new NullPointerException("theRouter"); }
      
            if (deleteRouter(theEnvironment,theRouter.getName()))
              { routerMap.remove(theRouter.getName()); } ;; <-- Change
            else
              { throw new IllegalArgumentException("Router named '" + theRouter.getName() +"' does not exist."); }
           }
      
       
  • Chaubert Jérôme

    Thanks for the fast fast correction. It works.

    I'm (finally) upgrading to 6.4.1, so I'm sure I'll have more questions and problems. Thanks for your work and help.

     

Log in to post a comment.