Delphi COM server processes one request at a time - multithreading

I have an out-of-process COM server originally developed in Delphi 7, then D2006, recently recompiled under XE3. Under the XE3 version, requests are processed one at a time. If one is in progress when another comes in, ObjectCount goes up, but the second isn't processed until the first finishes. The D7/D2006 versions processed the requests concurrently.
The server maintains a database. Most requests are processed very quickly, but a few take a minute or more. There is an form with a Memo that displays an activity log, that's the whole UI. Not sure what other information would be helpful here.
Any ideas why the XE3 version would behave differently? Or, in general, why the second server object might wait for the first to finish?
Here's an example. I installed XE5, created a VCL app, and added a COM object with one method, and created a test client. It processed one request at a time. I compiled the same project in D2006 (just had to remove the unit prefixes) and it processed them concurrently.
What am I doing wrong?
unit Unit2;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Windows, ActiveX, Classes, ComObj, Project1_TLB, StdVcl;
type
TTest = class(TTypedComObject, ITest)
protected
function Method1(N: Integer): HResult; stdcall;
end;
implementation
uses ComServ;
function TTest.Method1(N: Integer): HResult;
var I: Integer;
begin
for I := 1 to N do
Sleep( 1000);
end;
initialization
TTypedComObjectFactory.Create(ComServer, TTest, Class_Test,
ciMultiInstance, tmFree);
end.

Double check the threading model of each version of the COM object. It sounds like the XE3 version is Apartment-threaded (where COM serializes requests) while the earlier version is Multi/Free-threaded instead (where COM does not serialize requests).

I traced through the simple Delphi XE5 server above. Application.Initialize -> ComServ.InitComServer -> ComObj.InitComObj -> CoInitializeEx( nil, COINIT_MULTITHREADED), which returns RPC_E_CHANGED_MODE. The server processes one request at a time.
On tracing through the same project compiled under D2006, the CoInitializeEx call returns S_OK, and the server processes requests concurrently.
I added a call to CoUninitialize in the project source before the call to Application.Initialize. With that, the XE5 version processes requests concurrently. This worked for the production server that prompted the question, too.

Related

How to make the main (calling) thread wait for a child thread to complete execution?

Using: Delphi 10 Seattle, Win32 VCL forms application
I'm developing an updater application that checks for updates to one or more installed software applications, and when updates are found will download the updates in sequence. After each update is downloaded, it will install the update before proceeding to download the next update. The downloading bit is implemented as a thread class (descendant of TThread) and its constructor is as follows:
constructor TWebFileDownloaderThread.Create(CreateSuspended: Boolean; const AWebFileURL, ALocalFilePath: String;
ACallBackProc: TProgressCallback; AProxySetting: TProxySetting);
begin
inherited Create(CreateSuspended);
FWorkResult := False;
FWebFileURL := AWebFileURL;
FProxySetting := AProxySetting;
FLocalFilePath := ALocalFilePath;
FUpdateCallbackProc := ACallBackProc;
end;
The main thread creates and starts the downloader thread as follows:
procedure TfmMain.DownloadUpdateFromWeb(const AInstallerFileURL: String);
var
internet_file_download_thread: TWebFileDownloaderThread;
begin
internet_file_download_thread := TWebFileDownloaderThread.Create(True, AInstallerFileURL, FUpdateDownloadDir,
UpdateProgressCallback, FProxySetting);
internet_file_download_thread.OnTerminate := WebFileDownloaderThread_TerminatedMethod;
internet_file_download_thread.FreeOnTerminate := True;
internet_file_download_thread.Start;
end;
My specific question is: How to make the main (calling) UI thread wait until a downloader thread completes, before creating a new downloader thread to start the next download?
I believe that there is some form of queuing required, but not sure how to implement it. Your tips and advice are much appreciated.
If I understood it correctly, you have a list of URLs to download and install (obtained by previously performing a check for updates). You want to download updates from these URLs and install them one by one: download update 1, install update 1, download update 2, install update 2, etc.
Here's a possible design:
In your WebFileDownloaderThread_TerminatedMethod, start installing the just downloaded update (to keep the main thread responsive, do this in a separate thread).
In the OnTerminate handler of the installer thread, remove the just completed URL (or mark it as processed) and start downloading the next one, by calling DownloadUpdateFromWeb again, unless the list is already empty (or contains no more unprocessed items).
(BTW, the method DownloadUpdateFromWeb would better be named something like BeginDownloadUpdateFromWeb, to indicate its asynchronous nature.)
You should not block the main thread. So do not wait until the worker thread completes. Instead arrange for the worker thread to signal to the main thread when it has finished. For instance by:
Sending a message, or
Using TThread.Synchronize, or
Using TThread.Queue, or
Handling the OnTerminate event of the thread, or
Some other form of inter-thread communication.
The OnTerminate looks like a pretty good option to me.
You might also consider using a higher level parallel library. For instance the RTL's parallel library, or OTL. That way you can avoid getting tangled up in the details of threads, and let the parallel library deal with such matters.
Were you to do this you could design your application with a producer/consumer architecture:
Create a threadsafe queue.
Create one or more download threads, these are the consumers.
The main thread, the producer, pushes download jobs onto the queue.
The consumer threads wait on the queue and when it contains jobs, the consumer threads pull off jobs and process them, by downloading the file.
This sort of design is very simple to implement using a high level parallel library.

