14
votes

I have two applications: X and Y.
X is the main application and it handles a lot of XML files. It has a history of more than 10 years and half a dozen techniques have been used to store, process and handle these XML files.
Y is a debugging tool that I am developing, which can process and display XML files in a more human-readable form. Basically, it just has a collection of stylesheets that will detect the XML format and if it recognizes the format, it will transform the XML to HTML which is displayed in a TWebBrowser component.

Problem:
When Y is active, I want X to send any XML it proceses to Y for displaying purposes. But only when Y is running! If Y is not running, X just won't do anything.
The detection of Y needs to be done on any moment and needs to be fast. I've considered using TCP/IP communication but the delay caused by a missing Y is just too long. Especially since a lot of XML is processed sometimes. Same problem with named pipes and similar network-based solutions. I need to quickly determine if Y is running and available and if so, send the XML fast and then continue X.
I've also considered to make Y a COM-based application or maybe add a COM-based DLL with events that would allow the inter-process communication. The DLL solution would be interesting since it would expose a method to X to upload an XML file, then send an event to Y to process the XML. This seems to be the best option although I would also need to check if the DLL is registered or not. If not, then X can't even call it!
The application X will also be used by customers who won't receive Y or the additional DLL so in most cases, the DLL will not be registered. (As I said, it's meant to help during debugging...)

But maybe there are other options? TCP/IP is too slow, COM is a bit too complex.


X and Y will be running on the same system. Or just X will be on a system and Y is missing completely.


About using memory-mapped files... While practical, I need to keep in mind that most of the times, Y won't be running thus the MMF will waste memory. XML data can be up to 4 MB in size within X thus having multiple blocks of this size in-memory is a bit overkill. It can be used to send status messages between X and Y but memory is sometimes a bit of a problem with application X. And while a MMF can be connected to a physical file, I'm trying to avoid writing any temporary files altogether.
It's a good solution, but I fear not good enough.


Some additional explanations are in order, I think. Application X is an application which will be used for several hours, with users doing a lot of actions that translate to lots of XML data that gets processed. Application X is a desktop application that communicates with several web applications (REST), web services (SOAP) and other applications and most of this is through XML.
Application Y is just meant to peek inside the processes that X is running. Basically, X is working for 20 minutes and Y pops up. From that moment on, X should start sending XML to Y until Y disappears again or until X is terminated. In most cases, Y will just be running to capture just a small portion of the running tasks and it's possibly even started multiple times. But I might be thinking about the whole thing in the wrong direction. Maybe X should be a server with Y registering to it... It's not a real problem when Y can't find X. But X not finding Y cannot result in delays or other problems...

9

9 Answers

11
votes

Have a look at my IPC at:

http://www.cromis.net/blog/downloads/cromis-ipc/

It is fast, free and has a setable timeout, so you can set it to a very small amount (50ms for example). Because it is very fast (typical message cycle request -> process -> response takes less than 1ms, around 0.1ms) you can have very small timeouts. It has client server build into it, so many clients are no problem. It runs threaded with task pool behind so it does not freeze your program and it has very flexible data packets to ease writing / reading the data.

As already said you can even check with other means if the debugger is running.

  • Check for process
  • Check for main window of the process
  • Use a Mutex
  • ...
6
votes

You could have X write its output to a memory-mapped file - Y can retrieve the data if it is running. This way X does not care whether or not Y is up.

X could write some kind of control info at a known location (eg. store the offsets of last 1000 written XMLs starting at offset 0 in the mapped file) and use the rest of the file as a circular buffer for the raw data.

If you need Y to be the determining factor for actions in X, have Y create the mapped file, and then use its presence/absence as a check on the data production on the X side of the 'channel'. There is example code for creator and second user here.

6
votes

