Deferred Execution

Where Parallel Tasks Join

Consider the following example contracts. They demonstrate another key feature called deferred execution, where the scheduler moves transactions to the next generation to create a custom aggregation point after parallel execution.

Example1

VisitCounterWithDeferred uses U256Cumulative for parallel-safe increments and adds a deferred join step: all visit() calls in a generation run in parallel, with one promoted to the next generation to emit the aggregated result after the generation boundary.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0;

import "@arcologynetwork/concurrentlib/lib/commutative/U256Cum.sol"; 
import "@arcologynetwork/concurrentlib/lib/runtime/Runtime.sol"; 

// This counter supports commutative updates, allowing safe aggregation
// of increments from multiple transactions in parallel.
contract VisitCounterWithDeferred { 
    // A cumulative counter instance that allows concurrent increments
    // from multiple transactions without conflicts.   
    U256Cumulative totalVisit; 
    
    event CounterQuery(uint256 value);

    constructor()  {
        // Initialize the cumulative counter with a starting value of 0
        // and an upper limit of 1,000,000. Any attempt to exceed the
        // range will cause the transaction to revert.
        totalVisit = new U256Cumulative(0, 1000000);

        // Inform the scheduler when there are multiple transaction 
        // calling visit(), one of them will be executed in the
        // deferred transaction. Each call to visit() is charged an 
        // extra 2,000 gas to fund the deferred join step run later
        // in the block. If the deferred transaction fails, 
        // the unused gas is refunded.
        Runtime.defer("visit()", 2000);
    }

    // Increments the visit counter by 1.
    // Multiple invocations in the same generation can be executed in parallel
    // and will be merged deterministically by Arcology at commit time.
    function visit() public {
        // The line below adds 1 to the visit counter, which will executed anyway.
        totalVisit.add(1);

        // If the transaction is in a deferred generation, emit the counter value.
        // The code is executed in the deferred transaction only.
        if (Runtime.isInDeferred()) {
            emit CounterQuery(totalVisit.get());
        }
    }
}

Example 2

Last updated