Delphi tIdTCPClient with timer events and other multi-threaded client side events

We have a Delphi client server application using INDY. The client has a single tIdTCPClient connection to the server which is multi threaded. The client is "theoretically" a single thread. But in practice there are multiple threads on the client and this is where my problem is. For example think of a timer that fires every minute to get data from the server. And consider what happens when a user runs a command at the same time as this timer event. In truth, my problem is caused by our "Report Builder" reporting tool that (annoyingly) insists on loading every page of a report, which takes a while. The report runs off our "special" dataset that has a caching mechanism to transmit batches of records at a time (so multiple calls to the server to get all data). Meanwhile if a user does something else at the same time we seem to be getting crossed data. It seems the user getting back data that was meant for the report.
By the way, this bug is extremely rare, but a lot less rare for one particular customer who has the worlds slowest internet (luck me - I now have a test environment).
So on the client I have code a bit like this...
procedure DoCommand(MyIdTCPClient:tIdTCPClient; var DATA:tMemoryStream);
var
Buffer: TBytes;
DataSize: Integer;
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA.Size);
MyIdTCPClient.IOHandler.Write(RawToBytes(Data.Memory^,DataSize));
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
SetLength(Buffer,0); //Clear out buffer
MyIdTCPClient.IOHandler.ReadBytes(Buffer,6);
CommsVerTest:=BytesToString(Buffer);
if CommsVerTest<>'ABC123' then
raise exception.create('Invalid Comms'); //It bugs out here in rare cases
//Get Result Data Back from Server
DataSize:=MyIdTCPClient.IOHandler.ReadLongInt;
Data.SetSize(DataSize); //Report thread is stuck here
MyIdTCPClient.IOHandler.ReadBytes(Buffer,DataSize);
end;
Now when I debug it, I can confirm it bugs out when there are two threads in the middle of this procedure. The main thread stops at the exception. And the report thread is stuck somewhere else in the same procedure.
So, it looks to me like I need to make the procedure above thread safe.
I mean so if the user wants to do something they just have to wait until the report thread finishes.
Arrrgh, I thought my client application was single threaded for sending data to the server!
I think that using TThread would not work - because I don't have access to the thread inside Report Builder. I think I need a tCriticalSection.
I think I need to make the application so that the above procedure can only be run by one thread at a time. Other threads have to wait.
Someone please help with the syntax.
TIdIOHandler has Write() and Read...() overloads for sending/receiving TStream data:
procedure Write(AStream: TStream; ASize: TIdStreamSize = 0; AWriteByteCount: Boolean = False); overload; virtual;
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;
You do not need to copy the TMemoryStream contents to an intermediate TIdBytes before sending it, or receive it as TIdBytes before copying it back to the TStream. In fact, there is nothing in in the code you have shown that needs to use TIdBytes directly at all:
procedure DoCommand(MyIdTCPClient: TIdTCPClient; var DATA: TMemoryStream);
var
CommsVerTest: String;
begin
//Write Data
MyIdTCPClient.IOHandler.Write(DATA, 0, True);
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
CommsVerTest := MyIdTCPClient.IOHandler.ReadString(6);
if CommsVerTest <> 'ABC123' then
raise exception.create('Invalid Comms');
//Get Result Data Back from Server
DATA.Clear;
MyIdTCPClient.IOHandler.ReadStream(DATA, -1, False);
end;
With that said, if you have multiple threads writing to the same socket at the same time, or multiple threads reading from the same socket at the same time, they will corrupt each other's data (or worse). You need to synchronize access to the socket, such as with a critical section at a minimum. Because of your multi-threaded use of TIdTCPClient, you really need to re-think your overall client design.
At the very least, using your existing logic, when you need to send a command and read a response, stop the timer and wait for any pending data to be exchanged before then sending the command, and do not allow anything else to access the socket until the response comes back. You are trying to do too much at one time without synchronizing everything to avoid overlaps.
In the long run, it would be much safer to do all of the reading from a single dedicated thread and then pass any received data to your other threads for processing as needed. But that also means changing your sending logic to match. You could either:
If your protocol allows you to have multiple commands in flight in parallel, then you can send a command from any thread at any time (just be sure to use a critical section to avoid overlaps), but do not wait for a response immediately. Let each sending thread move on and do other things, and have the reading thread notify the appropriate sending thread asynchronously when the expected response actually arrives.
If the protocol does not allow for parallel commands, but you still need each sending thread to wait for its respective response, then give the socket thread a thread-safe queue that other threads can push commands into when needed. The socket thread can then run through that queue periodically sending each command and receiving its response one at a time as needed. Each thread that puts a command into the queue can include a TEvent to be signaled when the response arrives, that way they enter efficient sleep states while waiting, but you preserve your per-thread waiting logic.
Thanks Remy.
The TCriticalSection solved the problem. I have no control over things like the 3rd party report builder. And running reports entirely in their own thread wouldn't make much difference - they still need to share the same connection (I don't want or need parallel connections). Anyway the bulk of the program runs in the main thread, and it is rare that two threads need to communicate with the server at the same time.
So TCriticalSection was perfect - it prevented this procedure running twice at the same time (ie one thread has to wait until the first is finished). And happily - it worked brilliantly.
Basically the code now looks like this:
procedure DoCommand(
CS:tCriticalSection;
MyIdTCPClient:tIdTCPClient;
var DATA:tMemoryStream);
var
Buffer: TBytes;
DataSize: Integer;
CommsVerTest: String;
begin
CS.Enter; //enter Critical Section
try
//Write Data
MyIdTCPClient.IOHandler.Write(DATA.Size);
MyIdTCPClient.IOHandler.Write(RawToBytes(Data.Memory^,DataSize));
//Read back 6 bytes CommsVerTest should always be the same (ie ABC123)
SetLength(Buffer,0); //Clear out buffer
MyIdTCPClient.IOHandler.ReadBytes(Buffer,6);
CommsVerTest:=BytesToString(Buffer);
if CommsVerTest<>'ABC123' then
raise exception.create('Invalid Comms');
//Get Result Data Back from Server
DataSize:=MyIdTCPClient.IOHandler.ReadLongInt;
Data.SetSize(DataSize);
MyIdTCPClient.IOHandler.ReadBytes(Buffer,DataSize);
finally
cs.Leave;
end;
end;

