Solved

Best way to write a component to que objects

Posted on 2001-07-10
36
277 Views
Last Modified: 2010-04-06
I am starting to design a component which will send SMS messages. The way I would like to design this is to have a component that created a que that contains objects. Each object should be independant once it is created, sending the initial message then receiving status messages about the message. It should also destroy itself when the message has reached it's destination or when a timeout has expired. Each time an event occurs within a que object (e.g. message timed out, message sent successfully etc.), the parent component should be informed and should fire an Event.

I have some ideas about how to do this, but am a little confused as to how I pass the messages status back to the parent component.

Any ideas will be gratefully received along with 300 points to the best suggestion :) Examples would be nice too...
0
Comment
Question by:thornton_paul
  • 15
  • 10
  • 8
  • +3
36 Comments
 
LVL 13

Expert Comment

by:Epsylon
ID: 6269409
Have you checked TObjectQueue?
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6269430
and TObjectList and TObjectStack...

E.g. you can store objects in TObjectList. If you need to store different objects in the list then derive then all from an abstract base class with a virtual method that processes that object.
0
 
LVL 2

Expert Comment

by:egono
ID: 6269988
listening ...
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6271162

Hello Paul,

I may have the wrong end of the stick but here goes...
If you need the child objects to know about their owner ie: the queue then pass a reference to the owning queue in the constructor of the child object. eg :

constructor TSMSMessage.Create(AOwner:TMessageQueue);
begin
  // nb class names are examples
  inherited;
  FOwner := AOwner;
end;

procedure TSMSMessage.TimedOut;
begin
  // example of child object notifying its parent
  FOwner.MessageTimeOut(Self);

end;

// example of procedure type for custom event handler

TSMSEvent = procedure(Sender:TObject;Message:TSMSMessage) of object;

TMessageQueue = class
private
  FOnmessageTimeOut : TSMSEvent;
published
  property OnMessageTimeOut : TSMSEvent read FOnMessageTimeOut write FOnMessageTimeOut;
end;

procedure TMessageQueue.MessageTimeOut(Message:TSMSMessage);
begin
  // example of raising event
  if assigned(FOnMessageTimeOut) then
    FOnMessageTimeOut(Self,Message);

end;


Then the owning queue can be referenced whenever you like. If you decide to do this then you will need to ensure that both TSMSMessage and TMessageQueue are declared in the same unit as they both need to know about each other in their interface sections.

Regards

Jo
0
 

Author Comment

by:thornton_paul
ID: 6272126
Hi Jo,

The way you suggested is the way I have written it at the moment. The problem I face is that I don't want to expose an event such as FParent.MessageTimeOut to the user. How can I make it available to the Que Object, but not to the user?

Cheers,
Paul
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6272217
Hi Paul,

Private and protected methods are visible between classes declared in the same unit. So if you put classes in the same unit they will be able to call each others private methods, but code in any other unit will not.

Regards  

Jo
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6272491
> It should also destroy itself when the message has reached it's destination or when a timeout has expired.

Note that an object can't destroy itself and remove itself from a list. So you can't use an event to do that. What you can do is sending a custom message to the main form to signal it to free the object, or something like that.
0
 

Author Comment

by:thornton_paul
ID: 6272526
Can it not remove itself from the list and then destroy itself?
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6272563

If the object can reference it's owning queue then it can pass itself as a reference to a procedure in the queue object. If the list is a TObjectList then simply deleting the object's index will cause it to be freed also.

eg :


procedure TMessageQueue.MessageTimeOut(Message:TSMSMessage);
var
  intIndex:integer;
begin
  // FMessages is assumed to be a TObjectList that contains a list of TSMSMessage objects
 
  intIndex := FMessages.IndexOf(Message);
  FMessages.Delete(intIndex);

end;
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6272565
Well removing from the list can be done, but an object can not destroy itself. There's on exception: TThread. It has a FreeOnTerminate property.
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6272580
My previous example should had read

 if intIndex > -1 then
   Fmessages.Delete(intIndex);

Jo
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6272615
Read the warning in the help on TObject.Free.
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6272648
That's strange. I went through the source code of TObjectList to see what happen with the OwnsObjects parameter of TObjectList.Create. It copies the boolean to FOwnsObjects but then does absolutely nothing with it. Am I missing something? Weird.
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6272672

