Author Topic: Non-DDE IPC?  (Read 21041 times)

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Non-DDE IPC?
« on: March 30, 2006, 02:15:35 pm »
(actually this should go into "Bounties :lol:)

To get rid of DDE and all the problems caused by it once and for all, we might want to think about a solution that does without DDE again.

Under Linux, we could probably implement the functionality that we need very easily and very efficiently using msgsnd() and msgrcv(). Just take everything passed in argv[] and send them one by one. The other end reads them one by one from a poller thread and posts a wxCommandEvent to the application object. Should be like 10-15 lines of code.

If Microsoft supported named pipes for Windows98, we could have used those in the exact same way. But of course that would be too easy, named pipes only work on NT4/2000/XP... so we have to do something less elegant.

I believe something involving a mutex, a semaphore, and a shared memory area would work.

The idea is this:
  • call the instance checking function as early as possible (before initialising anything)
  • this may forward the commandline and terminate the application, or the application goes on like normal
  • the first instance of Code::Blocks creates a mutex, a semaphore, and a shared memory page
  • the primary instance also starts a poll thread which blocks in the semaphore
  • all other instances know they are not the first instance from the mutex's error code
  • in a non-primary instance, all argv arguments are written to a shared memory area one by one, and the semaphore is posted every time (that'll weak up the poller)
  • the poller reads from the shared memory one by one and blocks again

This scheme misses one vital thing yet: we either need to append items to a linked list inside the shared memory area (lock mutex!), or we have to make sure only exactly one message can be passed at a time. Otherwise, we will almost certainly overwrite messages before the poller gets them, sooner or later.
I think a linked list is probably too complicated (we would need more excessive memory management, too).
We are probably off better by waiting for a second semaphore (and thus blocking) until the poller thread posts the semaphore (it does that once in every iteration of its loop).
That way, exactly one commandline argument at a time can be forwarded to the primary process (no matter how many processes are being started simultaneously).
The order of appearance is not guaranteed by this, but we don't need that feature. Also, it is not overly efficient, because it generates n messages for n commandline arguments. However, it is simple. Simple things are good.

Here is some code which I quickly typed together to illustrate (it will probably not compile, and it does not prevent overwriting earlier arguments, but the general idea should become clear):
Code
class PollingThread : public wxThread //  should actually be a singleton
{
    HANDLE sem;
    bool abort;
    void* shared;
   
    PollingThread(HANDLE s, void* shm) : abort(false), shared(shm)
    {
        sem = DuplicateHandle(s);
    };
   
    virtual int Entry()
    {
        for(;;)
        {
            WaitForSingleObject(sem, INFINITE);
            if(abort)
                break;
               
            int *code = (int*) shared;
            char *argv = (char*) ( ((int*) shared) +1 );
           
            wxCommandEvent e(*code);
            e.SetString(cbC2U(argv));
            wxTheApp->PostMessage(e);
        }
        return 0;
    };
   
    void NotifyAppShutdown() // have to call this at shutdown, or the app will hang
    {
        abort = true;
        ReleaseSemaphore(sem, 1,  NULL);
    };
};


class InstanceChecker
{
    HANDLE  hMutex;
    HANDLE  hSemaphore;
    bool primary;
   
    void*   shared;
   
public:

    InstanceChecker()
    {
        hMutex = CreateMutex (NULL, FALSE, TEXT("Code::Blocks ipc mutex"));
        primary = GetLastError() == ERROR_ALREADY_EXISTS; // who am I?
       
        hSemaphore = CreateSemaphore(NULL, 0, 1, TEXT("Code::Blocks ipc semaphore"));
       
        if(primary)
        {
            HANDLE hFile = CreateFile ("/temp_path/codeblocks.ipc", GENERIC_READ | GENERIC_WRITE,
                                       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
                                       
            if (hFile == INVALID_HANDLE_VALUE)
                ; // uh... what to do now?
               
            SetFilePointer(hFile, 512, 0, FILE_BEGIN); // make sure we have 512 bytes of storage
            SetEndOfFile(hFile);
           
            HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
            shared = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
           
            CloseHandle(hFile);
            CloseHandle(hMapping);
           
            PollingThread t = new PollingThread(hSemaphore, shared);
            t->Run();
        }
        else
        {
            // Give up time slice, for the unlikely case that two processes are started at almost exactly the same time at first run.
            // In that case, we should allow the other process to create the shared memory file.
            // Two full slices are ~100 ms, that should be more than enough, even in the worst of cases.
            Sleep(0);
            Sleep(0);
            HANDLE hFile = CreateFile ("/temp_path/codeblocks.ipc", GENERIC_READ | GENERIC_WRITE,
                                       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                                       
            if (hFile == INVALID_HANDLE_VALUE)
                ; // might want to retry in this case
               
            HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
            shared = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
           
            CloseHandle(hFile);
            CloseHandle(hMapping);
        }
    };
   
    ~InstanceChecker()
    {
        if(shared)
            UnmapViewOfFile(shared);
        // Don't delete file. For one reason, it is small and being reused.
        // Also, it avoids the possible race condition with several processes being launched simultaneously.
        CloseHandle(hSemaphore);
        CloseHandle(hMutex);
    }
   
    void DoCheck(int argc, char* argv)
    {
        if (primary)
            return;   // continue normal operation
           
        if(argc == 0)
        {
            PostOtherInstance(ACTIVATE, wxEmptyString);
        }
        else
        {
            for(unsigned int i = 0; i < argc; ++i)
                PostOtherInstance(OPEN, cbC2U(argv[i]));
        }
        wxTheApp->ExitMainLoop();
    };
   
    void PostOtherInstance(int event, const wxString& arg)
    {
        int *code = (int*) shared;
        char *argv = (char*) ( ((int*) shared) +1 );
       
        *code = event;
        strcpy(argv, arg);
        ReleaseSemaphore(hSemaphore, 1,  NULL);
    };
}

Unluckily, I cannot spend the necessary time to implement this properly at the present time, as  I have every minute until 30 April already planned out. :(

Now here's the question:
Is anybody (preferrably someone experienced in both IPC and Windows programming) volunteering to follow that idea and make it work? ;)
"We should forget about small efficiencies, say about 97% of the time: Premature quotation is the root of public humiliation."

takeshimiya

  • Guest
Re: Non-DDE IPC?
« Reply #1 on: March 30, 2006, 02:32:39 pm »
Never used IPC, but the wx sample 'IPC' using TCP/IP isn't enough?
Or the problems of doing IPC with TCP/IP is the firewall thing?

Offline Der Meister

  • Regular
  • ***
  • Posts: 307
Re: Non-DDE IPC?
« Reply #2 on: March 30, 2006, 02:35:08 pm »
Does any desktop-firewall block tcp/ip traffic from and to 127.0.0.1?
Real Programmers don't comment their code. If it was hard to write, it should be hard to understand.
Real Programmers don't write in BASIC. Actually, no programmers write in BASIC, after the age of 12.

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Re: Non-DDE IPC?
« Reply #3 on: March 30, 2006, 02:38:44 pm »
Does any desktop-firewall block tcp/ip traffic from and to 127.0.0.1?
Yes, ZoneAlarm shows big, red warning alerts like "Code::Blocks is trying to act as a server". That does not precisely look nice.
We don't want to give a newbie the impression that we're running a spambot or a backdoor server or anything, right?
"We should forget about small efficiencies, say about 97% of the time: Premature quotation is the root of public humiliation."

takeshimiya

  • Guest
Re: Non-DDE IPC?
« Reply #4 on: March 30, 2006, 02:41:41 pm »
So, the only problem with TCP/IP is the firewall thing?
Or there are other problems attached with it?

(Just wondering for personal usage for doing IPC).
« Last Edit: March 30, 2006, 02:43:36 pm by Takeshi Miya »

Offline mandrav

  • Project Leader
  • Administrator
  • Lives here!
  • *****
  • Posts: 4315
    • Code::Blocks IDE
Re: Non-DDE IPC?
« Reply #5 on: March 30, 2006, 02:44:44 pm »
What are the DDE problems that caused this post?
Can someone enlighten me? Thomas?
Be patient!
This bug will be fixed soon...

TheNullinator

  • Guest
Re: Non-DDE IPC?
« Reply #6 on: March 30, 2006, 03:06:03 pm »
What are the DDE problems that caused this post?
Can someone enlighten me? Thomas?

The only remaining DDE problems that I've managed to come across is when default.conf gets deleted (but associations are already set) and when DDE is turned off on the command line.  The former is caused by the compiler selection dialog and the latter is caused because the DDE part of the associations still exist in the registry so Windows thinks Code::Blocks still uses DDE.  At least both of these issues were present last time I checked (2 days ago).

By the way, wouldn't it be easier to use WM_COPYDATA?  Or is doing this using wxWidgets a PITA?  The window can be easily found using FindWindow() if you know the class name and a dummy (hidden) window could be set up before the main wxWidgets frame is up and running to catch messages that may happen while the program is initializing.  The dummy window could be destroyed as soon as the wxWidgets frame is up.  That last bit depends on whether or not you can specify a window class name in wxWidgets and add your own WindowProc() style code to handle the WM_COPYDATA message.

I'll go have a look in the wxWidgets docs for that last bit to see if it is supported for the Windows wxWidgets.

Cheers
« Last Edit: March 30, 2006, 03:08:30 pm by TheNullinator »

sethjackson

  • Guest
Re: Non-DDE IPC?
« Reply #7 on: March 30, 2006, 03:07:23 pm »
Does any desktop-firewall block tcp/ip traffic from and to 127.0.0.1?

Yes mine does, and it asks me wether to permit traffic or not. I agree with thomas. It gives a negative impression.
« Last Edit: August 12, 2006, 04:29:29 pm by sethjackson »

Offline Der Meister

  • Regular
  • ***
  • Posts: 307
Re: Non-DDE IPC?
« Reply #8 on: March 30, 2006, 03:15:44 pm »
I wonder why a desktop firewall blocks traffic that only uses 127.0.0.1 as source and destination. This doesn't make any sense as neither this traffic can go out of this machine nor can any traffic from outside come to this process.
But anyway, as these damn firewall at least report such traffic it is really not a good solution.
Real Programmers don't comment their code. If it was hard to write, it should be hard to understand.
Real Programmers don't write in BASIC. Actually, no programmers write in BASIC, after the age of 12.

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Re: Non-DDE IPC?
« Reply #9 on: March 30, 2006, 03:33:58 pm »
What are the DDE problems that caused this post?
Can someone enlighten me? Thomas?
Well, we're having one issue after the other (the broken batch builds being the last in a long row) and somehow every problem seems to be related in some way to either DDE directly or a change which was necessary to make DDE work. Maybe that's just my impression, but it looks to me that way. :lol:

I mean, this is just a proposal, we don't need go that way if there are any objections. But isn't it at least worth thinking about? I believe we might get a solution which is a lot less painful (and could implement the same thing for Linux with minimal changes, too). We would also need less registry tampering :)

Quote
I wonder why a desktop firewall blocks traffic that only uses 127.0.0.1 as source and destination.
That is what I call SIS ("self-important software"). Firewalls and Antivirus software does that to stress its own importance. I suspect that antivirus scans are being made extra slow on purpose, too, for the same reason. You have to see that it is working hard. If there is never a network alert and a scans of your entire hard disk finishes in 5 minutes, then it can't be any good.
If you weren't being told that the software just saved your life again every 5 minutes, then you would not be willing to spend money on a product that makes your PC 30% slower...

"Warning: Spooler subsystem is trying to access the internet: 127.0.0.1. View detailled threat information?"
"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: Non-DDE IPC?
« Reply #10 on: March 30, 2006, 03:38:58 pm »
What are the DDE problems that caused this post?
Can someone enlighten me? Thomas?
Well, we're having one issue after the other (the broken batch builds being the last in a long row) and somehow every problem seems to be related in some way to either DDE directly or a change which was necessary to make DDE work. Maybe that's just my impression, but it looks to me that way. :lol:

These issues are not related to DDE at all...
Anyhow, writing a replacement for DDE would take great amounts of time (which we don't have) and testing under all supported platforms.
It's not worth it.
We will just fix this batch-build issue and go on with other, important, things ;)
Be patient!
This bug will be fixed soon...

TheNullinator

  • Guest
Re: Non-DDE IPC?
« Reply #11 on: March 30, 2006, 03:43:21 pm »
Well, we're having one issue after the other (the broken batch builds being the last in a long row) and somehow every problem seems to be related in some way to either DDE directly or a change which was necessary to make DDE work. Maybe that's just my impression, but it looks to me that way. :lol:

Also it was DDE that was causing projects to be loaded twice because Windows was sending the file on the command line and through DDE.  That was hidden away until the project file version was updated from v1.3 to v1.4.

However, ditching it may not be worth the effort.  But on the other hand, I'm all for working on different ways to do things (plenty of free time, ya'know ;) (or should that be :(? ;))).
« Last Edit: March 30, 2006, 03:47:58 pm by TheNullinator »

TheNullinator

  • Guest
Re: Non-DDE IPC?
« Reply #12 on: April 01, 2006, 05:27:39 pm »
I've just finished knocking together a WM_COPYDATA IPC system.  It runs great under Windows XP SP2 in unicode mode.  I'm just running the ANSI wxWidgets+Code::Blocks build now to test it on my Windows ME system in the morning.

Offline thomas

  • Administrator
  • Lives here!
  • *****
  • Posts: 3979
Re: Non-DDE IPC?
« Reply #13 on: April 04, 2006, 01:15:13 pm »
Another peculiarity appears in recent builds (since about 5-6 days) for me.

Header files are not opened any more. Neither when double-clicked, nor when run from the console. The IDE does start, so the file associations are good, but nothing happens. Source and resource files work just fine.
Although I don't know why this happens, I feel tempted to attribute this to the DDE workarounds, too ;)

Anyone else experiencing this?

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

Offline tiwag

  • Developer
  • Lives here!
  • *****
  • Posts: 1196
  • sailing away ...
    • tiwag.cb
Re: Non-DDE IPC?
« Reply #14 on: April 04, 2006, 03:07:19 pm »
i don't have this issue with svn rev 2285
my header files open fine for editing