Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works

Random project

CocinAsync

. Delphi High performance library for Delphi to simplify coding and improve performance of asynchronous and multithreaded applications.

View on Bitbucket

Anyone may press Interesting, signed in or not. It helps others find this project.

Member rating

No ratings yet.

Sign in to rate this project.

README

CocinAsync

A high performance library for Delphi to simplify coding and improve performance of asynchronous and multithreaded applications.

Installation

Option A — DPM (recommended for application projects)

DPM (Delphi Package Manager) installs CocinAsync as a source-only package. DPM downloads the sources into your local package cache and adds them to the project search path via $(DPMSearch). Nothing is registered in the IDE and no .bpl is required — units compile into your app like any other source.

Package id sivv.cocinasync
Feed delphi.dev public gallery (registered by default on first DPM run)
Delphi versions 10.0 Seattle through 13.0 Florence
Installed units Core, VCL, FMX, console, and Flux units (under src\ and src\flux\ in the package cache)

1. One-time setup

  1. Install the DPM client (command-line tool and IDE plugin). Leave Add to PATH enabled.

  2. Confirm a gallery source is configured:

    text
    dpm sources List

    You should see a source pointing at https://delphi.dev/... (often named DPM or delphi.dev). If not:

    text
    dpm sources Add "-name=delphi.dev" "-source=https://delphi.dev/api/v2/index.json" -type=DPMServer
    dpm sources Enable "-name=delphi.dev"

2. Add the package to your project

From the IDE: open your .dproj, right-click the project → Manage DPM Packages, search for sivv.cocinasync, and install.

From the command line (run from any folder):

text
dpm install sivv.cocinasync C:\path\to\YourProject.dproj

Pin a specific release:

text
dpm install sivv.cocinasync -version=1.0.0 C:\path\to\YourProject.dproj

Limit platforms if needed (defaults to all platforms your project targets):

text
dpm install sivv.cocinasync -platforms=Win32,Win64 C:\path\to\YourProject.dproj

3. Use it in code

pascal
uses
  cocinasync.async,
  cocinasync.jobs,
  cocinasync.collections,
  cocinasync.promise;

uses cocinasync.event.vcl;      // optional: VCL event helpers
uses cocinasync.monitor.vcl;    // optional: VCL job monitor form

uses cocinasync.event.fmx;      // optional: FMX event helpers
uses cocinasync.monitor.fmx;    // optional: FMX job monitor form

uses cocinasync.event.console;  // optional: console event helpers
uses cocinasync.monitor.console; // optional: console job monitor

uses cocinasync.flux;           // optional: Flux state management
uses cocinasync.flux.view.vcl;  // optional: Flux VCL views
uses cocinasync.flux.view.fmx; // optional: Flux FMX views

Add only the units your project needs. Core units (cocinasync.async, cocinasync.jobs, etc.) are framework-agnostic. VCL, FMX, console, and Flux units are optional layers on top of the core library.

4. What DPM changes in your .dproj

After install, commit the DPM-related edits to version control:

  • PackageReference elements at the bottom of the .dproj (package id + version).
  • $(DPMSearch) added to DCC_UnitSearchPath for each installed platform.

Other developers (and CI) do not need a copy of this repo. After cloning your project:

text
dpm restore C:\path\to\YourProject.dproj

That downloads the referenced package version into %AppData%\.dpm\ and wires up the same search paths.

5. Upgrade or remove

text
dpm install sivv.cocinasync -version=1.0.1 -upgrade C:\path\to\YourProject.dproj

Use the IDE package manager to uninstall, or edit the PackageReference entries and run dpm restore.

Local / private feed

If your team mirrors packages internally, register that folder or server as an additional DPM source and pass -source=YourSourceName on install / restore. The package spec lives at sivv.cocinasync.dspec.yaml.

Option B — Manual (clone / submodule / search path)

Clone this repo (or add it as a submodule) and add the repo root and flux\ folder to your project's search path. Build the .dpk packages in the IDE, or compile units directly into your project. Demo and test projects in this repo use relative search paths so contributors can build without DPM.

Maintainers — pack and publish

Tag the release first (git tag release/1.0.0), then:

powershell
$env:DPM_API_KEY = 'your-api-key'
.\scripts\publish-dpm.ps1

