Author Topic: Multiple Instances  (Read 27491 times)

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Multiple Instances
« on: September 07, 2005, 12:21:01 pm »
Code::Blocks used to have the multiple instances issue (you could accidentially open the same files in two instances and mess up terribly, especially when double-clicking something in Explorer). This behaviour has been adressed in RC1.

However, it is still not practical. Either you get the same behaviour as before (several copies) or you get a messagebox which disrupts your workflow, but you do not get the action you asked for.

So, during the last weeks, I have sporadically been thinking about this problem. Unluckily, it is not as easy as "well, tell the other instance..." because there is no such thing, at least not in an easy way, and not cross-platform.

wxWidgets does have some IPC support, but honestly, I am unable to understand both the API and the documentation that comes with it. It only seems to work on Windows if you have DDE running, too (which happens to be disabled on my machine).  Also, from various sources on the web, I gather it does not work the same everywhere. In one word: it kind of sucks.
There are proposed solutions of using named pipes or TCP sockets and whatever complicated stuff instead. Named pipes.... uh huh, who wants to implement that on several platforms? Step forward please.
TCP connections... great plan, but on my machine, like on many others too, ZoneAlarm pops up an alert each time a new application opens a TCP connection. This may lead to a quite negative first impression ("What the hell are they doing? Network == Spyware") on a new user.

So... in the end, I came up with the idea that using signal handlers and OS messages (with two separate codepaths) is maybe not so complicated and not so bad at all. It is only a rough idea yet, but well, look at it and give your ideas/comments :)

(I) Change MainFrame::MainFrame to pass CODEBLOCKS_WINDOW_ID to wxFrame rather than -1

(II) If another instance is found:
1. Create a temporary file inside WELL_KNOWN_FOLDER

2. Write the commandline into that file

3. a) on every OS except Windows:
    - send SIGHUP to the other process
    - SIGHUP is caught by the other process and ReadAlienCommandline() is called

3. b) on Windows:
 - use FindWindow(CODEBLOCKS_WINDOW_ID, -1) and PostMessage()
   to post SOME_MESSAGE to the first code::blocks window found
 - override virtual wxApp::ProcessMessage(), and call ReadAlienCommandline() if SOME_MESSAGE arrives

4. ReadAlienCommandline()
 - opens *any* present file in WELL_KNOWN_FOLDER
 - reads them in, parses the commandline, and does something in response (open a source file etc.)
 - deletes the tempfiles

Since we don't open a dozen of instances per second (one every couple of minutes is more like it), the overhead of writing and reading to/from a file should be nil.
"We should forget about small efficiencies, say about 97% of the time: Premature quotation is the root of public humiliation."

grv575

  • Guest
Re: Multiple Instances
« Reply #1 on: September 08, 2005, 11:31:45 pm »

(II) If another instance is found:
1. Create a temporary file inside WELL_KNOWN_FOLDER

...

4. ReadAlienCommandline()
 - opens *any* present file in WELL_KNOWN_FOLDER
 - reads them in, parses the commandline, and does something in response (open a source file etc.)
 - deletes the tempfiles

Never, ever do that.   If the application crashes and leaves temp files behind, it is an absolute nightmare to deal with.  I had to maintain a socket application that did exactly this (temp files in the hopes of a custom cross-platform locking mechanism)  and it was insane to debug and get all states to work correctly.   Ended up just writing a simple app from scratch which used atomic IPC.  Much better to use the OS locking mechanisms or maybe look into the wxWidgets wrappers.

Offline rickg22

  • Lives here!
  • ****
  • Posts: 2283
Re: Multiple Instances
« Reply #2 on: September 09, 2005, 12:01:50 am »
I thought there was the appsomethingserver class so processes could communicate with each other... but shouldn't it be easy just not save the config/workspace if there was another instance running?

grv575

  • Guest
Re: Multiple Instances
« Reply #3 on: September 09, 2005, 05:54:52 am »
Well most multiinstance programs just ignore the issue and let each one modify configuration settings, etc.  Look at netscape.  If you have many netscape processes, they can each change settings and when they hit apply or ok, the settings are updated to the last one to hit apply or ok.

