Skip to content

Async and Multi Interface

Qriist edited this page Sep 16, 2026 · 1 revision

The Multi interface drives concurrent transfers through a shared pool. Multiple easy handles are added to a multi handle, and Async() polls the pool until all transfers complete.

Creating a Multi Pool

MultiInit creates a new multi_handle. One is automatically established during LibQurl(), but you can create additional pools to group downloads as desired:

first_pool := curl.MultiInit()
second_pool := curl.MultiInit()

Adding Handles to a Pool

ReadyAsync adds easy_handles to a multi_handle pool. Accepts a single handle or an array:

; Single handle
curl.ReadyAsync(easy_handle)

; Batch
easyHandles := [easy1, easy2, easy3]
curl.ReadyAsync(easyHandles, multi_handle)

The multi_handle parameter is optional and defaults to the oldest created pool.

The Async Polling Loop

Async performs one pass of the transfer cycle and returns a still_running boolean (1 or 0). Poll in a loop until all transfers in the pool are complete:

curl.ReadyAsync([easy1, easy2, easy3])

Loop {
    still_running := curl.Async()
} Until !still_running

Timing between iterations is caller-controlled. libcurl handles the network layer and keep-alive internally. Any reasonable non-blocking yield works.

Multiple Pools

You can maintain multiple multi pools and poll them in the same loop:

first_pool  := curl.MultiInit()
second_pool := curl.MultiInit()

curl.ReadyAsync([easy1, easy2], first_pool)
curl.ReadyAsync([easy3, easy4], second_pool)

Loop {
    check := 0
    check += curl.Async(first_pool)
    check += curl.Async(second_pool)
} Until !check

Moving Handles Between Pools

SwapMultiPools transfers easy handles from one pool to another:

curl.SwapMultiPools([easyA, easyB], first_pool, second_pool)

The handles are removed from the source pool and added to the destination pool.

Sync Within an Async Context

Calling Sync on a handle that is currently in a multi_handle pool automatically removes it from the pool first. This prevents a handle from being driven by both interfaces simultaneously:

; This handle was added to the pool via ReadyAsync
; Sync removes it from the pool and performs the transfer synchronously
curl.Sync(easy_handle)

However, there are no checks to see if the easy_handle is being actively used by the multi_handle, so consider this a safety net rather than reliable full protection.

Handle Reuse

Once a transfer completes, the easy_handle can be reconfigured and added back to a multi_handle pool:

curl.SetOpt("URL", "https://example.com/next-download", easy_handle)
curl.WriteToFile("C:\downloads\next-file.bin", easy_handle)
curl.ReadyAsync(easy_handle, first_pool)

Loop {
    still_running := curl.Async(first_pool)
} Until !still_running

Inspecting Pool State

; Counts: current, running, pending, done, added
info := curl.GetMultiInfo(multi_handle)
MsgBox curl.PrintObj(info)

; Completed transfer details from the multi_handle pool's message queue
completed := curl.MultiInfoRead(multi_handle)
MsgBox curl.PrintObj(completed)

; List all easy handles currently in the pool
handles := curl.MultiGetHandles(multi_handle)
MsgBox handles.Length " handles in pool"

## Cleanup

`MultiCleanup` releases the multi_handle and all easy_handles associated with it. Any handles you want to keep should be removed via `RemoveEasyFromMulti` first:

```ahk
; Keep easy1, release the rest and the pool
curl.RemoveEasyFromMulti(easy1, multi_handle)
curl.MultiCleanup(multi_handle)

Where to Go Next

Clone this wiki locally