?
Solved

Best way to call code after interrupt

Posted on 2009-04-20
10
Medium Priority
?
308 Views
Last Modified: 2012-05-06
Hi,
I have a hardware interrupt which executes some code (in Delphi) and sets a flag.  
The flag is to be used to update displays and databases.
What is the best way to ensure that the code called by the flag is executed as quickly as possible after the end of the interrupt routine ?
Examples would be appreciated
Thanks
0
Comment
Question by:diver999
  • 4
  • 4
  • 2
10 Comments
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 24187255
use the observer pattern and put the flag in an observable
all the observers get called instantly when the flag changes

http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_23365012.html
0
 

Author Comment

by:diver999
ID: 24193674
Hi Geert
Thanks for that.  I think that the use of observers (I didn't know about them) sounds very interesting - and a big subject - which I need to research.  At present I feel out of my depth with this.
This question is connected with http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Graphics/Q_24334423.html
I have a fairly old and (until now) stable application for image analysis which is triggered by hardware and makes an interrupt.    Within the interrupt routine I had a lot of stuff including display of images and results.  I think that the faster computer is generating interrupts at a higher priority than my hardware, which is causing my application to crash (I know that you should always keep interrupt routines small, but this has seemed to work for a long time....)
I have re-written it so that the interrupt routine just gets the image data into a bitmap object and sets a boolean global variable "inspect_flag".  I then have a timer :
>>>>>>>>>>>>>>>>>>>>>>
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if inspect_flag then
  begin
    UpdateEverythingElse;      
    inspect_flag := false;
  end;
end;
<<<<<<<<<<<<<<<<<<<<<<
which now works fine, but of course I have the timer delay. (I am getting about 300 images per minute)
So I just want something that monitors "inspect_flag" and executes the UpdateEverythingElse as soon as it exits the interrupt routine.
Sorry if I sound a bit dumb - I know I should go and read up about observers etc but I just need to get this going rather quickly
Thanks for your help.
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 24194096
you used a timer to decouple the setting of the flag so your app doesn't need to wait on the update ?
you can use a windows message just the same, without needing a timer
and it should work faster ... if nothing changes, nothing needs an update and no timer running

you'll need to change the setting of the boolean flag to calling a procedure

procedure SetFlagChanded;
begin
  if Assigned(Form1) then
    Form1.FlagChanged := True;
end;
const
  WM_FlagChanged = WM_USER +1;
 
type
  TForm1 = class(TForm)
  private
    procedure SetInspectFlag(const Value: Boolean);
    procedure WMFlagChanged(var Msg: TMessage); message WM_FlagChanged;
  public
    property inspect_flag: boolean read fInspectFlag write SetInspectFlag;
  end;
 
procedure TForm1.SetInspectFlag(const Value: Boolean);
begin
  if fInspectFlag <> Value then 
  begin
    fInspectFlag := Value;
    if fInspectFlag then // only send message when true
      PostMessage(Handle, WM_FlagChanged, 0, 0);
  end;
end;
 
procedure TForm1.WMFlagChanged(var Msg: TMessage); 
begin
  UpdateEverythingElse;  
  fInspectFlag := False;
end;

Open in new window

0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 24194105
oops, typo:

procedure SetFlagChanded;
begin
  if Assigned(Form1) then
    Form1.inspect_flag := True;
end;
0
 

Author Comment

by:diver999
ID: 24197791
It looks the right sort of thing but I am not sure where fInspectFlag is declared
It doesn't compile and I can't see what to do
Thanks
Richard
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 24201464
sorry, i usually just type this in the firefox editor, don't use delphi all the time to test

Shift-Ctrl C would have solved that ...
(class completion, just type a procedure in the header and Shift-Ctrl C makes the implementation for you)

  TForm1 = class(TForm)
  private
    fInspectFlag: Boolean;
    procedure SetInspectFlag(const Value: Boolean);
    procedure WMFlagChanged(var Msg: TMessage); message WM_FlagChanged;
  public
    property inspect_flag: boolean read fInspectFlag write SetInspectFlag;
  end;
0
 

Author Comment

by:diver999
ID: 24212941
Thanks Geert
This is what I have...
I am using RapidDriver Pci to get the hardware interrupt (stable solution - been working several years)
What happens is that the UpdateEverythingElse code is not called on every occasion that the Inspect_flag is set.
Am I setting it in the right place ?  I tried before ClearInterrupt also.
I will do some more research.
  TForm1 = class(TForm);
  private
    fInspectFlag : boolean;
    procedure SetInspectFlag(const Value: Boolean);
    procedure WMFlagChanged(var Msg: TMessage); message WM_FlagChanged;
  public
    property inspect_flag: boolean read fInspectFlag write SetInspectFlag;
  end;
 
 
procedure TForm1.SetInspectFlag(const Value: Boolean);
begin
  if fInspectFlag <> Value then
  begin
    fInspectFlag := Value;
    if fInspectFlag then // only send message when true
      PostMessage(Handle, WM_FlagChanged, 0, 0);
  end;
end;
 
procedure TForm1.WMFlagChanged(var Msg: TMessage);
begin
  UpdateEverythingElse;
  fInspectFlag := False;
end;
 
 
procedure UpdateEverythingElse;
begin
   ...
   ...
   ...
end;
 
procedure OnHardwareInterrupt(TimStampLo: Longword; TimeSampHi: Longword); stdcall;
begin
    DisableInterrupt;  //set pci register
    get_image;
    ...
    ...
    ...
    ClearInterrupt;  //reset interrupt flag latch in hardware
    EnableInterrupt; //set pci register 
    Form1.Inspect_flag := true;
end;

Open in new window

0
 
LVL 4

Expert Comment

by:JonasMalmsten
ID: 24800954
Using the code above you will only post a message each time the fInspectFlag is changed from False into True.

If you receive interrupts faster than you are able to process the image and clear the flag, the windows message will not be sent once for every interrupt. One simple change may be able to solve this, swap the lines

  UpdateEverythingElse;
  fInspectFlag := False;

for

  fInspectFlag := False;
  UpdateEverythingElse;

But this is probably not the optimal solution. Do you need to process each and every image or would it be sufficient to process the most recently received image? A better solution may be to store all images in a temporary list/queue and then dequeue them for processing.

Given that this is a hardware interrupt the callback may not be using regular windows messages, in which case the code must also be thread safe to make sure that the interrupt is not copying a new image onto the previous image while you are still processing it. Do you know if this is the case?
0
 
LVL 4

Accepted Solution

by:
JonasMalmsten earned 1500 total points
ID: 24801475
Here is another version, same strategy as Geert_Gruwez using window messages but with a few more options (see comments in code).
I'm not sure exactely what you need for processing the image, if a bitmap is enough or if you also need the time stamps etc. In this case you can replace TBitMap with your custom class containing all the properties you need to process the image. This way you minimize the code used inside the interrupt.
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;
 
const
  UM_PROCESS_IMAGES = WM_USER + 1;
 
type
  TForm1 = class(TForm)
  private
    FUMProcessImagesRequests: Integer;
    FImages: TThreadList;
    function DequeueImage(var bmp: TBitMap): Boolean;
    function GetImageCount: Integer;
    procedure ProcessImages;
    procedure PostUMProcessMessages;
    procedure UMProcessImages(var msg: TMessage); message UM_PROCESS_IMAGES;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure EnqueueImage(bmp: TBitMap);
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
procedure OnHardwareInterrupt(TimStampLo: Longword; TimeSampHi: Longword); stdcall;
var
  bmp: TBitmap;
begin
//    DisableInterrupt;
  bmp := TBitMap.Create;
//    get_image; // Store the image in bmp here to add to process queue
  Form1.EnqueueImage(bmp);
//    ClearInterrupt;
//    EnableInterrupt;
end;
 
{ TForm1 }
 
constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  FImages := TThreadList.Create;
end;
 
destructor TForm1.Destroy;
var
  bmp: TBitMap;
begin
// Free any non-processed images
  while DequeueImage(bmp) do bmp.Free;
  FImages.Free;
  inherited;
end;
 
procedure TForm1.EnqueueImage(bmp: TBitMap);
begin
  FImages.Add(bmp);
  PostUMProcessMessages;
end;
 
function TForm1.GetImageCount: Integer;
var
  lst: TList;
begin
  lst := FImages.LockList;
  try
    Result := lst.Count;
  finally
    FImages.UnlockList;
  end;
end;
 
function TForm1.DequeueImage(var bmp: TBitMap): Boolean;
var
  lst: TList;
begin
  lst := FImages.LockList;
  try
    Result := lst.Count > 0;
    if Result then
    begin
      bmp := lst[0];
      lst.Delete(0);
    end;
  finally
    FImages.UnlockList;
  end;
end;
 
procedure TForm1.PostUMProcessMessages;
begin
  Inc(FUMProcessImagesRequests);
  PostMessage(Handle, UM_PROCESS_IMAGES, 0, 0);
end;
 
procedure TForm1.ProcessImages;
var
  bmp, bmp2: TBitMap;
begin
// Option 1
// This will run the UpdateEverythingElse once for each and every image that is
// received even when you are backed up and cannot process them fast enough.
  while DequeueImage(bmp) do
  begin
    UpdateEverythingElse;  // Update everything else using the image contained in bmp here
    bmp.Free;
  end;
 
// Option 2
// If you get backed up and there are too many images to be processed, option 1
// will cause the application to be non-responsive. This option will prevent this.
//  if DequeueImage(bmp) then
//  begin
//    UpdateEverythingElse;  // Update everything else using the image contained in bmp here
//    bmp.Free;
//    PostUMProcessMessages; // Allow your main form to process other messages and then eventually returning here
//  end;
 
// Option 3
// If images are displayed on screen only and you get backed up, droping a few
// frames may be acceptable. This option will only process the most recently
// received image
//  if DequeueImage(bmp) then
//  begin
//    while DequeueImage(bmp2) do
//    begin
//      bmp.Free;
//      bmp := bmp2;
//    end;
//    UpdateEverythingElse;  // Update everything else using the image contained in bmp here
//    bmp.Free;
//  end;
end;
 
procedure TForm1.UMProcessImages(var msg: TMessage);
begin
  Dec(FUMProcessImagesRequests);
// Here are a few options depending on how you want the program to work.
// FUMProcessImagesRequests > 0 means that you are backed up and more images
// are being continually received faster than you have been able to process them.
// The code below will process images as soon as you are not backed up or if the
// queue has grown to contain more than 10 images.
  if (FUMProcessImagesRequests = 0) or (GetImageCount > 10) then ProcessImages;
end;
 
end.

Open in new window

0
 

Author Closing Comment

by:diver999
ID: 31572341
Real problem was not solved - it was down to dual core procressor.  Reverted to signle core processr to make the problem go away
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Whether it be Exchange Server Crash Issues, Dirty Shutdown Errors or Failed to mount error, Stellar Phoenix Mailbox Exchange Recovery has always got your back. With the help of its easy to understand user interface and 3 simple steps recovery proced…
Suggested Courses

850 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