10
votes

I am impressed by this Library AsynCalls developed by Andy.

I write a piece of code just to test the library, but it always gets memory A/V, did I miss something here?

The following code is intended to parallel this simple task (get maximum from an array) using two asynchronous threads.

program Test;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Math,
  Windows,
  Forms,
  AsyncCalls in 'AsyncCalls.pas';

var
  arr: array of integer;
  i: integer;

procedure GetMax(const arr: array of integer; left, right: integer; var max: integer);
var
  i: integer;
begin
  max := arr[left];
  for i := left + 1 to right do
  begin
    if (arr[i] > max) then
    begin
      max := arr[i];
    end;
  end;
end;

const
  N = 100000;

var
  a, b: IAsyncCall;
  maxv, max1, max2: integer;

begin
  SetLength(arr, N);
  maxv := -1;
  for i := 0 to High(arr) do
  begin
    arr[i] := RandomRange(0, MaxInt);
    if (arr[i] > maxv) then
    begin
      maxv := arr[i];
    end;
  end;

  a := AsyncCall(@GetMax, [arr, 0, Length(arr) div 2, max1]);
  b := AsyncCall(@GetMax, [arr, (Length(arr) div 2) + 1, High(arr), max2]);
  while (AsyncMultiSync([a, b], True, 10) = WAIT_TIMEOUT) do
  begin
    Application.ProcessMessages;
  end;
  Writeln(max1, ', ', max2, ', ', Max(max1, max2));
  Writeln(maxv);
  Readln;
end.
1
Why are you calling ProcessMessages in a console app? - David Heffernan
it doesn't matter, if I remove it, still causing A/V - justyy
+1 for the superb SSCCE! - David Heffernan

1 Answers

8
votes

You are attempting to use the variable parameter version of AsyncCall. The code says that the following types are supported:

Supported types:
  Integer      :  Arg: Integer
  Boolean      :  Arg: Boolean
  Char         :  Arg: AnsiChar
  WideChar     :  Arg: WideChar
  Int64        :  [const] Arg: Int64
  Extended     :  [const] Arg: Extended
  Currency     :  [const] Arg: Currency
  String       :  [const] Arg: ShortString
  Pointer      :  [const] Arg: Pointer
  PChar        :  [const] Arg: PChar
  Object       :  [const] Arg: TObject
  Class        :  [const] Arg: TClass
  AnsiString   :  [const] Arg: AnsiString
  UnicodeString:  [const] Arg: UnicodeString
  PWideChar    :  [const] Arg: PWideChar
  WideString   :  [const] Arg: WideString
  Interface    :  [const] Arg: IInterface
  Variant      :  const Arg: Variant

Your function receives an open array parameter and a var parameter, neither to be found in the list above. What's more, the function must use the cdecl calling convention.

Here is a version of your code that does work:

program Test;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Math,
  AsyncCalls in 'AsyncCalls.pas';

var
  arr: array of integer;

procedure GetMax(left, right: integer; max: PInteger); cdecl;
var
  i: integer;
begin
  max^ := arr[left];
  for i := left + 1 to right do
  begin
    if (arr[i] > max^) then
    begin
      max^ := arr[i];
    end;
  end;
end;

const
  N = 100000;

var
  a, b: IAsyncCall;
  i, maxv, max1, max2: integer;

begin
  SetLength(arr, N);
  maxv := -1;
  for i := 0 to High(arr) do
  begin
    arr[i] := RandomRange(0, MaxInt);
    if (arr[i] > maxv) then
    begin
      maxv := arr[i];
    end;
  end;
  a := AsyncCall(@GetMax, [0, Length(arr) div 2, @max1]);
  b := AsyncCall(@GetMax, [(Length(arr) div 2) + 1, High(arr), @max2]);
  AsyncMultiSync([a, b], True, INFINITE);
  Writeln(max1, ', ', max2, ', ', Max(max1, max2));
  Writeln(maxv);
  Readln;
end.

Note that I made the following changes:

  1. Removed the use of Forms and the call to ProcessMessages.
  2. Made GetMax use the cdecl calling convention.
  3. Pass max as a pointer to integer rather than a var parameter. That is because var parameters are not supported.
  4. Let GetMax use the global variable arr rather than receive it as a parameter. That was for expediency. An alternative might be to pass the address of the first element.

Frankly, your code is complex enough to make the variable parameter AsyncCall feel a little stretched. You might be better passing an object, or a pointer to a record rather than using the variable parameter overload.


I should also point out that AsyncCalls is no longer developed. You would, in my view, be better served by adopting OTL than AsyncCalls.