TThread.Synchronize causing (near) deadlock in Delphi 2009 (worked in Delphi 7)

In Delphi 2009, synchronize'd functions that worked fine in Delphi 7 execute with sublime slowness until you wiggle the mouse over the open form. Wiggling the mouse causes the program to go into some mouse idle state which does CheckSynchronize(). CheckSynchronize() appears to be called less frequently in Delphi 2009 than in Delphi 7, but we can't figure out why or where.
Placing this code:
procedure TMyForm.FormCreate(Sender : TObject)
Classes.WakeMainThread := WakeMainThread;
end;
procedure TMyForm.WakeMainThread(Sender: TObject);
begin
SendMessage(Application.MainForm.Handle, WM_NULL, 0, 0);
end;
Speeds things along at the normal rate. But I get: EAccessViolation in module rtl120.bpl when the thread dies if it's used in a modal app (works fine for a simple dialog utility). I'm guessing 'Classes.WakeMainThread()' is being called just as much as it is in Delphi 7, but sending WM_NULL to application.handle isn't accomplishing anything. I guess it's time to 'get steppin'.
The internals of the Synchronize() mechanism have not changed much between D7 and D2009. Sure, there have been new features added (asynchronous queuing, anonymous methods, etc), but the core implementation to run code in the main thread has not changed. What is more likely happening is something else in your main thread code that you have not shown yet is blocking the main thread from processing pending messages and Synchronize() requests correctly.
TApplication.Create is getting called from another DLL, hence it is waking an invalid handle or some nonsense in that callback.
You need to eliminate statically linked DLLs that include [vcl.]controls.pas because TApplication.Create happens in some initialization code in that unit.
Once you do this, synchronize will be restored to it's former glory.
Unfortunately, fixes you make in one version of Delphi might be undone by changes made in another version of Delphi. So if the problem comes back, go back to the drawing board. Step through the initialization code, specifically the initUnits procedure in system.pas. It runs the initialization code and will bump into vcl.controls.pas eventually and you can peer into the UnitInfo record to find out which file this is being called from.
The best way to fix this is to use the delayed with all your external dlls (at least all your Delphi VCL external DLLs).
function didntknowIusedcontrolsbutIdo() : Integer; external 'useful.dll' delayed;
But this only works in Delphi 2010 and up. Good thing you upgraded to XE2 between the time you asked this question and the time you finally find a satisfying answer to it.

