Board index » delphi » Threads within COM object - handling Exceptions

Threads within COM object - handling Exceptions

This isn't regarding the apartment method of the COM object, it's multiple
threads within a single COM object - maybe not the correct way to do it ????

I have a Delphi COM object running under MTS which needs to search multiple
databases simultaneously.  After having problems with TThread I came accross
a TCriticalThread Class which is similar to TThread but uses critical
sections for synchronization.

COM Object creates an instance of a TCriticalSection and passes it into each
thread.  The thread performs its search and sends the results back to the
COM class within a Critical Section.

The searches work perfectly and the synchronisation of the three results
sets back into the COM object is fine.

The problem I'm having is that if an exception is raised in one of the
threads (such as an SQL error) I receive an exception in MTX.EXE which is
displayed on the server

"The Exception unknown software exception (0x0eedfade) occurred in the
application at location 0x77f1d479"

followed by the correct EOleException error (General SQL error, END_OF_QUERY
assumed etc) which is also displayed on the server.

I'm pretty sure that the problems are being caused by the threading, but why
and how can I fix this please - driving me mad..

Any ideas on threads within a COM object would be appreciated.
Martin

 

Re:Threads within COM object - handling Exceptions


Which problem are you trying to solve: the error which causes the exception, or
the reporting of the exception?  I have no idea abut the former (do you expect
to have exceptions raised sometimes?); if the latter, then I have to ask the
obvious: are your thread operations wrapped in a try/except block and exceptions
gracefully reported back to the COM object?

-Jim

Quote
Martin wrote:
> This isn't regarding the apartment method of the COM object, it's multiple
> threads within a single COM object - maybe not the correct way to do it ????

> I have a Delphi COM object running under MTS which needs to search multiple
> databases simultaneously.  After having problems with TThread I came accross
> a TCriticalThread Class which is similar to TThread but uses critical
> sections for synchronization.

> COM Object creates an instance of a TCriticalSection and passes it into each
> thread.  The thread performs its search and sends the results back to the
> COM class within a Critical Section.

> The searches work perfectly and the synchronisation of the three results
> sets back into the COM object is fine.

> The problem I'm having is that if an exception is raised in one of the
> threads (such as an SQL error) I receive an exception in MTX.EXE which is
> displayed on the server

> "The Exception unknown software exception (0x0eedfade) occurred in the
> application at location 0x77f1d479"

> followed by the correct EOleException error (General SQL error, END_OF_QUERY
> assumed etc) which is also displayed on the server.

> I'm pretty sure that the problems are being caused by the threading, but why
> and how can I fix this please - driving me mad..

> Any ideas on threads within a COM object would be appreciated.
> Martin

Re:Threads within COM object - handling Exceptions


I'm trying to solve the reporting of exceptions.  Any exception raised
within the threads is giving the same problem, even ones I explicitly
raise - MTX gives an access violation, the exception is raised and displayed
on the MTS server machine and the client gives an exception 'Not enough
storage is available to complete this operation'.  It's definately down to
the threading as I can raise exceptions outside the thread and it's
marshalled back OK to the client.

My COM object has a simple interface method (Load) which creates an instance
of TPerson (the business Object where the rules are).

Interface method then calls TPerson.Load passing an array of DB aliases and
the parameters for the SQL.

TPerson.Load creates 'x' number of threads, one for each DB Alias passing
the SQL to perform.  Each thread connects to the appropriate DB Alias,
performs the retrieveal and then within a critical section passes the
dataset contents back into TPerson where then are loaded into a combined
dataset.

As I say everything is working other than the exception handling from within
the threads.

Quote
Jim Green <jgre...@concentric.net> wrote in message

news:3B1AB627.58422DFB@concentric.net...
Quote
> Which problem are you trying to solve: the error which causes the
exception, or
> the reporting of the exception?  I have no idea abut the former (do you
expect
> to have exceptions raised sometimes?); if the latter, then I have to ask
the
> obvious: are your thread operations wrapped in a try/except block and
exceptions
> gracefully reported back to the COM object?

> -Jim

> Martin wrote:

> > This isn't regarding the apartment method of the COM object, it's
multiple
> > threads within a single COM object - maybe not the correct way to do it
????

> > I have a Delphi COM object running under MTS which needs to search
multiple
> > databases simultaneously.  After having problems with TThread I came
accross
> > a TCriticalThread Class which is similar to TThread but uses critical
> > sections for synchronization.

> > COM Object creates an instance of a TCriticalSection and passes it into
each
> > thread.  The thread performs its search and sends the results back to
the
> > COM class within a Critical Section.

> > The searches work perfectly and the synchronisation of the three results
> > sets back into the COM object is fine.

> > The problem I'm having is that if an exception is raised in one of the
> > threads (such as an SQL error) I receive an exception in MTX.EXE which
is
> > displayed on the server

> > "The Exception unknown software exception (0x0eedfade) occurred in the
> > application at location 0x77f1d479"