procedure TObjectList.Notify(Ptr: Pointer; Action: TListNotification);
begin
  if OwnsObjects then
    if Action = lnDeleted then
      TObject(Ptr).Free;
  inherited Notify(Ptr, Action);
end;
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6272724
I see  |o)

So this means that an object can't remove itself from the list. Correct me if I'm wrong.
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6272804

Thats right. But the solution I proposed was for the child object to notify the parent queue (passing itself as a paramter) and the parent queue would remove it from the list.
0
 

Author Comment

by:thornton_paul
ID: 6272854
Okay guys, I'm working on the code now... be patient and I may be back with a few more questions if that's OK...
0
 

Author Comment

by:thornton_paul
ID: 6272964
OK, I've got most of the queing working. The only problem now is removing the message from the que...

Jo, could you elaborate a little on the following that you posted earlier. I'm not familiar with Notify.

procedure TObjectList.Notify(Ptr: Pointer; Action: TListNotification);
begin
 if OwnsObjects then
   if Action = lnDeleted then
     TObject(Ptr).Free;
 inherited Notify(Ptr, Action);
end;

Cheers
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 

Expert Comment

by:felonious
ID: 6274728
paul, i think Jo was directing that comment to Epsylon (Epsylon couldn't figure out what TObjectList did with OwnsObjects).. i might be wrong though.


felonious
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6274764
Correct. I was tracing back the code to see if an object in TObjectList can free itself...
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6274797
Sorry Paul,

That piece of code was from the vcl source for Epsylon.

Jo
0
 

Author Comment

by:thornton_paul
ID: 6276078
My fault, too much coding! My head's spinning :) I'll get back soon with more comments.
0
 

Expert Comment

by:lsae
ID: 6287612
listening...
0
 

Author Comment

by:thornton_paul
ID: 6288733
OK, done it... the solution I decided on was to have 2 ques in the que object. The first que hold objects which are in the process of being sent, the second hold the ones that have finished and are waiting to be destroyed. A message object informs the que object when it has completed.

This method seems to work quite nicely.

I guess Jo should get the points, but maybe someone out there (Jo?) could give me some idea about creating the actual message objects as threads. I had a little play with this, but ended up in a right mess :)

/Paul
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6289911

I'll have a think about and post later (I'm at work at the moment). How many message objects will there be? I can't remember what the recommended max is for threads but it's certainly not high (maybe 16 to 24). If you can post anymore info about what you had in mind it would help.

Jo
0
 

Author Comment

by:thornton_paul
ID: 6289960
There should never be more that about 10.

When you send an SMS message, it is possible to send another before you have received the result of the first. The first SMS message object must keep track of incoming information about the SMS message and update the user. When the message has been delivered, the thread can be killed.

Is this clear? It's kind of difficult to explain!
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6290954
Hmmm, I'm still a bit vague. Any change you could post some code?
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6293074
Do you just need some advice on writing threads?
0
 

Author Comment

by:thornton_paul
ID: 6293101
I would love advice on writing threads :) Whenever I try it, I always end up in a mass of exceptions! All I want to do is create each message in it's own thread. That way, any waiting done in the message thread (e.g. waiting for incoming information) will not affect the parent thread. That's my idea anyway.
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6294730

ok. I'll post something later...
0
 
LVL 3

Accepted Solution

by:
nnbbb09 earned 300 total points
ID: 6295526

I don't know how much you've done with threads but I'll start with the basics.

All threads inherit from abstract class TThread. What you need to do is override and implement the Execute method. If you plan to update any resources in the main VCL thread then you need to use the Synchronize procedure.
Remember to check the Terminated property in any procedures called from the Execute procedure. If its true then exit the procedure.


This is a quick listing for a form unit that contains a button and a label. It has a simple queue component and a message thread class. Hopefully it should demonstrate the basics of writing a thread.

Good luck

Jo

{======================================================}

unit frmthread;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls,contnrs;

