Skip to content

Python-Clojure/Leiningen/Java-Interop: Leiningen-Prozess kehrt nicht zurück.

Ich habe jetzt einige Zeit gebraucht um folgendes Problem zu lösen:

Kurzfassung und Abstrakt

ruft /. Der Aufruf des -Prozesses kommt nicht zurück. Ewiges Warten.

Langfassung mit technischen Details

Aus einem Python-Skript heraus wird ein Clojure-Skript (bzw. eine Clojure-Anwendung) gestartet. Weil das Py-Skript das Ergebnis der Verarbeitung im Clj-Skript benötigt, wird auf die Rückkehr des Aufrufs gewartet.

Der Clojure/Java-Prozess wird auf Python-Seite mittels subprocess.Popen gestartet. Dann wird mit wait() auf den Returnwert gewartet und stdout/stderr des Java-Prozesses ausgelesen. Soviel zur Theorie.

Bislang habe ich das Clojure-Skript mittels Leiningen gestartet – weil da die Abhängigkeiten praktischerweise anhand der sowieso immer aktuellen Projektdefinitionsdatei verwaltet werden. Sprich: der Java- stimmt immer. Unschön daran ist, dass deshalb zwei Java-Prozesse gestartet werden: einer für , das dann wiederum den eigentlichen Clojure-Java-Prozess mit dem entsprechenden Classpath startet. Das aber nur nebenbei.

‚Plötzlich‘ (also irgendwann letzte Woche) ist der Leiningen-Prozess nicht mehr terminiert. Der Clojure-Prozess, die eigentliche Verarbeitung also, wohlgemerkt schon. Schon seltsam.

Problemlösungsversuche

Die üblichen Verdächtigen (shutdown-agents) und (System/exit) konnte ich nicht der Schuld überführen.

Auch ein Wiederumstellen auf Nailgun – mit dem manuellen Anpassen des Classpaths – brachte keine Änderung. Auch hier kam der aufgerufene ng-Prozess (statt des Leiningen-Prozesses) nicht zurück.

Auch das totale Weglassen all des Brimbamboriums (also ein Direktaufruf von java.exe mit dem vom Nailgun-Aufruf bereits bekannten Classpath) brachte nichts. Auf der (shutdown-agents)-Problematik beharrend, vermutete ich weiterhin ein Problem mit dem Threadpool. Sowas wie nicht-daemon-Threads, die ein Beenden der Java-Runtime verhinderten. Eine Umstellung der mir bewussten Code-Änderung, die sich auf Threads bezog, auf Daemon-Threads brachte jedoch keine Besserung des unguten Zustands. Der mistige Prozess wollte einfach nicht terminieren.

Zusammenfassung des Zwischenstands

Nochmal kurz eine Zusammenfassung des Status Quo: Java-Prozess. Null Prozessorlast. Terminiert auch nach minutenlangem Warten nicht.

Weitere Schritte in Richtung Lösung

Heute habe ich den Java-Prozess dann instrumentiert, so dass ich ihn mit JSwat untersuchen konnte. Hätte ich mir die ‚Mühe‘ mal früher gemacht.

Erste Erkenntnisse

Der main-Thread hing in einem FileOutputStream.writeBytes – weiter oben im Stacktrace fand sich ein Aufruf eines clojure.core/println. Da bin ich schnell auf ein Problem in der Prozesskommunikation gekommen. Meine Vermutung ist, dass der Java-Prozess darauf gewartet hat, dass sein Output gelesen wird. Das hat die Python-Seite aber anscheinbar nicht gemacht.

Heureka!

Bei Stackoverflow bin ich dann fündig geworden: statt proc.wait() verwende ich jetzt proc.communicate(). Den Rückgabewert, den bislang wait() lieferte, liefert jetzt – kryptischerweise – proc.returnvalue. Ausprobiert. Funktioniert.

Schluss mit lustig

Hätte ich doch nur JSwat früher bemüht. Die Instrumentierung bleibt jetzt erstmal drinnen –
Allzeit bereit!

Post a Comment

Your email is never published nor shared. Required fields are marked *