> > followed by the correct EOleException error (General SQL error,
END_OF_QUERY
> > assumed etc) which is also displayed on the server.

> > I'm pretty sure that the problems are being caused by the threading, but
why
> > and how can I fix this please - driving me mad..

> > Any ideas on threads within a COM object would be appreciated.
> > Martin

Re:Threads within COM object - handling Exceptions


I found the following information about threads in MTS. The main problems
are that your object can dissapear, while the worker thread is running, and
that you have to marshal interfaces that are passed between object and
thread.

-----------------------------------------
Creating threads in MTS

MTS does not prohibit creating threads. If you do create them, no errors
generated will be generated and no events posted to the event log. However,
if you must create threads then you should review your design.
In particular take a look at how you are using transactions. Transactions
have a finite lifetime, which determines how long locks are kept on
transactional data - and a lock means that no other client can access that
data. To ensure that data is kept available as much as possible, locks
should be applied for as short a time as possible, and hence transactions
should be kept short. For this reason MTS applies a timeout on each
transaction. Thich can be set on a per-machine basis and the default is 60
seconds.
The lifetime of the component is determined not only by the client that
holds the component reference, but also on MTS. First, you must ensure that
the component finishes its work within the transaction timeout, otherwise
the transaction will be aborted and the component will be deactivated.
Secondly, MTS uses just-in-time activation and as-soon-as-possible
deactivation, so usually when a component method completes the component
will be deactivated.

When a component is deactivated the client will still have a reference to
its wrapper object, but the actual component will be destroyed. So if a
worker thread tries to access the component, the call will throw an
exception. Since the component will depend on the thread's outcome
(otherwise why create one?) you have to ensure that the thread's lifetime
will not be longer than the transaction timeout, or the component's.
Another issue is object context. The worker thread will not have access to
the component's context for two reasons. First, the worker thread will be in
a new apartment, as it must call CoCreateInstanceEx() to use COM. Thus any
interface pointer that you pass to this thread must be marshalled, but
IObjectContext is declared as [local]. Secondly, the workder thread is in a
different apartment and therefore will not be in the component's activity.
Thus GetContextObject() will fail.
------------------------------------

Martijn Houtman

Quote
"Martin" <Mar...@savant.co.uk> wrote in message news:3b17b94a_2@dnews...
> This isn't regarding the apartment method of the COM object, it's multiple
> threads within a single COM object - maybe not the correct way to do it
????

> I have a Delphi COM object running under MTS which needs to search
multiple
> databases simultaneously.  After having problems with TThread I came
accross
> a TCriticalThread Class which is similar to TThread but uses critical
> sections for synchronization.

> COM Object creates an instance of a TCriticalSection and passes it into
each
> thread.  The thread performs its search and sends the results back to the
> COM class within a Critical Section.

> The searches work perfectly and the synchronisation of the three results
> sets back into the COM object is fine.

> The problem I'm having is that if an exception is raised in one of the
> threads (such as an SQL error) I receive an exception in MTX.EXE which is
> displayed on the server

> "The Exception unknown software exception (0x0eedfade) occurred in the
> application at location 0x77f1d479"

> followed by the correct EOleException error (General SQL error,
END_OF_QUERY
> assumed etc) which is also displayed on the server.

> I'm pretty sure that the problems are being caused by the threading, but
why
> and how can I fix this please - driving me mad..

> Any ideas on threads within a COM object would be appreciated.
> Martin

Re:Threads within COM object - handling Exceptions


Thanks.

Maybe I'll have to look at running the DB searches in series rather than in
simultaneous threads.  Shame because the actual DB retrievals worked a treat
and greatly reduced the response time, it's just the exceptions that were
causing me a problem.

Quote
"Martijn Houtman" <newsgr...@martijnhoutman.com> wrote in message

news:3b1bbaed_2@dnews...
Quote
> I found the following information about threads in MTS. The main problems
> are that your object can dissapear, while the worker thread is running,
and
> that you have to marshal interfaces that are passed between object and
> thread.

> -----------------------------------------
> Creating threads in MTS

> MTS does not prohibit creating threads. If you do create them, no errors
> generated will be generated and no events posted to the event log.
However,
> if you must create threads then you should review your design.
> In particular take a look at how you are using transactions. Transactions
> have a finite lifetime, which determines how long locks are kept on
> transactional data - and a lock means that no other client can access that
> data. To ensure that data is kept available as much as possible, locks
> should be applied for as short a time as possible, and hence transactions
> should be kept short. For this reason MTS applies a timeout on each
> transaction. Thich can be set on a per-machine basis and the default is 60
> seconds.
> The lifetime of the component is determined not only by the client that
> holds the component reference, but also on MTS. First, you must ensure
that
> the component finishes its work within the transaction timeout, otherwise
> the transaction will be aborted and the component will be deactivated.
> Secondly, MTS uses just-in-time activation and as-soon-as-possible
> deactivation, so usually when a component method completes the component
> will be deactivated.

