1

Is it necessary to call cleanup() before close() in a PythonInterpreter in Jython every time?

I've been reading the docs, but I don't find much information about this function. The javadocs don't say anything at all. The closest information I've found is here, in readthedocs, where they explain that a cleanup is necessary in some cases programming with threads, and I'm not even sure they refer to this particular function.

I wonder when I need to call cleanup()... and if the answer is always, then why would they make cleanup() and close() separate functions?

Miguel
  • 2,130
  • 1
  • 11
  • 26
  • 2
    I don't really have an answer, but `cleanup()` is called from `close()`. See https://github.com/jython/jython/blob/master/src/org/python/util/PythonInterpreter.java#L414. – mzjn Aug 23 '20 at 12:44
  • Well, it's enough for me. Thank you! – Miguel Aug 23 '20 at 12:59

1 Answers1

1

Okay, I've been reading the Jython source code and doing some tests. Here is what I found:

What cleanup() does: It takes charge of the unhandeled resources, like running threads and files.

What cleanup() doesn't: Reset the state of the interpreter in any form; imported modules and defined variables are kept.

The following examples show this behavior:

Example 1

Let's import a module, define a variable and open a file.

PythonInterpreter py = new PythonInterpreter();

String code1 = "import sys;"
        + "a=45;"
        + "f = open('test.txt')";
String code2 = "print(sys.version_info);"
        + "print(a);"
        + "print(f.closed)";

// first execution
py.exec(code1);
py.exec(code2);

// second execution
py.cleanup();
py.exec(code2);

py.close()

It outputs

sys.version_info(major=2, minor=7, micro=2, releaselevel='final', serial=0)
45
False
------
sys.version_info(major=2, minor=7, micro=2, releaselevel='final', serial=0)
45
True

The module sys and the variables a and f still exist with the same values after the cleanup, but the open file is closed.

Example 2

For this, func is a slow function that takes aprox 2 seconds to complete (more than a normal cleanup()).

PythonInterpreter py = new PythonInterpreter();

String code3 = "from threading import Thread\n"
        + "def func():\n"
        + "  print 'th start'\n"
        + "  for i in range(0,20000000):\n"
        + "    x=i\n"
        + "  print 'th done'\n"
        + "th = Thread(target=func)\n"
        + "th.start()";
String code4 = "print th.isAlive()\n"
        + "th.join()";

// first execution
py.exec(code3);
py.exec(code4);
System.out.println("------");   

// second execution
py.exec(code3);
py.cleanup();
py.exec(code4);

py.close();

That outputs:

th start
True
th done
------
th start
th done
False

In the first execution, the main thread has plenty of time to check if th is alive and print it. In the second one, it always waits for th to finish, meaning that the cleanup() joins the thread somewhere.

Conclusion

As @mzjn pointed out, the close() function calls cleanup() and that makes sense, so you never need to call cleanup() before close(). The only case where you could need to call it manually would be if you wanted to continue using the PythonInterpreter but needed to close all open files and join all threads.

Miguel
  • 2,130
  • 1
  • 11
  • 26
  • 1
    Thanks for the detailed info. What if I have an interpreter listening to all coming `interpreter.exec()` or `interpreter.eval()` requests? Do I need to periodically manually call `cleanup()`? I am conducting a load test where I can see for the same python code, the execution time is becoming slower and slower. And after a period of time, the execution time is getting better, and then slower again. But I can see the overall execution time is getting longer. I am thinking if the `cleanup()` can help or not? – Vision Feb 05 '21 at 18:19
  • If you are managing files and threads correctly in your code (closing and joining them, respectively) there would be no need, as far as I understand. – Miguel Feb 06 '21 at 19:15
  • I am not managing files but just initializing instance and call the method. As far as I understand based on the Jython doc, I am able to directly use interpreter's own thread management to handle the coming `eval` or `exec` call (as the interpreter is thread safe): I have a pool to control how many requests are currently handled, but they are all using the same interpreter. Before you replied, I have tried to periodically call `cleanup()`, and it seems the memory consumption is more stable, and the execution time is more stable as well. But I might need more load test to see if it truly helps. – Vision Feb 06 '21 at 22:57
  • @Vision did you find a solution to yours, i see the same – csf Jul 05 '22 at 09:08