how to write and execute a thread

I have never worked with threads before, and now I'm trying to create a thread with a query to check database status. The query is as follows:
select (*) as DBCount from v$datafile where status in 'OFFLINE';.
This query returns the total number of all the databases that are offline. Now I want to create a thread in Delphi that will execute this query in the background of my application when I run it and display the results on a label.
Multithreading is hard. You may be better off using a simple threading framework like AsyncCalls.
Following StackOverlow threads may give you more information on how to solve your problem:
Delphi - Threading frameworks
How Do I Choose Between the Various Ways to do Threading in Delphi?
Delphi thread that waits for data, processes it, then resumes waiting
This is fairly easy to do using AsyncCalls. Let's assume your non-threaded code looks like this (ignoring all error handling):
Query := 'select (*) as DBCount from...';
ExecuteSQL(SqlConnection,Query);
SqlResult := GetSqlResult(SqlConnection);
SqlRow := GetSqlRow(SqlResult);
MyLabel.Text := SqlRow[0];
...Go on to do other things...
Where the second line is blocking (waiting for the server to reply). Your new code would look like this:
uses AsyncCalls; //added to your existing uses statement
...
procedure DoesSomething();
var Thread: TAsyncCall; //your interface to AsyncCalls
procedure AsyncSqlCall(); //this is a LOCAL procedure
Query := 'select (*) as DBCount from...';
ExecuteSQL(SqlConnection,Query);
SqlResult := GetSqlResult(SqlConnection);
SqlRow := GetSqlRow(SqlResult);
EnterMainThread;
try
Assert(GetCurrentThreadId = MainThreadId);
MyLabel.Text := SqlRow[0];
finally
LeaveMainThread;
end;
begin //this begins proc DoSomething()
...
Thread := LocalAsyncCall(#AsyncSqlCall);
...Go on to do other things...
end;
All we've done is put the blocking SQL calls in a local proc and told AsyncCalls to execute it in another thread, while the main thread continues executing. The only tricky part was using the VCL, which is not thread-safe. So I had that line safely run in the main thread.
If at some point you need to be sure the Async thread has completed, you would execute this line to block the main thread till AsyncSqlCall terminates:
Thread.sync;
The really nice thing here is that AsyncCalls handles all the stuff about creating a thread pool, creating threads, etc. Though not shown in this example, you can pass variables to your thread, and return a value. You don't have to use a local proc, but doing so gives it access to all local vars. You could make all this global, and then launch the Async thread in one routine, and test for its completion in another.
Restrictions:
Your Async thread must not touch (read or write) anything but its own variables, and your main thread must not touch them while the Async thread is running. YOU must code it that way. Nothing will stop you from creating total chaos. In the above example, your main thread must not touch Query, SqlConnection, SqlResult, and SqlRow. If any part of your code used one of those vars before the Thread.sync call, your code would work -- but throw exceptions in weird places you never expected. So keep it simple.
Your Async thread must not use the VCL. The sample above shows one of several ways to safely get around this limitation.
Finally:
AsyncCalls is not a full Multi-Threading framework. It's just a way of calling procs & functions asynchronously (i.e. without waiting). Don't try to push it too far -- by which I mean, don't try to make it the basis of a fully multitasking program.
In Delphi there is TThread class. You can create such thread using simple wizard from Delphi IDE (in Turbo Delphi: File->New->Delphi Projects->Delphi Files->Thread Obcjet). In its constructor create connection to database, and in Execute method you can create loop that query database, update some global variable (NOT declared as threadvar) and sleep some time.
Here you can find a discussion about using threads on DataBase.
Simple Thread Sample Delphi
There are some code that can be usefull for you.
Regards.

Delphi - Help calling threaded dll function from another thread

I'm using Delphi 2006 and have a bit of a problem with an application I'm developing.
I have a form that creates a thread which calls a function that performs a lengthy operation, lets call it LengthyProcess. Inside the LengthyProcess function we also call several Dll functions which also create threads of their own.
The problem that I am having is that if I don't use the Synchronize function of my thread to call LengthyProcess the thread stops responding (the main thread is still responding fine). I don't want to use Synchronize because that means the main thread is waiting for LengthyProcess to finish and therefore defeats the purpose of creating a separate thread.
I have tracked the problem down to a function inside the dll that creates a thread and then calls WaitFor, this is all done using TThread by the way. WaitFor checks to see if the CurrentThreadID is equal to the MainThreadID and if it is then it will call CheckSychronization, and all is fine. So if we use Synchronize then the CurrentThreadID will equal the MainThreadID however if we do not use Synchronize then of course CurrentThreadID <> MainThreadID, and when this happens WaitFor tells the current thread (the thread I created) to wait for the thread created by the DLL and so CheckSynchronization never gets called and my thread ends up waiting forever for the thread created in the dll.
I hope this makes sense, sorry I don't know any better way to explain it. Has anyone else had this issue and knows how to solve it please?
If your secondary thread "stops responding," then I assume it has a message pump. (Otherwise, you need to explain what it stops responding to.) You appear to also wish for the thread to be able to detect when the tertiary thread finishes running. (The "primary" thread here is the VCL thread, which isn't involved at all.)
You tried using WaitFor, but were disappointed when you discovered that it blocks. That's what it has always been designed to do, though. Its behavior in the main thread is where it gets weird, so it's safe to call from the VCL thread even though it was never really meant to be used that way originally.
To process messages and wait for threads to finish running, you need to use one or more of the wait functions from the Windows API. Start with MsgWaitForMultipleObjects. It can wait for various types of kernel handles, including thread handles, but also notify you when messages are available. The idea is that you'll call that function in a loop. When it says messages are available, handle them, and then loop again to continue waiting.
The following is just an outline. You'll want to check the documentation for all the API functions used, and combine that with the rest of the knowledge you have about your own threads.
procedure TSecondaryThread.Execute;
var
ret: DWord;
ThreadHandle: THandle;
Msg: TMsg;
begin
ThreadHandle := TertiaryThread.Handle;
repeat
ret := MsgWaitForMultipleObjects(1, ThreadHandle, False, Infinite, qs_AllEvents);
case ret of
Wait_Object_0: begin
// The thread terminated. Do something about it.
CloseHandle(ThreadHandle);
PostQuitMessage(0);
// Put *something* in the parameter so further calls to MWFMO
// will have a valid handle. May as well use a handle to something
// that will never become signaled so all we'll get are more
// messages. I'm pretty sure you can't pass an empty array of
// handles; there must be at least one, and it must be valid.
ThreadHandle := Self.Handle;
end;
Wait_Object_0 + 1: begin
// At least one message is available. Handle *all* of
// them before calling MsgWaitForMultipleObjects again
while PeekMessage(Msg, 0, 0, 0, pm_Remove) do
case Msg.Message of
wm_Quit: begin
// Do something about terminating the tertiary thread.
// Then stop the message loop and the waiting loop.
Exit;
end;
else begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
end;
Wait_Timeout: Assert(False, 'Infinity has passed');
Wait_Failed: RaiseLastOSError;
else Assert(False, 'Unexpected return value');
end;
until False;
end;
The part about handling all the messages is important. As soon as you call GetMessage, PeekMessage, or WaitMessage, the OS marks all messages in the queue as "old," but MsgWaitForMultipleObjects will only return when there is a "new" message on the queue — one that arrived after the last call to PeekMessage.
HI, Thanks for your reply, yes i realize that my question isn't very clear and somewhat confusing; so i'll try to clarify things a bit, here goes..
All of the threads described below are derived from TThread.
I have a form which starts a thread but does not wait for it. The thread started by the form calls a function that performs a long task.
The function calls another function in a DLL, the function in the DLL starts a thread and waits for it. The thread started by the DLL function calls another function via synchronize.
Form->Starts a thread but does not wait->The thread calls a functions->The function calls a DLL function->The Dll function starts a thread and waits->The thread started by the DLL function calls another function via synchronize i.e Synchronize(UpdateRecords).
The problem is that the call to synchronize never returns because, from what i can see, it has entered some sort of dead lock.
How synchronize works: Synchronize puts the method call into a queue and sets an event, Synchronize then waits for the event to become signaled. When the main thread is idle it will process the method calls that are waiting in the queue, after it has processed a method call it will signal the associated event so that the thread that initiated the synchronization can continue on.
The thread that was started by the form does not use synchronize to call the function that performs the long task, if it does use synchronize then the application does not dead lock, but this defeats the purpose of use a thread for the long process.
I've tracked down the problem, it seems to be that the TApplication object created by the dll is not processing messages and has a handle of 0, how this happened I don't know (I didn't write the DLL, it was written by someone else), but it is a cause of the problem because it will never process the method called queued by synchronize.
I mentioned earlier that if i call the function that performs the long process from my thread using synchronize then the application does not dead lock. This is because the main thread will be responsible for calling the function that performs the long process. So the long process function calls a DLL function which starts another thread and then calls WaitFor. WaitFor checks to see if the current thread is the main thread, and if it is, it processes method calls which have been queued by synchronize, continuously in a loop until the thread the thread that it is waiting for is released (i.e. the method it queued via synchronize gets called and the wait event is signaled).
In WaitFor, if the current thread is not the main thread then WaitFor simply blocks until the thread it is waiting for is released.
Anyway i can't do anything about the application object in the dll because that dll is quite complex and used by a larger system. I guess i can expose a method in the dll which can process the methods in the synchronization queue, i can then call this method from my application while its idle.
Anyway again thanks for your help but i've solved this problem now.
Using the TThread class or even the Application object in a Delphi DLL is extremely unsafe. The RTL and VCL core classes, globals and singleton objects are designed to exist once per process and can't handle the namespace duplication and incomplete initialization in a stadard DLL library.
You may get around it by building with runtime packages (RTL & VCL are enough; you may also build your own with just the system units you reallt need), in the EXE and all DLLs referencing the runtime units (Forms and Classes, especially) - they get single shared namespace and the full EXE initialization sequence this way.
If you can't modify the DLL at all, you may try to set it's Application.Handle, MainThreadID, SyncEvent and WakeMainThread to the corresponding values in the main EXE module - this may work, but it's just as ugly as it looks like and it doesn't cover all edge-cases (the classes and important globals will still be duplicated).

Resources