public function multiCurlPoll()
 {
     $curl_mh = $this->curl_mh;
     do {
         $mrc = curl_multi_exec($curl_mh, $still_running);
         if ($mrc == CURLM_OK) {
             $info = curl_multi_info_read($curl_mh);
             if ($info && isset($this->curl_cb[(int) $info['handle']])) {
                 $as = $this->curl_cb[(int) $info['handle']];
                 $curl = $info['handle'];
                 unset($this->curl_cb[(int) $curl]);
                 $as->_futoin_response = curl_multi_getcontent($curl);
                 $as->success($curl, $info);
                 curl_multi_remove_handle($this->curl_mh, $curl);
             }
         }
     } while ($mrc === CURLM_CALL_MULTI_PERFORM);
     if ($this->curl_event !== null) {
         \FutoIn\RI\AsyncTool::cancelCall($this->curl_event);
         $this->curl_event = null;
     }
     if ($this->curl_cb) {
         $this->curl_event = \FutoIn\RI\AsyncTool::callLater([$this, "multiCurlSelect"]);
     }
 }
 /**
  * Install Async Tool implementation, call using derived class
  */
 public static function init()
 {
     \FutoIn\RI\AsyncTool::init(new static());
 }
 /**
  * Execute loop until *as.break()* is called
  * @param callable $func loop body callable( as )
  * @param string $label optional label to use for *as.break()* and *as.continue()* in inner loops
  */
 public function loop(callable $func, $label = null)
 {
     $this->_sanityCheck();
     $loop_state = new \StdClass();
     $loop_state->model_as = new \FutoIn\RI\AsyncSteps();
     $loop_state->inner_as = null;
     $loop_state->outer_as = null;
     $loop_state->func = $func;
     $loop_state->label = $label;
     $this->add(function ($outer_as) use($loop_state) {
         $loop_state->outer_as = $outer_as;
         $create_iteration = function () use($outer_as, $loop_state) {
             $inner_as = new \FutoIn\RI\AsyncSteps($outer_as->state());
             $inner_as->copyFrom($loop_state->model_as);
             $loop_state->inner_as = $inner_as;
             $inner_as->execute();
         };
         $loop_state->model_as->add(function ($as) use($loop_state) {
             $func = $loop_state->func;
             $func($as);
         }, function ($as, $err) use($loop_state) {
             if ($err === \FutoIn\Error::LoopCont) {
                 $label = $loop_state->label;
                 $term_label = $as->state()->_loop_term_label;
                 if ($term_label && $term_label !== $label) {
                     // Unroll loops continue
                     \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $term_label) {
                         try {
                             $loop_state->outer_as->continueLoop($term_label);
                         } catch (\Exception $e) {
                         }
                     });
                 } else {
                     $as->success();
                     return;
                 }
             } elseif ($err === \FutoIn\Error::LoopBreak) {
                 $label = $loop_state->label;
                 $term_label = $as->state()->_loop_term_label;
                 if ($term_label && $term_label !== $label) {
                     // Unroll loops and break
                     \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $term_label) {
                         try {
                             $loop_state->outer_as->breakLoop($term_label);
                         } catch (\Exception $e) {
                         }
                     });
                 } else {
                     // Continue linear execution
                     \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $term_label) {
                         try {
                             $loop_state->outer_as->success();
                         } catch (\Exception $e) {
                         }
                     });
                 }
             } else {
                 \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $err) {
                     try {
                         $loop_state->outer_as->error($err);
                     } catch (\Exception $e) {
                     }
                 });
             }
             $loop_state->model_as->cancel();
             $loop_state->model_as = null;
             $loop_state->func = null;
         })->add(function ($as) use($create_iteration) {
             \FutoIn\RI\AsyncTool::callLater($create_iteration);
         });
         $outer_as->setCancel(function ($as) use($loop_state) {
             $loop_state->inner_as->cancel();
             $loop_state->inner_as = null;
             if ($loop_state->model_as) {
                 $loop_state->model_as->cancel();
                 $loop_state->model_as = null;
             }
             $loop_state->func = null;
         });
         $create_iteration();
     });
     return $this;
 }
use FutoIn\RI\AsyncTool;
function dummy_service_read($success, $error)
{
    // We expect it calles success when data is available
    // and error, if error occurs
    // Returns some request handle
    return null;
}
function dummy_service_cancel($reqhandle)
{
    // We assume it cancels previously scheduled reqhandle
}
$root_as = new ScopedSteps();
$root_as->add(function ($as) {
    AsyncTool::callLater(function () use($as) {
        $as->success('async success()');
    });
    $as->setTimeout(10);
    // ms
})->add(function ($as, $arg) {
    echo $arg . PHP_EOL;
    $reqhandle = dummy_service_read(function ($data) use($as) {
        $as->success($data);
    }, function ($err) use($as) {
        if ($err !== 'SomeSpecificCancelCode') {
            $as->error($err);
        }
    });
    $as->setCancel(function ($as) use($reqhandle) {
        dummy_service_cancel($reqhandle);
    });
 public function testAsyncSuccess()
 {
     $as = $this->as;
     $as->executed = false;
     $as->add(function ($as) {
         $as->setCancel(function () {
         });
         AsyncTool::callLater(function () use($as) {
             try {
                 $as->error('MyError');
             } catch (\Exception $e) {
             }
         });
     }, function ($as, $err) {
         $as->executed = $err === 'MyError';
     });
     $as->execute();
     AsyncToolTest::run();
     $this->assertTrue($as->executed);
 }