The script reads the version from the release/* tag at HEAD (or the latest release/* tag), packs into dist\dpm, and pushes to the delphi.dev gallery. Pack only: .\scripts\publish-dpm.ps1 -PackOnly · Push only: -PushOnly · See scripts/publish-dpm.ps1.

Units

Unit: cocinasync.async

Inline coding helpers for implementing asynchronous actions

This unit includes two synchronization helpers that make it easy to make sure code is executed in the main thread. They automatically protect you from accidentally synchronizing from the main thread.

pascal
#!delphi

TAsync.SynchronizeIfInThread(
  procedure
  begin
    // Do something in the main thread
  end
);

TAsync.QueueIfInThread(
  procedure
  begin
    // Do something in the main thread
  end
);

Asynchronous operations can easily be performed inline in code. The global Async object may be used when lifetime of the async operations is not dependent upon another object (like a form or data module).

pascal
#!delphi

Async.AfterDo(1000, 
  procedure
  begin
    // Execute this code after waiting 1s
  end
);

Async.DoLater(
  procedure
  begin
    // Execute this code at some point in the very near future.
  end
);

Async.OnDo(
  function : boolean
  begin
    // Check for some condition, return true if the due should be executed. False if it should not
  end,
  procedure
  begin
    // Executes only when the condition above has been met
  end,
  1000, // interval on which the check function should be called
  function : boolean
  begin
    // Return True to continue checking or False to abort
  end
);

Aync.DoEvery(1000,
  function : boolean
  begin
    // Execute this function every 1s. Return True to allow it to execute again or False to stop.
  end
);

When lifetime of an asynchronous operation depends upon another object such as a form or data module, create an instance of TAsync for that object. If the global Async is used, then it's possible the asynchronous methods will be called after needed objects are freed.

pascal
#!delphi

procedure FormCreate(Sender : TObject);
begin
  FAsync := TAsync.Create;
  FAsync.DoEvery(10,
    procedure
    begin
      Shape1.Left := Random(Width-Shape1.Width);
    end
  );
end;

procedure FormDestroy(Sender : TObject);
begin
  FAsync.Free;
end;

Unit: cocinasync.collections

Lock-Free generic collection classes for high performance use in mutlithreaded applications

  • TQueue<T> - high performance queue collection that is thread safe without locking
  • TStack<T> - high performance stack collection that is thread safe without locking
  • THash<T> - high performance hash collection that is thread safe without locking

cocinasync.global

Utility objects

cocinasync.jobs

A background job runner which easily executes tasks in the background

Delphi's System.Threading unit includes a Task execution library which on it's surface is very useful. It suffers from a throttling algorithm that can cause performance issues for some workloads. Jobs is an attempt to execute background tasks in a highly performant way through natural throttling based on workload and system capacity. By default a global Jobs runner is created with one running thread per logical processor on the system.

pascal
#!delphi
// Queue a background job that will execute as soon as an idle job runner notices it.
TJobManager.Excute(
  procedure
  begin
    // Add something to the Jobs Queue and execute as soon as possible
  end
);

// Jobs can also return values.  Asking for the return value 
// will cause the thread to wait until the value is available.
jobCount := TJobManager.Execute<Integer>(
  function : Integer
  begin
    Result := 0;
    for i := 1 to 30 do
    begin 
     sleep(1000)
     inc(Result);
    end
  end
);

// ... do some other work here in the main thread or in other jobs

Result := jobCount.Result; // will stop execution of this thread here until 
                           // the jobCount job has returned it's result.
                           // NOTE: if you do this in the main thread, 
                           // gui operations will block at this point until 
                           // the job returns.  Also, if using in the main thread
                           // do not do operations from within the job that execute
                           // through synchronization as it will cause a deadlock.

// Sometimes you want to do a bunch of jobs and wait for them all to complete.  
// To do this you can use a TJobQueue

queue := TJobQueue.Create;

TJobManager.Execute(
  procedure
  begin
    // do something important
  end,
  queue
);

TJobManager.Execute(
  procedure
  begin
    // do something else here
  end,
  queue
);

queue.WaitForAll; // Execution will stop here until all of the jobs in the queue have completed.

Depending upon workload, it may be desirable to create a separate jobs queue for a particular workload. To do so call CreateJobs() with the number of threads you wish to dedicate to running the jobs.

pascal
#!delphi

procedure FormCreate(Sender : TObject);
begin
  // Create a Jobs Runner with 25 threads
  // Note that there is a default job runner under a global variable "Jobs"
  // which should suffice in most applications.
  FJobs := TJobManager.CreateJobs(25); 
end; 

procedure Button1Click(Sender : TObject);
begin
  FJobs.Queue(
    procedure
    begin
      // Add some workload to the jobs runner
    end
  );
end;

Contribution guidelines

  • Fork and PR
  • Fixes to the core library are welcome and encouraged
  • Performance improvement PRs must be provable
  • Additions to the library will be accepted if it furthers the project goal to simplify asynchronous and multithreaded development and to do so with a focus on high performance.

MIT License

See LICENSE.

Copyright 2017 by Bugfish Limited