You can do it more simply, since you're just trying to find out if one app is running from another one. As long as they're running on the same machine by the same user, you can have X simply use FindWindow() to see if Y is currently running. Just make sure you give Y a meaningful name (in the sample below, it's TXMLFormatterForm):

var
  XMLWindow: HWnd;
begin
  XMLWindow := FindWindow('TXMLFormatterForm', nil);
  if XMLWindow > 0 then
    // Y is running
end;

You can also use Y's window caption (title), as long as you're sure it's distinct:

XMLWindow := FindWindow(nil, 'Workshop Alex's XML Formatter');
4
votes

Named pipes are fast, because they are based on memory mapped files, for their implementation. What could be slow is the timeout in case of server failure...

If you need to have fast response on the same computer, why don't you use good old GDI messages?

You can use these messages, even with no User Interface or visual form, in plain console or background service applications (in case of a service application, the security settings must specify that this service must interact with the desktop, that is, must be able to receive and send messages).

The trick is to handle WM_COPYDATA messages. See for example our TSQLRestClientURIMessage class, and the ExportServerMessage/AnswerToMessage methods of TSQLRestServer, as implemented in http://synopse.info/fossil/finfo?name=SQLite3/SQLite3Commons.pas

In practice, I found out that GDI messages are much faster than named pipes for small amount of data (up to 64 KB or such per message).

You have alternatives in Looking for an alternative to windows messages used in inter-process communication

2
votes

Here are some real data about speed, according to diverse client/server investigation.

All benchmarks were run locally on one computer. You can achieve more than 15000 queries per second on direct access, 4300 queries per second on HTTP/1.1 remote access. This was benchmarked on a Notebook using a Centrino2 CPU, with AntiVirus ON.

2.5. Client server access: 
  - Http client keep alive: 3001 assertions passed
     first in 7.87ms, done in 153.37ms i.e. 6520/s, average 153us
  - Http client multi connect: 3001 assertions passed
     first in 151us, done in 305.98ms i.e. 3268/s, average 305us
  - Named pipe access: 3003 assertions passed
     first in 78.67ms, done in 187.15ms i.e. 5343/s, average 187us
  - Local window messages: 3002 assertions passed
     first in 148us, done in 112.90ms i.e. 8857/s, average 112us
  - Direct in process access: 3001 assertions passed
     first in 44us, done in 41.69ms i.e. 23981/s, average 41us
  Total failed: 0 / 15014  - Client server access PASSED

This benchmark tests both client and server speed, and is not multithreaded (even if our framework is multi-thread safe).

So you can guess that, for a 4 KB data block of JSON content for each request:

  1. The direct access (like your dll approach) is the fastest, and less resource consuming.
  2. Then GDI messages.
  3. Then named pipes.
  4. Then FastCGI and HTTP (depending on your web server for FastCGI, the HTTP classes are very low consuming).

Keep alive connections are HTTP/1.1 connections, and multi connect is plain HTTP/1.0, with a new connection for every request. Multi-connect is not so bad, because from the server point of view, we used an efficient Thread Pool based on I/O completion ports.

See http://synopse.info/forum/viewtopic.php?id=90

1
votes

Use memory-mapped files. They are great for your particular tasks. And our MsgConnect with it's MMF transport will come to rescue if you don't have time or intention to implement custom scheme.

1
votes

I would go the memory mapped files direction, but I would not implement directly in the code path..instead I would go through an intermediate object that would perform the writing to the memory mapped file, this way it could be replaced with one that just discarded the data if it was not in place.

When the program first starts (or is told to check via a configuration change) the system would either create the stub "do nothing" or the "log via memory mapped files" object. This also gives you the capability of adding a later debugger when the need arises...such as a UDP logger for network logging, etc.

You can use the "FindWindow" call to see if your debugger is running.

0
votes

If you are willing to use temporary file as transport, then it will be easy

  1. Both programs register a same custom message on start up MsgAppNotifyID := RegisterWindowMessage(......);

  2. Program Y must have codes to handle the custom message using SetWindowLong or anything equivalent

  3. Program X, using FindWindow to see if program Y is running or not

  4. Program X, If Y is running, Create a temp file on known location/directory of both end using consistent file name such as XXYY1234 (XXYYn) format where 1234=n

  5. Program X, use BroadcastSystemMessage to signal program Y NotifyRecipients := BSM_APPLICATIONS; BroadcastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE or BSF_FORCEIFHUNG, @NotifyRecipients, MsgAppNotifyID, any custom value, n);

  6. Program y, process above message using WParam as n and reconstruct the file for process

Cheers Good luck

0
votes

running a TCP server which just on a connect (as a trigger) is really fast and should do what you want