type

  TSMSQueue=class(TComponent)
  private
    FList:TObjectlist;
    FintMessageCount: integer;
    FOnMessage: TNotifyEvent;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;
    procedure MessageComplete(Sender:TObject);
    procedure SendMessage;
    property MessageCount : integer read FintMessageCount;
  published
    property OnMessage : TNotifyEvent read FOnMessage write FOnMessage;
  end;

  TSMSThread=class(TThread)
  private
    FQueue:TSMSQueue;
    procedure DoSend;
  protected
    procedure Execute; override;
  public
    Constructor Create(Queue:TSMSQueue);
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    Queue : TSMSQueue;
    procedure UpdateGUI(Sender:TObject);
  public
  end;
 
var
  Form1: TForm1;

implementation

{$R *.DFM}

{ TSMSThread }

constructor TSMSThread.Create(Queue: TSMSQueue);
begin
  {Thread constructor. The queue object has been passed as a param. It's not
  actually needed here but might be useful in the future}

  FQueue:=Queue;

  {call the inherited constructor. making sure it is created suspended}

  inherited Create(True);

  {Set priority. Set FreeOnTerminate to false as the Queue object will be
  responsible for freeing the thread}

  Priority:=tpLower;
  FreeOnTerminate:=False;
end;

procedure TSMSThread.DoSend;
var
  i:integer;
begin

  {Any procedures that are called from Execute should check for terminated being true
  If it is then bail out}

  if terminated then
    exit;

  {just waste some time}

  for i:=0 to 50 do
  begin
    sleep(100);
  end;

  {When the process is up then call terminate to signal that the thread is done This will cause the MessageComplete procedure in TSMSQueue to execute}

  terminate;
end;

procedure TSMSThread.Execute;
begin
  {This is the main procedure. Make sure you don't call inherited (it's abstract in the base class}
  DoSend;
end;

{ TSMSQueue }

constructor TSMSQueue.Create(AOwner:TComponent);
begin
  {Constructor for a very basic Queue component}

  inherited Create(AOwner);

  {Create list to manage message threads}

  Flist:=TObjectlist.Create;
end;

destructor TSMSQueue.Destroy;
begin
  {tidy up}

  FList.Free;
  inherited;
end;

procedure TSMSQueue.MessageComplete(Sender:TObject);
begin
  {This event handler is attached to every thread and executes when the thread
  is done. All it does is get the index of the thread in the list and then
  deletes the pointer (and frees the thread)}

  if FList.IndexOf(Sender) > -1 then
    FList.Delete(FList.IndexOf(Sender));

  {Decrement the message count and call the event handler}

  Dec(FintMessageCount);

  if assigned(FOnMessage) then
    FOnMessage(Self);

end;

procedure TSMSQueue.SendMessage;
var
  objMessage:TSMSThread;
begin

  {create a thread and add it to the list}

  objMessage:=TSMSThread.Create(Self);
  Flist.add(objMessage);

  {Set the OnTerminate event handler}

  objMessage.OnTerminate:=MessageComplete;

  {Increment the message count and call the event handler}
  inc(FintMessageCount);
  if assigned(FOnMessage) then
    FOnMessage(Self);

  {Start the thread executing}
  objMessage.Resume;

end;

{============== form stuff ================}

procedure TForm1.FormCreate(Sender: TObject);
begin

  {create queue object and assign an event handler to OnMessage}

  Queue := TSMSQueue.Create(Self);
  Queue.OnMessage := UpdateGUI;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(Queue);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  {Send a message}

  Queue.SendMessage;
end;

procedure TForm1.UpdateGUI(Sender: TObject);
begin

  {This event handler every time a message is starts / finished sending}

  label1.caption := 'messages='+inttostr(Queue.MessageCount);
end;

end.
0
 

Author Comment

by:thornton_paul
ID: 6297126
That's great, just what I need :) You've definately got the points, but I'd like to keep the question open a couple of days while I try it out.

Thanks for your help.
Paul
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6297208
Glad to help.

Jo
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6354069
Hi Paul,

Is everything going ok with your component?

Jo
0
 

Author Comment

by:thornton_paul
ID: 6354896
Hi Jo,

Sorry about the delay, been very busy with the project. The queing is working great, but unfortunately I haven't had chance to look into the threading. Thanks for your help. The points are yours :)

Cheers,
Paul
0
 
LVL 3

Expert Comment

by:nnbbb09
ID: 6355035
No problem. Just post here if you need anymore help. I'll keep the notifications switched on.

Jo
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Suggested Solutions

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now