> When a component is deactivated the client will still have a reference to
> its wrapper object, but the actual component will be destroyed. So if a
> worker thread tries to access the component, the call will throw an
> exception. Since the component will depend on the thread's outcome
> (otherwise why create one?) you have to ensure that the thread's lifetime
> will not be longer than the transaction timeout, or the component's.
> Another issue is object context. The worker thread will not have access to
> the component's context for two reasons. First, the worker thread will be
in
> a new apartment, as it must call CoCreateInstanceEx() to use COM. Thus any
> interface pointer that you pass to this thread must be marshalled, but
> IObjectContext is declared as [local]. Secondly, the workder thread is in
a
> different apartment and therefore will not be in the component's activity.
> Thus GetContextObject() will fail.

Re:Threads within COM object - handling Exceptions


Just an idea. Use a try except around your thread method and a critical
section in the except block to raise your error. I don't know if it works,
but you will know after testing it.

Martijn Houtman

Quote
"Martin" <m...@bigfoot.com> wrote in message news:3b1bd2e1_2@dnews...
> Thanks.

> Maybe I'll have to look at running the DB searches in series rather than
in
> simultaneous threads.  Shame because the actual DB retrievals worked a
treat
> and greatly reduced the response time, it's just the exceptions that were
> causing me a problem.

Re:Threads within COM object - handling Exceptions


Hi.  I already tried and it gave the same results.  I don't really
understand why because the results sets were returned to the main object Ok
within a citical section.  Anyway thanks, it did seem like a good idea.

This is a quick simulation of the problem which causes the same results in
MTS (NT4/2K) just in case anyone has a flash of inspiration.  No critical
section in this code as there's only 1 thread and nothing is being returned.
Also tried with  CoInitializeEx(Nil,COINIT_APARTMENTTHREADED); and
CoInitialize(Nil); in the execute method with CoUninitialize in the except
block.

type
  TMyThread = class(TThread)
  public
    procedure Execute(); override;
  end;

type
  TMTSTestIMPL = class(TMtsAutoObject, IMTSTestIMPL)
  protected
    procedure RunThread; safecall;
    { Protected declarations }

  private
    ThreadCount: integer;
    MyThread: TMyThread;
    ThreadDone: Boolean;
  public
    procedure ThreadTerminate(Sender:TObject);
  end;

implementation

uses ComServ;

procedure TMTSTestIMPL.RunThread;
begin

  MyThread := TMyThread.Create(True);

  MyThread.FreeOnTerminate := False;
  ThreadDone  := False;
  ThreadCount := 1;
  MyThread.OnTerminate := ThreadTerminate;
  MyThread.Resume;

  While (not ThreadDone) do sleep(100);

end;

procedure TMTSTestIMPL.ThreadTerminate(Sender: TObject);
begin
  ThreadCount := ThreadCount -1;
  if ThreadCount = 0 then begin
    ThreadDone := True;
  end;
end;

procedure TMyThread.Execute;
var
  i: integer;
begin
  try
    for i := 0 to 50 do begin
      Sleep(10);
      if (i = 25) then begin
        Raise Exception.Create('My Exception');
      end;
    end;
    Terminate;
    Exit;
  except
    Raise;
  end;
end;

initialization
  TAutoObjectFactory.Create(ComServer, TMTSTestIMPL, Class_MTSTestIMPL,
    ciMultiInstance, tmApartment);
end.

Re:Threads within COM object - handling Exceptions


Quote
Martin wrote:
> procedure TMyThread.Execute;
> var
>   i: integer;
> begin
>   try
>     for i := 0 to 50 do begin
>       Sleep(10);
>       if (i = 25) then begin
>         Raise Exception.Create('My Exception');
>       end;
>     end;
>     Terminate;
>     Exit;
>   except
>     Raise;
>   end;
> end;

Your except block is not really handling the exception, it's just re-raising
it.  Seems like you want something like:

try
  <execute Query>
  <return results to TPerson>
except
  <notify TPerson of error>
end;

-Jim

Re:Threads within COM object - handling Exceptions


Thanks.  Seems so obvious now.  I'm saving the exception Message in a public
property of the thread, Raising a notify event (within a critical section)
back to the ThreadManager which reads the property and raising the exception
in the ThreadManager when all threads have completed.  Now works a treat.

Martin

Quote
Jim Green <jgre...@concentric.net> wrote in message

news:3B1E5A7E.D7890D97@concentric.net...
Quote
> Martin wrote:

> > procedure TMyThread.Execute;
> > var
> >   i: integer;
> > begin
> >   try
> >     for i := 0 to 50 do begin
> >       Sleep(10);
> >       if (i = 25) then begin
> >         Raise Exception.Create('My Exception');
> >       end;
> >     end;
> >     Terminate;
> >     Exit;
> >   except
> >     Raise;
> >   end;
> > end;

> Your except block is not really handling the exception, it's just
re-raising
> it.  Seems like you want something like:

> try
>   <execute Query>
>   <return results to TPerson>
> except
>   <notify TPerson of error>
> end;

> -Jim

Other Threads