Windows explorer does this as well (you can save windows position, size when it is closed.  the last window closed for a specific folder is the one that gets the final write.  so it's position and size are the one that are restored when you open exporer again to that folder).

Edit: I don't quite get it though.  I have 2 instances of CB open.  I double click a .cbp file (with DDE server off) and it launches a new, 3rd CB.  So what's the gotcha?
« Last Edit: September 09, 2005, 06:03:58 am by grv575 »

Offline rickg22

  • Lives here!
  • ****
  • Posts: 2283
Re: Multiple Instances
« Reply #4 on: September 09, 2005, 07:09:42 am »
Hmmm that's what DDE is for. So it won't open a new C::B instance each time.

Offline David Perfors

  • Developer
  • Lives here!
  • *****
  • Posts: 560
Re: Multiple Instances
« Reply #5 on: September 09, 2005, 08:12:18 am »
Hmmm that's what DDE is for. So it won't open a new C::B instance each time.
But isn't DDE windows only? if so how 'bout *nix?
OS: winXP
Compiler: mingw
IDE: Code::Blocks SVN WX: 2.8.4 Wish list: faster code completion, easier debugging, refactoring

Offline mandrav

  • Project Leader
  • Administrator
  • Lives here!
  • *****
  • Posts: 4315
    • Code::Blocks IDE
Re: Multiple Instances
« Reply #6 on: September 09, 2005, 09:20:53 am »
Hmmm that's what DDE is for. So it won't open a new C::B instance each time.
But isn't DDE windows only? if so how 'bout *nix?

According to wx docs, uner other platforms it uses TCP/IP or named pipes. Haven't tried it though and IIRC the DDE code in C::B is #ifdef'd for windows only...
Be patient!
This bug will be fixed soon...

Offline rickg22

  • Lives here!
  • ****
  • Posts: 2283
Re: Multiple Instances
« Reply #7 on: September 09, 2005, 04:57:27 pm »
The DDE server class is a subclass of ... some more generic class :lol: . Anyway, the wxwidgets docs say a TCP server is effectively cross-platform.

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Re: Multiple Instances
« Reply #8 on: September 09, 2005, 04:58:34 pm »
Edit: I don't quite get it though.  I have 2 instances of CB open.  I double click a .cbp file (with DDE server off) and it launches a new, 3rd CB.  So what's the gotcha?
The gotcha is this:
You may not want to use 10 different editors. I use Code::Blocks not only as a full IDE, but also as a plain editor for about everything (except for HTML, because there is not browser preview).
So you have a file open inside Code::Blocks and do something else (look up something on the web, for example, or answer the phone). Maybe you completely forget about Code::Blocks for an entire hour or two.
So any time later, you double-click on this file you wish to modify. This is quite intuitive because the Explorer window containing the file is still open. However, Code::Blocks is minimised, which you forgot about.
So what happens is you see your file opened in Code::Blocks, you work on, and ten minutes later, you realize that you have been working on two copies, neither of which is consistent.
The same can happen if you add new files to a project or change the build settings.

The idea with tempfiles in KNOWN_LOCATION would have the advantage that it is simple. Not good, but simple. Every system can create temp files, no DDE, OLE, or whatever obscure Microsoft-proprietary technology needed to make it work on Windows (and sadly, wx seems to rely on DDE).
True enough, TCP sockets are a much better solution, but like I pointed out, these may give people running an application-layer firewall on Windows an unneeded bad feeling. Most people I know get quite nervous when they suddenly see "Do you want to allow XXX to act as a server?" or "Do you want to allow XXX to access the internet?".

The problem with leftover files is not as bad as you picture it, really. Adobe Photoshop regularly leaves behind tempfiles on the order of 90-130 megabytes, and few people ever notice. Who would complain about a file which is, say 30 bytes in size.

The only question is what to do with leftover files from the application's PoV.

Lets see what can happen:
1. application starts, no other instance is running
---> no temp file is created. app does rm KNOWN_FOLDER/* to clean up (just to  be sure)
---> everybody is happy
2. application starts, sees another instance, creates tempfile, signals other instance, exits
--->other instance opens all files that are newer than a threshold (say 1-2 seconds?), executes the requested
commands, and does rm KNOWN_FOLDER/*
---> everybody is happy
3. application starts, sees another instance, creates tempfile, but crashes
---> no one knows about the tempfile, but no one cares, either
---> next time the tempfile is deleted anyway
---> the user is angry because the app crashed, but nothing else happens
4. application starts, sees another instance, creates tempfile, signals and exits, but other instance crashes
---> no one knows about the tempfile, but no one cares, either
---> the user is angry because the app crashed, but nothing else happens

So this does not look so bad, does it? A tempfile older than a second or two can safely be ignored, as we only start looking at them when receiving a signal (and that really does not take a long time to propagate). And since we "own" KNOWN_FOLDER, we can delete whatever we find in it, too.
"We should forget about small efficiencies, say about 97% of the time: Premature quotation is the root of public humiliation."

Offline mandrav

  • Project Leader
  • Administrator
  • Lives here!
  • *****
  • Posts: 4315
    • Code::Blocks IDE
Re: Multiple Instances
« Reply #9 on: September 09, 2005, 06:33:06 pm »
Thomas, just to get this straight: you 're talking about non-windows version, right? Because under windows it doesn't launch a new instance for every file you double-click in explorer, instead it re-uses the open instance...
Be patient!
This bug will be fixed soon...

grv575

  • Guest
Re: Multiple Instances
« Reply #10 on: September 09, 2005, 07:49:40 pm »
The gotcha is this:
...
So what happens is you see your file opened in Code::Blocks, you work on, and ten minutes later, you realize that you have been working on two copies, neither of which is consistent.
The same can happen if you add new files to a project or change the build settings.

What about file change detection a lot of editors do?  This would, whenever the window is made active, check the timestamp on the active file real quickly (or maybe all open files) and then alert the user the file has changed on disk - would they like to reload it or not.  Very handy, I use this all the time in ultraedit since then you can just have a file open, run some command that changes the file, and then ue will just reload it for you (easier than browsing for the file again).  This should keep files which are multiply open in sync as well.

Quote
Lets see what can happen:
1. application starts, no other instance is running
---> no temp file is created. app does rm KNOWN_FOLDER/* to clean up (just to  be sure)
---> everybody is happy

Yes but that create/delete file stuff is not atomic (and if you try to make it so, then you need to use OS locking stuff anyway).  Crazy states that you did not anticipate or code for can make for some really strange behavior when you do this sort of stuff.  For example, say the user highlights two files in explorer that they wish to modify.  Two CB processes start and this type of IPC mechanism will more than likely fall over.
« Last Edit: September 09, 2005, 07:53:48 pm by grv575 »

Offline mandrav

  • Project Leader
  • Administrator
  • Lives here!
  • *****
  • Posts: 4315
    • Code::Blocks IDE
Re: Multiple Instances
« Reply #11 on: September 09, 2005, 08:37:50 pm »
What about file change detection a lot of editors do?  This would, whenever the window is made active, check the timestamp on the active file real quickly (or maybe all open files) and then alert the user the file has changed on disk - would they like to reload it or not.  Very handy, I use this all the time in ultraedit since then you can just have a file open, run some command that changes the file, and then ue will just reload it for you (easier than browsing for the file again).  This should keep files which are multiply open in sync as well.

Already supported.
Be patient!
This bug will be fixed soon...

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Re: Multiple Instances
« Reply #12 on: September 11, 2005, 12:48:21 pm »
Yiannis,
unluckily no... Windows does not use the running instance of Code::Blocks.
I don't know if there is a way that Windows will actually do that. There are such things on other platforms - on MacOS, if I remember well from the old days, you set a flag in the executable's resource fork, and it just works, but no idea about Windows. Googling on terms like "single instance windows" only gets you variants of using a mutex and raising a window which has the same class as your class. That is not particularly useful, though (n.b.: Dev-CPP has the very same problem as Code::Blocks, but for example MS Word works just fine, so it must *somehow* be possible).

The current alert box "another instance is already running, aborting now" does prevent damage, but it disrupts your workflow, hence my thoughts about how to pass the commandline to another instance.

grv575,
indeed Code::Blocks does check file modification times. However, that does not necessarily help. If you have a modified copy in RAM and minimize the application, you can still open the same file in another instance and edit that. Especially if your thoughts leap a lot from one thing to another (like mine do, unluckily) and if you always have 20-30 files open in every program, this can go unnoticed quite easily. Eventually, you realize you have three files with two versions each in RAM, and you have to manually merge them with copy and paste, which is quite annoying and error-prone. Especially since you don't remember what you modified in which version of any of the three files...

To prevent that, you would have to kind of... exclusive lock the file or something, but that is even more evil than tempfiles. If your IDE crashes, you have to reboot Windows to edit your sources, ouch...
Or keep a "codeblocks open files log" or something, maybe. This is not as bad as exclusive locking.

EDIT:
Yes but that create/delete file stuff is not atomic (and if you try to make it so, then you need to use OS locking stuff anyway).  Crazy states that you did not anticipate or code for can make for some really strange behavior when you do this sort of stuff.  For example, say the user highlights two files in explorer that they wish to modify.  Two CB processes start and this type of IPC mechanism will more than likely fall over.
It should be possible to make it "atomic enough" without much pain.
All you have to do is three things:
  • Get all files from the directory (using wxDir::GetAllFiles, for example). This gives you a snapshot of what is currently in your directory, what comes after that is not your business
  • Sort out the ones that are "old" (remember we were sent a message, this takes a few microseconds only, so everything older than 1-2 seconds can safely be ignored), and act on the ones that remain
  • Delete all files you know about (so, not all files in KNOWN_DIR at that time, but all files you found earlier)
So, if two instances are started, three things can happen:
  • Instance 1 notifies the running app after creating its tempfile, and instance 2 creates its tempfile a few milliseconds later (and notifies). The application scans the directory twice, opening file 1 on run 1, and file 2 on run 2.
  • Instance 2 writes its tempfile before Instance 1 sends out the notification. The running app will scan the directory, and find both files, take action and delete them. Later, instance 2 will send out a notification, but without effect.
  • Instance 2 creates a tempfile, but is not finished writing when the running app receives the notification from instance 1. No problem! Since the file is still exclusively opened for writing by instance 2, it cannot be deleted by the running app. The running app will therefore know it must ignore that file. After receiving the notification from instance 2, the app scans the directory as usual, and finds the same file again, opening it, etc.
For this to work, the order of actions must be different of course, but the above is for clarity  :)
« Last Edit: September 11, 2005, 01:36:30 pm by thomas »
"We should forget about small efficiencies, say about 97% of the time: Premature quotation is the root of public humiliation."

Offline mandrav

  • Project Leader
  • Administrator
  • Lives here!
  • *****
  • Posts: 4315
    • Code::Blocks IDE
Re: Multiple Instances
« Reply #13 on: September 11, 2005, 03:34:17 pm »
Yiannis,
unluckily no... Windows does not use the running instance of Code::Blocks.
I don't know if there is a way that Windows will actually do that. There are such things on other platforms - on MacOS, if I remember well from the old days, you set a flag in the executable's resource fork, and it just works, but no idea about Windows. Googling on terms like "single instance windows" only gets you variants of using a mutex and raising a window which has the same class as your class. That is not particularly useful, though (n.b.: Dev-CPP has the very same problem as Code::Blocks, but for example MS Word works just fine, so it must *somehow* be possible).
to work, the order of actions must be different of course, but the above is for clarity  :)

I don't use "single instance" C::B because I have to debug it :P
This may have something to do with it. If you allow multiple instances, at least in windows, it does re-use existing C::B instances for source files and projects. Only workspaces require a new instance to be launched - and this is intentional.
Maybe it only has to do with the single instance thing. Can you confirm this, because I haven't booted windows for a couple of weeks :P
Be patient!
This bug will be fixed soon...

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Re: Multiple Instances
« Reply #14 on: September 11, 2005, 05:29:09 pm »
Confirmed. The following image shows:
1) instance created by double-clicking on app.cpp
2) another instance created by double-clicking on app.cpp again
3) instance created by clicking on taskbar icon, opening my default workspace
4) instance created by clicking on taskbar icon again, also opening my default workspace

It is possible to edit app.cpp in either editor concurrently (as shown) and it is possible to change projects and workspaces in either instance.
The in my opinion correct behaviour would be to put the instance having app.cpp already open to the foreground instead of spawning more instances.

"We should forget about small efficiencies, say about 97% of the time: Premature quotation is the root of public humiliation."