Title: geany lxterminal interaction ===================================
27 December 2020
This is a re-issue of issue:
https://github.com/geany/geany/issues/2703
that clarifies and avoids distracting discussion and false correlations. That issue will be closed momentarily. This issue expressly provides a deterministic way to generate the fault. It is divided into five sections with headings underscored by '=====':
0) this introduction 1) specification of context 2) empirical deterministic behavior 3) analysis 4) concluding remarks
Context =======
Distro : Bodhi linux 5.1.0 Based on : Ubuntu 18.04 Geany : 1.32 Lxterminal : 0.3.1
The only item that needs further explanation is lxterminal. The other contexts can probably change and the issue will remain. The issue may apply to other x-terminals, but I have not verified that.
Lxterminal is not the default Bodhi linux x-terminal - terminology is. Terminology is barbaric and so I always install lxterminal and uninstall terminology, resetting any persistent symlinks to point to lxterminal. Therefore, geany is using lxterminal on my system, via the x-terminal-emulator symlink.
Lxterminal is a tabbed GTK x-terminal application that is apparently written in a code re-entrant, reusable fashion. What this means is that there is only one binary in memory for a given user, no matter how many lxterminal tabs or windows are open. There may be only one binary for multiple users, but I have not verified that.
The practical effect is that there is an 'original' execution instance with some corresponding process id that is defined by the first invocation of lxterminal after boot or the first invocation of lxterminal after all previous invocations have closed. The original PID persists until all lxterminal windows and tabs are closed. That is to say, when later instances remain after the truly 'first' instance has closed, the later instances retain the 'original' process id, for they share that single executable binary in memory.
When additional lxterminals are launched, by whatever mechanism, including from within geany, they carry a new process id during their fleeting existence. This can be determined in spite of the brevity of the new invocation, by executing 'lxterminal& echo $!' from within lxterminal, which yields a PID that will not be visible in the task manager, since it has already ended. Indeed the command 'lxterminal' without the '&' returns immediately after the new window opens.
The lxterminal code is smart enough to detect that it has re-entered, and that this is a new instance. It therefore creates a new data block for the new window or tab, and spawns a new shell instance. The latter is distinct from any existing shell instance and has its own process id. Within that shell there naturally may be other new processes spawned, as will be the case when geany executes a python module.
After creating the new data block, the new invocation lxterminal with the fleeting PID exits, and the original instance with the original process id manages all of the existing instances, isolating them by their data blocks. This is actually a very smart design. The prevailing execution model is that of a single processor pre-emptive multi-tasking system. Therefore, there may be only one instance executing at a given time, and properly handling mutexes keeps them from stepping on each other, even in a multi-processor environment (which I have) where truly parallel execution can happen.
Empirical Deterministic Behavior ================================
In the following observations, I have a fresh cold boot and never have processes other than geany and lxterminal running (except of course the multitude of OS and window manager processes that make the world go around). There are no suspensions or hibernations, power is from the wall with an adequately charged battery, and wifi and networking are disabled. To keep instance origin clear, the independent lxterminal is opened on a different desktop from the one geany uses. All of geany's lxterminals appear on geany's desktop.
Observations are specified by the sequence of invocations. I invoke geany just once. You may easily replicate the behavior by following each step.
Behavior described as 'bad' indicates the case where geany changes the gear icon to circle-slash, and the latter persists until the lxterminal instance window is closed, or the user clicks circle-slash, which closes the lxterminal, or the user enters ^C in the lxterminal which closes it, or the sub-process running in the lxterminal completes and the user presses enter, which closes the lxterminal.
This is the one-dimensional, one process at a time mode of thinking that apparently the developers of geany have considered 'correct' behavior for a very long time. It prevents running multiple programs from within geany, for example separately developed co-routines, or two instances of the same program with small alterations so that the output can be compared, or many other desirable common use cases for multi-processing development in a pre-emptive multi-user, multi-tasking environment.
I have had the luxury of being protected from the 'correct' behavior due to my slavish adherence to long standing habits - I always open a terminal as the first application after boot. That was until 24 December 2020, when I accidentally discovered the 'correct' behavior and was horrified.
Behavior described as 'good' is my preferred multi execution behavior. In this case the geany gear icon fleetingly changes to a circle-slash and immediately reverts to a gear icon. Therefore, multiple lxterminals launched by geany can coexist, including multiple distinct execution instances of the same program. Each lxterminal may be closed by any of the above described mechanisms except using the geany icon, which, being a gear now, will launch a new execution instance.
0) cold boot 1) start geany - 10 tabs are open with 10 different python files in them. 2) run pygtk2_color_chooser.py by clicking the gear icon. This is a simple program that displays the old color chooser, which I prefer to the new one. 3) bad behavior.
4) close the geany lxterminal (there can be only one) by whatever means. 5) start the first (original) lxterminal instance, independent of geany. 6) run pygtk2_color_chooser.py by clicking the gear icon 7) good behavior. 8) run pygtk2_color_chooser.py again by clicking the gear icon - there are now two color choosers. 9) good behavior. 10) run another program - there are now two color choosers and another lxterminal. 11) good behavior.
12) close all lxterminals including the original. 13) run pygtk2_color_chooser.py by clicking the gear icon, leaving it open; this **is** the original. 14) start an lxterminal instance, independent of geany; this is **not** the original. 15) close the color chooser by whatever means except the circle-slash icon. 16) notice that the independent lxterminal persists. 17) notice that the circle-slash icon in geany persists. 18) click the circle-slash icon, which changes to a gear. 19) notice that the **independent** lxterminal has **closed** 20) **very bad behavior** - all lxterminals are now closed
21) run pygtk2_color_chooser.py by clicking the gear icon, leaving it open; this **is** the original. 22) start an independent lxterminal instance, independent of geany; this is **not**the original. 23) notice that the circle-slash icon in geany persists. 24) close the color chooser by the circle-slash icon. 25) notice that both the color chooser and the independent lxterminal are closed. 26) **very very bad behavior** - all lxterminals are now closed
27) repeat steps 12) through 17) 28) start another independent lxterminal instance; this is also **not** the original. 29) notice both independent terminals are rooted in the PID of the now closed color chooser. 30) click the circle-slash icon, which changes to a gear. 31) notice that _both_ **independent** lxterminals have **closed** 32) **extremely bad behavior** - all lxterminals are now closed
Importantly, all of this is rigorously, predictably, repeatable.
Analysis ========
In this section I will infer the internal behavior of geany based on the preceding raw data.
0) after startup, geany waits in an event loop. 1) when an execute icon click occurs geany enters a callback which: a) changes the icon to a circle-slash b) invokes the terminal tool command with the build execute command c) saves the PID of the spawned process d) returns to the event loop 2) when the circle-slash icon is clicked geany enters a callback which: a) changes the icon back to a gear b) kills the saved PID c) returns to the event loop 3) if instead the lxterminal is closed by ^C or closing the lxterminal window: a) geany receives a process terminated signal (event) from the OS b) geany enters a callback which: i) changes the icon back to a gear ii) kills the saved PID iii) returns to the event loop
This all seems very logical and trouble free, if your thinking is one-dimensional and application centric. It is not. Geany has no knowledge of other processes on the system. Geany should have no control or influence over other activities, but as we have seen, this is **not** the case in some circumstances.
This sequence of geany operation if viewed in isolation will obviously not allow multiple execution instances to be launched by geany as in sequence 0) through 3) in the empirical data section above. This apparently is the developer's intended 'correct' behavior.
If we posit the existence of an independent lxterminal as in the sequence 4) through 11) in the empirical data section above, we see that the actions 1) a) through d) occur, then the new invocation of lxterminal immediately exits so that actions 3) a) through iii) also occur. The new lxterminal invocation is already dead, having exited, so kill PID is a NOP. The result is the geany icon fleetingly changes to circle-slash and back to a gear, and the spawned lxterminal remains open since it is anchored by the _original_ independent invocation. This is very good and very convenient for those of us that think multi-dimensionally and work on multi-processing projects.
If instead we posit that there had been no other lxterminal instance, but instead a new independent instance was invoked after events 1) a) through d), then we are in trouble. In the sequence 12) through 20) in the empirical data section, as well as in sequences 21) through 26) and 27) through 32) this is the case.
In the first of these sequences, closing the geany lxterminal window does **not** invoke actions 3) a) through iii). This is because the independent lxterminal exists and shares the geany spawned PID, so there is no OS termination signal. The independent terminal persists and the circle-slash icon persists. Clicking the circle-slash icon invokes actions 2) a) through c), which kills the independent lxterminal, since it has the geany spawned PID: very bad.
In the second of these sequences, clicking the circle-slash icon invokes actions 2) a) through c), which kills both the geany spawned window and the independent invocation: very, very bad.
In the last of these sequences the same actions close **all** other running instances of lxterminal, despite the fact that the geany spawned window is already closed, since **all** are rooted in the same original lxterminal, which in this case, is the one spawned by geany: this is extremely bad.
Concluding Remarks ==================
I am fearful that the geany developers will resolve this by somehow enforcing the one-dimensional design paradigm. It would be preferable for users to move the design paradigm into a modern multi-dimensional, multi-tasking, multi-processing mindset.
For the present I have this advice for users that have encountered these peculiar behaviors. Open an lxterminal before you start working and leave it open for the entire session, even if you don't use it. That will give you the advantage of transforming geany into a multi-processing development tool, unintended by geany's developers, and it will also protect you against anomolous termination of other lxterminal processes or the processes they might have spawned. It will also remove unexpected variations in geany's user interface icon presentations, which I suppose is all anybody has previously noticed, and they have been dismissed as 'Heisenbugs'.
TL;DR Summary
The OP prefers lxterminal which is a multi-tabbed application where the second and subsequent invocations open a tab in the original process and exit, exactly as Geany itself does.
This has two side effects when lxterminal is used as the Geany execute terminal,
1) the second and subsequent invocations exit immediately, causing the Geany execute state to reset and another execution to be possible, the OP prefers this behaviour, and
2) if Geany happens to cause the first invocation of lxterminal the execute state will not reset as the lxterminal process does not exit, and if the execute command is used to terminate the process, all the tabs in that process terminate, no matter how they were invoked. The OP does not like this behaviour.
A workaround is to open lxterminal prior to starting Geany, so no Geany invocation is the first lxterminal.
An alternative workaround was suggested twice on #2703, edit the `Terminal` setting to run in a shell so the actual terminal can be detached and the shell return, then it won't matter if its the first or last invocation, the OP preferred behaviour is preserved. If the nesting makes the quotes too hard in the `Terminal` entry it can be a shell script instead.
Otherwise there don't appear to be any actual actionable suggestions made.
github-comments@lists.geany.org