1244 lines
182 KiB
HTML
1244 lines
182 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>
|
||
File: promises.out
|
||
|
||
— Concurrent Ruby
|
||
|
||
</title>
|
||
|
||
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
||
|
||
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
||
|
||
<script type="text/javascript" charset="utf-8">
|
||
pathId = "promises.out";
|
||
relpath = '';
|
||
</script>
|
||
|
||
|
||
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
||
|
||
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
||
|
||
|
||
</head>
|
||
<body>
|
||
<div class="nav_wrap">
|
||
<iframe id="nav" src="file_list.html?1"></iframe>
|
||
<div id="resizer"></div>
|
||
</div>
|
||
|
||
<div id="main" tabindex="-1">
|
||
<div id="header">
|
||
<div id="menu">
|
||
|
||
<a href="_index.html">Index</a> »
|
||
<span class="title">File: promises.out</span>
|
||
|
||
</div>
|
||
|
||
<div id="search">
|
||
|
||
<a class="full_list_link" id="class_list_link"
|
||
href="class_list.html">
|
||
|
||
<svg width="24" height="24">
|
||
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
||
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
||
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
||
</svg>
|
||
</a>
|
||
|
||
</div>
|
||
<div class="clear"></div>
|
||
</div>
|
||
|
||
<div id="content"><div id='filecontents'><h1>Basics</h1>
|
||
|
||
<h2>Factory methods</h2>
|
||
|
||
<p>Future and Event are created indirectly with constructor methods in
|
||
FactoryMethods. They are not designed for inheritance but rather for
|
||
composition.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html" title="Concurrent::Promises::FactoryMethods (module)">FactoryMethods</a></span></span><span class='period'>.</span><span class='id identifier rubyid_instance_methods'>instance_methods</span><span class='lparen'>(</span><span class='kw'>false</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_sort'>sort</span>
|
||
<span class='comment'># => [:any,
|
||
</span><span class='comment'># :any_event,
|
||
</span><span class='comment'># :any_event_on,
|
||
</span><span class='comment'># :any_fulfilled_future,
|
||
</span><span class='comment'># :any_fulfilled_future_on,
|
||
</span><span class='comment'># :any_resolved_future,
|
||
</span><span class='comment'># :any_resolved_future_on,
|
||
</span><span class='comment'># :delay,
|
||
</span><span class='comment'># :delay_on,
|
||
</span><span class='comment'># :fulfilled_future,
|
||
</span><span class='comment'># :future,
|
||
</span><span class='comment'># :future_on,
|
||
</span><span class='comment'># :make_future,
|
||
</span><span class='comment'># :rejected_future,
|
||
</span><span class='comment'># :resolvable_event,
|
||
</span><span class='comment'># :resolvable_event_on,
|
||
</span><span class='comment'># :resolvable_future,
|
||
</span><span class='comment'># :resolvable_future_on,
|
||
</span><span class='comment'># :resolved_event,
|
||
</span><span class='comment'># :resolved_future,
|
||
</span><span class='comment'># :schedule,
|
||
</span><span class='comment'># :schedule_on,
|
||
</span><span class='comment'># :zip,
|
||
</span><span class='comment'># :zip_events,
|
||
</span><span class='comment'># :zip_events_on,
|
||
</span><span class='comment'># :zip_futures,
|
||
</span><span class='comment'># :zip_futures_on,
|
||
</span><span class='comment'># :zip_futures_over,
|
||
</span><span class='comment'># :zip_futures_over_on]
|
||
</span></code></pre>
|
||
|
||
<p>The module can be included or extended where needed.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'>Class</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span> <span class='kw'>do</span>
|
||
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html" title="Concurrent::Promises::FactoryMethods (module)">FactoryMethods</a></span></span>
|
||
|
||
<span class='kw'>def</span> <span class='id identifier rubyid_a_method'>a_method</span>
|
||
<span class='id identifier rubyid_resolvable_event'>resolvable_event</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='period'>.</span><span class='id identifier rubyid_a_method'>a_method</span>
|
||
<span class='comment'># => #<Concurrent::Promises::ResolvableEvent:0x0000000000000002 pending>
|
||
</span>
|
||
<span class='id identifier rubyid_mod'>mod</span> <span class='op'>=</span> <span class='const'>Module</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span> <span class='kw'>do</span>
|
||
<span class='id identifier rubyid_extend'>extend</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html" title="Concurrent::Promises::FactoryMethods (module)">FactoryMethods</a></span></span>
|
||
<span class='kw'>end</span>
|
||
<span class='id identifier rubyid_mod'>mod</span><span class='period'>.</span><span class='id identifier rubyid_resolvable_event'>resolvable_event</span>
|
||
<span class='comment'># => #<Concurrent::Promises::ResolvableEvent:0x0000000000000003 pending>
|
||
</span></code></pre>
|
||
|
||
<p>The default executor can be changed by overriding <code>default_executor</code> method
|
||
inherited from <code>Concurrent::Promises::FactoryMethods</code>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_mod'>mod</span> <span class='op'>=</span> <span class='const'>Module</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span> <span class='kw'>do</span>
|
||
<span class='id identifier rubyid_extend'>extend</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html" title="Concurrent::Promises::FactoryMethods (module)">FactoryMethods</a></span></span>
|
||
<span class='kw'>def</span> <span class='kw'>self</span><span class='period'>.</span><span class='id identifier rubyid_default_executor'>default_executor</span>
|
||
<span class='symbol'>:fast</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='id identifier rubyid_mod'>mod</span><span class='period'>.</span><span class='id identifier rubyid_future'>future</span> <span class='lbrace'>{</span> <span class='int'>1</span> <span class='rbrace'>}</span><span class='period'>.</span><span class='id identifier rubyid_default_executor'>default_executor</span> <span class='comment'># => :fast
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='int'>1</span> <span class='rbrace'>}</span><span class='period'>.</span><span class='id identifier rubyid_default_executor'>default_executor</span>
|
||
<span class='comment'># => :io
|
||
</span></code></pre>
|
||
|
||
<p>The module is already extended into <span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Concurrent::Promises</a></span> for convenience.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_resolvable_event'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#resolvable_event-instance_method" title="Concurrent::Promises::FactoryMethods#resolvable_event (method)">resolvable_event</a></span></span>
|
||
<span class='comment'># => #<Concurrent::Promises::ResolvableEvent:0x0000000000000004 pending>
|
||
</span></code></pre>
|
||
|
||
<h2>Asynchronous task</h2>
|
||
|
||
<p>The most basic use-case of the framework is asynchronous processing. A task can
|
||
be processed asynchronously by using a <code>future</code> factory method. The block will
|
||
be executed on an internal thread pool.</p>
|
||
|
||
<p>Arguments of <code>future</code> are passed to the block and evaluation starts immediately.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='float'>0.1</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_duration'>duration</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_sleep'>sleep</span> <span class='id identifier rubyid_duration'>duration</span>
|
||
<span class='symbol'>:result</span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000005 pending>
|
||
</span></code></pre>
|
||
|
||
<p>Asks if the future is resolved, here it will be still in the middle of the
|
||
sleep call.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_resolved?'>resolved?</span> <span class='comment'># => false
|
||
</span></code></pre>
|
||
|
||
<p>Retrieving the value will block until the future is <strong>resolved</strong>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='comment'># => :result
|
||
</span><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_resolved?'>resolved?</span> <span class='comment'># => true
|
||
</span></code></pre>
|
||
|
||
<p>If the task fails, we talk about the future being <strong>rejected</strong>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span><span class='semicolon'>;</span> <span class='id identifier rubyid_raise'>raise</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Boom</span><span class='tstring_end'>'</span></span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000006 pending>
|
||
</span></code></pre>
|
||
|
||
<p>There is no result, the future was rejected with a reason.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='comment'># => nil
|
||
</span><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_reason'>reason</span> <span class='comment'># => #<RuntimeError: Boom>
|
||
</span></code></pre>
|
||
|
||
<p>It can be forced to raise the reason for rejection when retrieving the value.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='kw'>begin</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span>
|
||
<span class='kw'>rescue</span> <span class='op'>=></span> <span class='id identifier rubyid_e'>e</span>
|
||
<span class='id identifier rubyid_e'>e</span>
|
||
<span class='kw'>end</span> <span class='comment'># => #<RuntimeError: Boom>
|
||
</span></code></pre>
|
||
|
||
<p>Which is the same as <code>future.value! rescue $!</code> which will be used hereafter.</p>
|
||
|
||
<p>Or it can be used directly as argument for raise, since it implements exception
|
||
method.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_raise'>raise</span> <span class='id identifier rubyid_future'>future</span> <span class='kw'>rescue</span> <span class='gvar'>$!</span> <span class='comment'># => #<RuntimeError: Boom>
|
||
</span></code></pre>
|
||
|
||
<h2>States</h2>
|
||
|
||
<p>Let's define an inspection helper for methods.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='kw'>def</span> <span class='id identifier rubyid_inspect_methods'>inspect_methods</span><span class='lparen'>(</span><span class='op'>*</span><span class='id identifier rubyid_methods'>methods</span><span class='comma'>,</span> <span class='label'>of:</span><span class='rparen'>)</span>
|
||
<span class='id identifier rubyid_methods'>methods</span><span class='period'>.</span><span class='id identifier rubyid_reduce'>reduce</span><span class='lparen'>(</span><span class='lbrace'>{</span><span class='rbrace'>}</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_h'>h</span><span class='comma'>,</span> <span class='id identifier rubyid_m'>m</span><span class='op'>|</span> <span class='id identifier rubyid_h'>h</span><span class='period'>.</span><span class='id identifier rubyid_update'>update</span> <span class='id identifier rubyid_m'>m</span> <span class='op'>=></span> <span class='id identifier rubyid_of'>of</span><span class='period'>.</span><span class='id identifier rubyid_send'>send</span><span class='lparen'>(</span><span class='id identifier rubyid_m'>m</span><span class='rparen'>)</span> <span class='rbrace'>}</span>
|
||
<span class='kw'>end</span>
|
||
</code></pre>
|
||
|
||
<p>Event has a <code>pending</code> and a <code>resolved</code> state. </p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_event'>event</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_resolvable_event'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#resolvable_event-instance_method" title="Concurrent::Promises::FactoryMethods#resolvable_event (method)">resolvable_event</a></span></span>
|
||
<span class='id identifier rubyid_inspect_methods'>inspect_methods</span><span class='lparen'>(</span><span class='symbol'>:state</span><span class='comma'>,</span> <span class='symbol'>:pending?</span><span class='comma'>,</span> <span class='symbol'>:resolved?</span><span class='comma'>,</span> <span class='label'>of:</span> <span class='id identifier rubyid_event'>event</span><span class='rparen'>)</span>
|
||
<span class='comment'># => {:state=>:pending, :pending?=>true, :resolved?=>false}
|
||
</span>
|
||
<span class='id identifier rubyid_event'>event</span><span class='period'>.</span><span class='id identifier rubyid_resolve'>resolve</span>
|
||
<span class='id identifier rubyid_inspect_methods'>inspect_methods</span><span class='lparen'>(</span><span class='symbol'>:state</span><span class='comma'>,</span> <span class='symbol'>:pending?</span><span class='comma'>,</span> <span class='symbol'>:resolved?</span><span class='comma'>,</span> <span class='label'>of:</span> <span class='id identifier rubyid_event'>event</span><span class='rparen'>)</span>
|
||
<span class='comment'># => {:state=>:resolved, :pending?=>false, :resolved?=>true}
|
||
</span></code></pre>
|
||
|
||
<p>Future's <code>resolved</code> state is further specified to be <code>fulfilled</code> or <code>rejected</code>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_resolvable_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#resolvable_future-instance_method" title="Concurrent::Promises::FactoryMethods#resolvable_future (method)">resolvable_future</a></span></span>
|
||
<span class='id identifier rubyid_inspect_methods'>inspect_methods</span><span class='lparen'>(</span><span class='symbol'>:state</span><span class='comma'>,</span> <span class='symbol'>:pending?</span><span class='comma'>,</span> <span class='symbol'>:resolved?</span><span class='comma'>,</span> <span class='symbol'>:fulfilled?</span><span class='comma'>,</span> <span class='symbol'>:rejected?</span><span class='comma'>,</span>
|
||
<span class='label'>of:</span> <span class='id identifier rubyid_future'>future</span><span class='rparen'>)</span>
|
||
<span class='comment'># => {:state=>:pending,
|
||
</span><span class='comment'># :pending?=>true,
|
||
</span><span class='comment'># :resolved?=>false,
|
||
</span><span class='comment'># :fulfilled?=>false,
|
||
</span><span class='comment'># :rejected?=>false}
|
||
</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_fulfill'>fulfill</span> <span class='symbol'>:value</span>
|
||
<span class='id identifier rubyid_inspect_methods'>inspect_methods</span><span class='lparen'>(</span><span class='symbol'>:state</span><span class='comma'>,</span> <span class='symbol'>:pending?</span><span class='comma'>,</span> <span class='symbol'>:resolved?</span><span class='comma'>,</span> <span class='symbol'>:fulfilled?</span><span class='comma'>,</span> <span class='symbol'>:rejected?</span><span class='comma'>,</span>
|
||
<span class='symbol'>:result</span><span class='comma'>,</span> <span class='symbol'>:value</span><span class='comma'>,</span> <span class='symbol'>:reason</span><span class='comma'>,</span> <span class='label'>of:</span> <span class='id identifier rubyid_future'>future</span><span class='rparen'>)</span>
|
||
<span class='comment'># => {:state=>:fulfilled,
|
||
</span><span class='comment'># :pending?=>false,
|
||
</span><span class='comment'># :resolved?=>true,
|
||
</span><span class='comment'># :fulfilled?=>true,
|
||
</span><span class='comment'># :rejected?=>false,
|
||
</span><span class='comment'># :result=>[true, :value, nil],
|
||
</span><span class='comment'># :value=>:value,
|
||
</span><span class='comment'># :reason=>nil}
|
||
</span>
|
||
<span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_rejected_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#rejected_future-instance_method" title="Concurrent::Promises::FactoryMethods#rejected_future (method)">rejected_future</a></span></span> <span class='const'>StandardError</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span>
|
||
<span class='id identifier rubyid_inspect_methods'>inspect_methods</span><span class='lparen'>(</span><span class='symbol'>:state</span><span class='comma'>,</span> <span class='symbol'>:pending?</span><span class='comma'>,</span> <span class='symbol'>:resolved?</span><span class='comma'>,</span> <span class='symbol'>:fulfilled?</span><span class='comma'>,</span> <span class='symbol'>:rejected?</span><span class='comma'>,</span>
|
||
<span class='symbol'>:result</span><span class='comma'>,</span> <span class='symbol'>:value</span><span class='comma'>,</span> <span class='symbol'>:reason</span><span class='comma'>,</span> <span class='label'>of:</span> <span class='id identifier rubyid_future'>future</span><span class='rparen'>)</span>
|
||
<span class='comment'># => {:state=>:rejected,
|
||
</span><span class='comment'># :pending?=>false,
|
||
</span><span class='comment'># :resolved?=>true,
|
||
</span><span class='comment'># :fulfilled?=>false,
|
||
</span><span class='comment'># :rejected?=>true,
|
||
</span><span class='comment'># :result=>[false, nil, #<StandardError: StandardError>],
|
||
</span><span class='comment'># :value=>nil,
|
||
</span><span class='comment'># :reason=>#<StandardError: StandardError>}
|
||
</span></code></pre>
|
||
|
||
<h2>Direct creation of resolved futures</h2>
|
||
|
||
<p>When an existing value has to be wrapped in a future it does not have to go
|
||
through evaluation as follows.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.01</span><span class='semicolon'>;</span> <span class='symbol'>:value</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000007 pending>
|
||
</span></code></pre>
|
||
|
||
<p>Instead, it can be created directly as already-resolved:</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_fulfilled_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#fulfilled_future-instance_method" title="Concurrent::Promises::FactoryMethods#fulfilled_future (method)">fulfilled_future</a></span></span><span class='lparen'>(</span><span class='symbol'>:value</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000008 fulfilled>
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_rejected_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#rejected_future-instance_method" title="Concurrent::Promises::FactoryMethods#rejected_future (method)">rejected_future</a></span></span><span class='lparen'>(</span><span class='const'>StandardError</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Ups</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000009 rejected>
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_resolved_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#resolved_future-instance_method" title="Concurrent::Promises::FactoryMethods#resolved_future (method)">resolved_future</a></span></span><span class='lparen'>(</span><span class='kw'>true</span><span class='comma'>,</span> <span class='symbol'>:value</span><span class='comma'>,</span> <span class='kw'>nil</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x000000000000000a fulfilled>
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_resolved_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#resolved_future-instance_method" title="Concurrent::Promises::FactoryMethods#resolved_future (method)">resolved_future</a></span></span><span class='lparen'>(</span><span class='kw'>false</span><span class='comma'>,</span> <span class='kw'>nil</span><span class='comma'>,</span> <span class='const'>StandardError</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Ups</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x000000000000000b rejected>
|
||
</span></code></pre>
|
||
|
||
<h2>Chaining</h2>
|
||
|
||
<p>A big advantage of promises is the ability to chain tasks together without blocking
|
||
the current thread.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span> <span class='id identifier rubyid_v'>v</span><span class='period'>.</span><span class='id identifier rubyid_succ'>succ</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 4
|
||
</span></code></pre>
|
||
|
||
<p>As <code>future</code> factory method takes an argument, so does the <code>then</code> method. Any
|
||
supplied arguments are passed to the block, and the library ensures that they
|
||
are visible to the block.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>3</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_s'>s</span><span class='op'>|</span> <span class='id identifier rubyid_s'>s</span><span class='period'>.</span><span class='id identifier rubyid_to_i'>to_i</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='comma'>,</span> <span class='id identifier rubyid_arg'>arg</span><span class='op'>|</span> <span class='id identifier rubyid_v'>v</span> <span class='op'>+</span> <span class='id identifier rubyid_arg'>arg</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value'>value</span> <span class='comment'># => 5
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_fulfilled_future'>fulfilled_future</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>3</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:to_i</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='int'>2</span><span class='comma'>,</span> <span class='op'>&</span><span class='symbol'>:+</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value'>value</span> <span class='comment'># => 5
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_fulfilled_future'>fulfilled_future</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_chain'>chain</span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_fulfilled'>fulfilled</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_reason'>reason</span><span class='comma'>,</span> <span class='id identifier rubyid_arg'>arg</span><span class='op'>|</span> <span class='id identifier rubyid_value'>value</span> <span class='op'>+</span> <span class='id identifier rubyid_arg'>arg</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value'>value</span> <span class='comment'># => 3
|
||
</span></code></pre>
|
||
|
||
<p>Passing the arguments in (similarly as for a thread <code>Thread.new(arg) { |arg|
|
||
do_stuff arg }</code>) is <strong>required</strong>. Both of the following bad examples may break:</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_arg'>arg</span> <span class='op'>=</span> <span class='int'>1</span> <span class='comment'># => 1
|
||
</span><span class='const'>Thread</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_do_stuff'>do_stuff</span> <span class='id identifier rubyid_arg'>arg</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Thread:0x000000000000000c@promises.in.md:203 run>
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='id identifier rubyid_do_stuff'>do_stuff</span> <span class='id identifier rubyid_arg'>arg</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x000000000000000d pending>
|
||
</span></code></pre>
|
||
|
||
<p>Correct:</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_arg'>arg</span> <span class='op'>=</span> <span class='int'>1</span> <span class='comment'># => 1
|
||
</span><span class='const'>Thread</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='id identifier rubyid_arg'>arg</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_arg'>arg</span><span class='op'>|</span> <span class='id identifier rubyid_do_stuff'>do_stuff</span> <span class='id identifier rubyid_arg'>arg</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Thread:0x000000000000000e@promises.in.md:211 run>
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_arg'>arg</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_arg'>arg</span><span class='op'>|</span> <span class='id identifier rubyid_do_stuff'>do_stuff</span> <span class='id identifier rubyid_arg'>arg</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x000000000000000f pending>
|
||
</span></code></pre>
|
||
|
||
<h2>Branching, and zipping</h2>
|
||
|
||
<p>Besides chaining it can also be branched.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_head'>head</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_fulfilled_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#fulfilled_future-instance_method" title="Concurrent::Promises::FactoryMethods#fulfilled_future (method)">fulfilled_future</a></span></span> <span class='op'>-</span><span class='int'>1</span>
|
||
<span class='id identifier rubyid_branch1'>branch1</span> <span class='op'>=</span> <span class='id identifier rubyid_head'>head</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:abs</span><span class='rparen'>)</span>
|
||
<span class='id identifier rubyid_branch2'>branch2</span> <span class='op'>=</span> <span class='id identifier rubyid_head'>head</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span>
|
||
|
||
<span class='id identifier rubyid_branch1'>branch1</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 1
|
||
</span><span class='id identifier rubyid_branch2'>branch2</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 1
|
||
</span></code></pre>
|
||
|
||
<p>It can be combined back to one future by zipping (<code>zip</code>, <code>&</code>).</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_branch1'>branch1</span><span class='period'>.</span><span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span><span class='id identifier rubyid_branch2'>branch2</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => [1, 1]
|
||
</span><span class='lparen'>(</span><span class='id identifier rubyid_branch1'>branch1</span> <span class='op'>&</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_a'>a</span><span class='comma'>,</span> <span class='id identifier rubyid_b'>b</span><span class='op'>|</span> <span class='id identifier rubyid_a'>a</span> <span class='op'>+</span> <span class='id identifier rubyid_b'>b</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 2
|
||
</span><span class='lparen'>(</span><span class='id identifier rubyid_branch1'>branch1</span> <span class='op'>&</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:+</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 2
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span><span class='id identifier rubyid_branch1'>branch1</span><span class='comma'>,</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='comma'>,</span> <span class='id identifier rubyid_branch1'>branch1</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='op'>*</span><span class='id identifier rubyid_values'>values</span><span class='op'>|</span> <span class='id identifier rubyid_values'>values</span><span class='period'>.</span><span class='id identifier rubyid_reduce'>reduce</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:+</span><span class='rparen'>)</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 3
|
||
</span></code></pre>
|
||
|
||
<p>Instead of zipping only the first one can be taken, if needed.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_any'>any</span><span class='lparen'>(</span><span class='id identifier rubyid_branch1'>branch1</span><span class='comma'>,</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'><span class='object_link'><a href="Concurrent/Promises/Future.html#value!-instance_method" title="Concurrent::Promises::Future#value! (method)">value!</a></span></span>
|
||
<span class='comment'># => 1
|
||
</span><span class='lparen'>(</span><span class='id identifier rubyid_branch1'>branch1</span> <span class='op'>|</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 1
|
||
</span></code></pre>
|
||
|
||
<h2>Blocking methods</h2>
|
||
|
||
<p>In these examples we have used blocking methods like <code>value</code> extensively for
|
||
their convenience, however in practice is better to avoid them and continue
|
||
chaining.</p>
|
||
|
||
<p>If they need to be used (e.g. when integrating with threads), <code>value!</code> is a
|
||
better option over <code>value</code> when rejections are not dealt with differently.
|
||
Otherwise the rejections are not handled and probably silently forgotten.</p>
|
||
|
||
<h2>Error handling</h2>
|
||
|
||
<p>When a task in the chain fails, the rejection propagates down the
|
||
chain without executing the tasks created with <code>then</code>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_fulfilled_future'>fulfilled_future</span><span class='lparen'>(</span><span class='const'>Object</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_result'>result</span>
|
||
<span class='comment'># => [false,
|
||
</span><span class='comment'># nil,
|
||
</span><span class='comment'># #<NoMethodError: undefined method `succ' for #<Object:0x0000000000000010>>]
|
||
</span></code></pre>
|
||
|
||
<p>As <code>then</code> chained tasks execute only on fulfilled futures, there is a <code>rescue</code>
|
||
method which chains a task which is executed only when the future is rejected.
|
||
It can be used to recover from rejection.</p>
|
||
|
||
<p>Using rescue to fulfill to 0 instead of the error.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_fulfilled_future'>fulfilled_future</span><span class='lparen'>(</span><span class='const'>Object</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_rescue'>rescue</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_err'>err</span><span class='op'>|</span> <span class='int'>0</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_result'>result</span> <span class='comment'># => [true, 0, nil]
|
||
</span></code></pre>
|
||
|
||
<p>Rescue not executed when there is no rejection.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_fulfilled_future'>fulfilled_future</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_rescue'>rescue</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_e'>e</span><span class='op'>|</span> <span class='int'>0</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_result'>result</span> <span class='comment'># => [true, 3, nil]
|
||
</span></code></pre>
|
||
|
||
<p>Tasks added with <code>chain</code> are always evaluated.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_fulfilled_future'>fulfilled_future</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_chain'>chain</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_fulfilled'>fulfilled</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_reason'>reason</span><span class='op'>|</span> <span class='id identifier rubyid_fulfilled'>fulfilled</span> <span class='op'>?</span> <span class='id identifier rubyid_value'>value</span> <span class='op'>:</span> <span class='id identifier rubyid_reason'>reason</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 1
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_rejected_future'>rejected_future</span><span class='lparen'>(</span><span class='const'>StandardError</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Ups</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_chain'>chain</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_fulfilled'>fulfilled</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_reason'>reason</span><span class='op'>|</span> <span class='id identifier rubyid_fulfilled'>fulfilled</span> <span class='op'>?</span> <span class='id identifier rubyid_value'>value</span> <span class='op'>:</span> <span class='id identifier rubyid_reason'>reason</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => #<StandardError: Ups>
|
||
</span></code></pre>
|
||
|
||
<p>Zip is rejected if any of the zipped futures is.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_rejected_zip'>rejected_zip</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_fulfilled_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#fulfilled_future-instance_method" title="Concurrent::Promises::FactoryMethods#fulfilled_future (method)">fulfilled_future</a></span></span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span><span class='comma'>,</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_rejected_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#rejected_future-instance_method" title="Concurrent::Promises::FactoryMethods#rejected_future (method)">rejected_future</a></span></span><span class='lparen'>(</span><span class='const'>StandardError</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Ups</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='rparen'>)</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000011 rejected>
|
||
</span><span class='id identifier rubyid_rejected_zip'>rejected_zip</span><span class='period'>.</span><span class='id identifier rubyid_result'>result</span>
|
||
<span class='comment'># => [false, [1, nil], [nil, #<StandardError: Ups>]]
|
||
</span><span class='id identifier rubyid_rejected_zip'>rejected_zip</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_rescue'>rescue</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_reason1'>reason1</span><span class='comma'>,</span> <span class='id identifier rubyid_reason2'>reason2</span><span class='op'>|</span> <span class='lparen'>(</span><span class='id identifier rubyid_reason1'>reason1</span> <span class='op'>||</span> <span class='id identifier rubyid_reason2'>reason2</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_message'>message</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value'>value</span> <span class='comment'># => "Ups"
|
||
</span></code></pre>
|
||
|
||
<h2>Delayed futures</h2>
|
||
|
||
<p>Delayed futures will not evaluate until asked by <code>touch</code> or other method
|
||
requiring resolution. </p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_delay'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#delay-instance_method" title="Concurrent::Promises::FactoryMethods#delay (method)">delay</a></span></span> <span class='lbrace'>{</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span><span class='semicolon'>;</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>lazy</span><span class='tstring_end'>'</span></span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000012 pending>
|
||
</span><span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_resolved?'>resolved?</span> <span class='comment'># => false
|
||
</span><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_touch'>touch</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000012 pending>
|
||
</span><span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.2</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_resolved?'>resolved?</span> <span class='comment'># => true
|
||
</span></code></pre>
|
||
|
||
<p>All blocking methods like <code>wait</code>, <code>value</code> call <code>touch</code> and trigger evaluation.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_delay'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#delay-instance_method" title="Concurrent::Promises::FactoryMethods#delay (method)">delay</a></span></span> <span class='lbrace'>{</span> <span class='symbol'>:value</span> <span class='rbrace'>}</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span>
|
||
<span class='comment'># => :value
|
||
</span></code></pre>
|
||
|
||
<p>It propagates up through the chain, allowing whole or partial lazy chains.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_head'>head</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_delay'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#delay-instance_method" title="Concurrent::Promises::FactoryMethods#delay (method)">delay</a></span></span> <span class='lbrace'>{</span> <span class='int'>1</span> <span class='rbrace'>}</span>
|
||
<span class='id identifier rubyid_branch1'>branch1</span> <span class='op'>=</span> <span class='id identifier rubyid_head'>head</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span>
|
||
<span class='id identifier rubyid_branch2'>branch2</span> <span class='op'>=</span> <span class='id identifier rubyid_head'>head</span><span class='period'>.</span><span class='id identifier rubyid_delay'>delay</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span>
|
||
<span class='id identifier rubyid_join'>join</span> <span class='op'>=</span> <span class='id identifier rubyid_branch1'>branch1</span> <span class='op'>&</span> <span class='id identifier rubyid_branch2'>branch2</span>
|
||
|
||
<span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span>
|
||
</code></pre>
|
||
|
||
<p>Nothing resolves.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='lbracket'>[</span><span class='id identifier rubyid_head'>head</span><span class='comma'>,</span> <span class='id identifier rubyid_branch1'>branch1</span><span class='comma'>,</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='comma'>,</span> <span class='id identifier rubyid_join'>join</span><span class='rbracket'>]</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:resolved?</span><span class='rparen'>)</span>
|
||
<span class='comment'># => [false, false, false, false]
|
||
</span></code></pre>
|
||
|
||
<p>Force <code>branch1</code> evaluation.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_branch1'>branch1</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='comment'># => 2
|
||
</span><span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span>
|
||
<span class='lbracket'>[</span><span class='id identifier rubyid_head'>head</span><span class='comma'>,</span> <span class='id identifier rubyid_branch1'>branch1</span><span class='comma'>,</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='comma'>,</span> <span class='id identifier rubyid_join'>join</span><span class='rbracket'>]</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:resolved?</span><span class='rparen'>)</span>
|
||
<span class='comment'># => [true, true, false, false]
|
||
</span></code></pre>
|
||
|
||
<p>Force evaluation of both by calling <code>value</code> on <code>join</code>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_join'>join</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='comment'># => [2, 2]
|
||
</span><span class='lbracket'>[</span><span class='id identifier rubyid_head'>head</span><span class='comma'>,</span> <span class='id identifier rubyid_branch1'>branch1</span><span class='comma'>,</span> <span class='id identifier rubyid_branch2'>branch2</span><span class='comma'>,</span> <span class='id identifier rubyid_join'>join</span><span class='rbracket'>]</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:resolved?</span><span class='rparen'>)</span>
|
||
<span class='comment'># => [true, true, true, true]
|
||
</span></code></pre>
|
||
|
||
<h2>Flatting</h2>
|
||
|
||
<p>Sometimes it is needed to wait for an inner future. An apparent solution is to wait
|
||
inside the future <code>Concurrent::Promises.future { Concurrent::Promises.future { 1+1 }.value }.value</code>.
|
||
However, as mentioned before, <code>value</code> calls should be <strong>avoided</strong> to avoid
|
||
blocking threads. Therefore there is a <code>#flat</code> method which is a correct solution
|
||
in this situation and does not block any thread.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='int'>1</span><span class='op'>+</span><span class='int'>1</span> <span class='rbrace'>}</span> <span class='rbrace'>}</span><span class='period'>.</span><span class='id identifier rubyid_flat'>flat</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span>
|
||
<span class='comment'># => 2
|
||
</span></code></pre>
|
||
|
||
<p>A more complicated example.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_future'>future</span> <span class='lbrace'>{</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='int'>1</span> <span class='op'>+</span> <span class='int'>1</span> <span class='rbrace'>}</span> <span class='rbrace'>}</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_flat'>flat</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_future'>future</span><span class='op'>|</span> <span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_flat'>flat</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 3
|
||
</span></code></pre>
|
||
|
||
<h2>Scheduling</h2>
|
||
|
||
<p>Tasks can be planned to be executed with a time delay.</p>
|
||
|
||
<p>Schedule task to be executed in 0.1 seconds.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_scheduled'>scheduled</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_schedule'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#schedule-instance_method" title="Concurrent::Promises::FactoryMethods#schedule (method)">schedule</a></span></span><span class='lparen'>(</span><span class='float'>0.1</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='int'>1</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000013 pending>
|
||
</span><span class='id identifier rubyid_scheduled'>scheduled</span><span class='period'>.</span><span class='id identifier rubyid_resolved?'>resolved?</span> <span class='comment'># => false
|
||
</span></code></pre>
|
||
|
||
<p>Value will become available after 0.1 seconds. </p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_scheduled'>scheduled</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='comment'># => 1
|
||
</span></code></pre>
|
||
|
||
<p>It can be used in the chain as well, where the delay is counted from the moment
|
||
its parent resolves. Therefore, the following future will be resolved in 0.2 seconds.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_future'>future</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span><span class='semicolon'>;</span> <span class='symbol'>:result</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_schedule'>schedule</span><span class='lparen'>(</span><span class='float'>0.1</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:to_s</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => "result"
|
||
</span></code></pre>
|
||
|
||
<p>Time can be used as well.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_schedule'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#schedule-instance_method" title="Concurrent::Promises::FactoryMethods#schedule (method)">schedule</a></span></span><span class='lparen'>(</span><span class='const'>Time</span><span class='period'>.</span><span class='id identifier rubyid_now'>now</span> <span class='op'>+</span> <span class='int'>10</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='symbol'>:val</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000014 pending>
|
||
</span></code></pre>
|
||
|
||
<h2>Resolvable Future and Event:</h2>
|
||
|
||
<p>Sometimes it is required to resolve a future externally, in these cases
|
||
<code>resolvable_future</code> and <code>resolvable_event</code> factory methods can be used. See
|
||
<span class='object_link'><a href="Concurrent/Promises/ResolvableFuture.html" title="Concurrent::Promises::ResolvableFuture (class)">Concurrent::Promises::ResolvableFuture</a></span> and
|
||
<span class='object_link'><a href="Concurrent/Promises/ResolvableEvent.html" title="Concurrent::Promises::ResolvableEvent (class)">Concurrent::Promises::ResolvableEvent</a></span>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_resolvable_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#resolvable_future-instance_method" title="Concurrent::Promises::FactoryMethods#resolvable_future (method)">resolvable_future</a></span></span>
|
||
<span class='comment'># => #<Concurrent::Promises::ResolvableFuture:0x0000000000000015 pending>
|
||
</span></code></pre>
|
||
|
||
<p>The thread will be blocked until the future is resolved</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_thread'>thread</span> <span class='op'>=</span> <span class='const'>Thread</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='rbrace'>}</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_fulfill'>fulfill</span> <span class='int'>1</span>
|
||
<span class='comment'># => #<Concurrent::Promises::ResolvableFuture:0x0000000000000015 fulfilled>
|
||
</span><span class='id identifier rubyid_thread'>thread</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='comment'># => 1
|
||
</span></code></pre>
|
||
|
||
<p>A future can be resolved only once.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_fulfill'>fulfill</span> <span class='int'>1</span> <span class='kw'>rescue</span> <span class='gvar'>$!</span>
|
||
<span class='comment'># => #<Concurrent::MultipleAssignmentError: Future can be resolved only once. It's [true, 1, nil], trying to set [true, 1, nil]. {:current_result=>[true, 1, nil], :new_result=>[true, 1, nil]}>
|
||
</span><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_fulfill'>fulfill</span> <span class='int'>2</span><span class='comma'>,</span> <span class='kw'>false</span> <span class='comment'># => false
|
||
</span></code></pre>
|
||
|
||
<h2>How are promises executed?</h2>
|
||
|
||
<p>Promises use global pools to execute the tasks. Therefore each task may run on
|
||
different threads which implies that users have to be careful not to depend on
|
||
Thread-local variables (or they have to be set at the beginning of the task and
|
||
cleaned up at the end of the task).</p>
|
||
|
||
<p>Since the tasks are running on may different threads of the thread pool, it's
|
||
better to follow following rules:</p>
|
||
|
||
<ul>
|
||
<li> Use only data passed via arguments or values of parent futures, to
|
||
have better control over what are futures accessing.</li>
|
||
<li> The data passed in and out of futures is easier to deal with if it is
|
||
immutable or at least treated as such.</li>
|
||
<li> Any mutable and mutated object accessed by more than one thread or future
|
||
must be thread-safe, see <span class='object_link'><a href="Concurrent/Array.html" title="Concurrent::Array (class)">Concurrent::Array</a></span>, <span class='object_link'><a href="Concurrent/Hash.html" title="Concurrent::Hash (class)">Concurrent::Hash</a></span>, and
|
||
<span class='object_link'><a href="Concurrent/Map.html" title="Concurrent::Map (class)">Concurrent::Map</a></span>. (The value of a future may be consumed by many futures.)</li>
|
||
<li> Futures can access outside objects, but they have to be thread-safe.</li>
|
||
</ul>
|
||
|
||
<blockquote>
|
||
<p><em>TODO: This part to be extended</em></p>
|
||
</blockquote>
|
||
|
||
<h1>Advanced</h1>
|
||
|
||
<h2>Callbacks</h2>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_queue'>queue</span> <span class='op'>=</span> <span class='const'>Queue</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span> <span class='comment'># => #<Thread::Queue:0x0000000000000016>
|
||
</span><span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_delay'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#delay-instance_method" title="Concurrent::Promises::FactoryMethods#delay (method)">delay</a></span></span> <span class='lbrace'>{</span> <span class='int'>1</span> <span class='op'>+</span> <span class='int'>1</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000017 pending>
|
||
</span>
|
||
<span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_on_fulfillment'>on_fulfillment</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_queue'>queue</span> <span class='op'><<</span> <span class='int'>1</span> <span class='rbrace'>}</span> <span class='comment'># evaluated asynchronously
|
||
</span><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_on_fulfillment!'>on_fulfillment!</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_queue'>queue</span> <span class='op'><<</span> <span class='int'>2</span> <span class='rbrace'>}</span> <span class='comment'># evaluated on resolving thread
|
||
</span>
|
||
<span class='id identifier rubyid_queue'>queue</span><span class='period'>.</span><span class='id identifier rubyid_empty?'>empty?</span> <span class='comment'># => true
|
||
</span><span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_value'>value</span> <span class='comment'># => 2
|
||
</span><span class='id identifier rubyid_queue'>queue</span><span class='period'>.</span><span class='id identifier rubyid_pop'>pop</span> <span class='comment'># => 2
|
||
</span><span class='id identifier rubyid_queue'>queue</span><span class='period'>.</span><span class='id identifier rubyid_pop'>pop</span> <span class='comment'># => 1
|
||
</span></code></pre>
|
||
|
||
<h2>Using executors</h2>
|
||
|
||
<p>Factory methods, chain, and callback methods all have other versions of them
|
||
which takes an executor argument.</p>
|
||
|
||
<p>It takes an instance of an executor, or a symbol which is a shortcut for the
|
||
two global pools in concurrent-ruby. <code>:fast</code> for short and non-blocking tasks
|
||
and <code>:io</code> for long-running and blocking tasks.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future_on'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future_on-instance_method" title="Concurrent::Promises::FactoryMethods#future_on (method)">future_on</a></span></span><span class='lparen'>(</span><span class='symbol'>:fast</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='int'>2</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then_on'>then_on</span><span class='lparen'>(</span><span class='symbol'>:io</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='const'>File</span><span class='period'>.</span><span class='id identifier rubyid_read'>read</span> <span class='kw'>__FILE__</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value'>value</span><span class='period'>.</span><span class='id identifier rubyid_size'>size</span> <span class='comment'># => 27115
|
||
</span></code></pre>
|
||
|
||
<h2>Run (simulated process)</h2>
|
||
|
||
<p>Similar to flatting is running. When <code>run</code> is called on a future it will flat
|
||
indefinitely as long the future fulfils into a <code>Future</code> value. It can be used
|
||
to simulate a thread-like processing without actually occupying the thread.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_count'>count</span> <span class='op'>=</span> <span class='id identifier rubyid_lambda'>lambda</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_v'>v</span> <span class='op'>+=</span> <span class='int'>1</span>
|
||
<span class='id identifier rubyid_v'>v</span> <span class='op'><</span> <span class='int'>5</span> <span class='op'>?</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future_on'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future_on-instance_method" title="Concurrent::Promises::FactoryMethods#future_on (method)">future_on</a></span></span><span class='lparen'>(</span><span class='symbol'>:fast</span><span class='comma'>,</span> <span class='id identifier rubyid_v'>v</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_count'>count</span><span class='rparen'>)</span> <span class='op'>:</span> <span class='id identifier rubyid_v'>v</span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => #<Proc:0x0000000000000018@promises.in.md:520 (lambda)>
|
||
</span><span class='int'>400</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_map'>map</span> <span class='lbrace'>{</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future_on'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future_on-instance_method" title="Concurrent::Promises::FactoryMethods#future_on (method)">future_on</a></span></span><span class='lparen'>(</span><span class='symbol'>:fast</span><span class='comma'>,</span> <span class='int'>0</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_count'>count</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'><span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">run</a></span></span><span class='period'>.</span><span class='id identifier rubyid_value!'><span class='object_link'><a href="Concurrent/Promises/Future.html#value!-instance_method" title="Concurrent::Promises::Future#value! (method)">value!</a></span></span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_all?'>all?</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span> <span class='id identifier rubyid_v'>v</span> <span class='op'>==</span> <span class='int'>5</span> <span class='rbrace'>}</span> <span class='comment'># => true
|
||
</span></code></pre>
|
||
|
||
<p>Therefore the above example finished fine on the the <code>:fast</code> thread pool even
|
||
though it has much fewer threads than are simulated in the simulated process.</p>
|
||
|
||
<h1>Interoperability</h1>
|
||
|
||
<h2>Actors</h2>
|
||
|
||
<p>Create an actor which takes received numbers and returns the number squared. </p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_actor'>actor</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor.html" title="Concurrent::Actor (module)">Actor</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils.html" title="Concurrent::Actor::Utils (module)">Utils</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils/AdHoc.html" title="Concurrent::Actor::Utils::AdHoc (class)">AdHoc</a></span></span><span class='period'>.</span><span class='id identifier rubyid_spawn'><span class='object_link'><a href="Concurrent/Actor/AbstractContext.html#spawn-class_method" title="Concurrent::Actor::AbstractContext.spawn (method)">spawn</a></span></span> <span class='symbol'>:square</span> <span class='kw'>do</span>
|
||
<span class='tlambda'>-></span> <span class='id identifier rubyid_v'>v</span> <span class='tlambeg'>{</span> <span class='id identifier rubyid_v'>v</span> <span class='op'>**</span> <span class='int'>2</span> <span class='rbrace'>}</span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => #<Concurrent::Actor::Reference:0x0000000000000019 /square (Concurrent::Actor::Utils::AdHoc)>
|
||
</span></code></pre>
|
||
|
||
<p>Send result of <code>1+1</code> to the actor, and add 2 to the result sent back from the
|
||
actor.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_future'>future</span> <span class='lbrace'>{</span> <span class='int'>1</span> <span class='op'>+</span> <span class='int'>1</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then_ask'>then_ask</span><span class='lparen'>(</span><span class='id identifier rubyid_actor'>actor</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span> <span class='id identifier rubyid_v'>v</span> <span class='op'>+</span> <span class='int'>2</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 6
|
||
</span></code></pre>
|
||
|
||
<p>So <code>(1 + 1)**2 + 2 = 6</code>.</p>
|
||
|
||
<p>The <code>ask</code> method returns future.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_actor'>actor</span><span class='period'>.</span><span class='id identifier rubyid_ask'>ask</span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:succ</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 5
|
||
</span></code></pre>
|
||
|
||
<h2>Channel</h2>
|
||
|
||
<p>There is an implementation of channel as well. Let's start by creating a
|
||
channel with a capacity of 2 messages.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_ch1'>ch1</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/Channel.html" title="Concurrent::Promises::Channel (class)">Channel</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Concurrent/Synchronization/Object.html#new-class_method" title="Concurrent::Synchronization::Object.new (method)">new</a></span></span> <span class='int'>2</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Channel:0x000000000000001a size:2>
|
||
</span></code></pre>
|
||
|
||
<p>We push 3 messages, it can be observed that the last future representing the
|
||
push is not fulfilled since the capacity prevents it. When the work which fills
|
||
the channel depends on the futures created by push it can be used to create
|
||
backpressure – the filling work is delayed until the channel has space for
|
||
more messages.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_pushes'>pushes</span> <span class='op'>=</span> <span class='int'>3</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_i'>i</span><span class='op'>|</span> <span class='id identifier rubyid_ch1'>ch1</span><span class='period'>.</span><span class='id identifier rubyid_push'>push</span> <span class='id identifier rubyid_i'>i</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x000000000000001b fulfilled>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000001c fulfilled>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000001d pending>]
|
||
</span><span class='id identifier rubyid_ch1'>ch1</span><span class='period'>.</span><span class='id identifier rubyid_pop'>pop</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 0
|
||
</span><span class='id identifier rubyid_pushes'>pushes</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x000000000000001b fulfilled>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000001c fulfilled>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000001d fulfilled>]
|
||
</span></code></pre>
|
||
|
||
<p>A selection over channels can be created with the <code>.select_channel</code> factory method. It
|
||
will be fulfilled with a first message available in any of the channels. It
|
||
returns a pair to be able to find out which channel had the message available.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_ch2'>ch2</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/Channel.html" title="Concurrent::Promises::Channel (class)">Channel</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Concurrent/Synchronization/Object.html#new-class_method" title="Concurrent::Synchronization::Object.new (method)">new</a></span></span> <span class='int'>2</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Channel:0x000000000000001e size:2>
|
||
</span><span class='id identifier rubyid_result'>result</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_select_channel'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods/NewChannelIntegration.html#select_channel-instance_method" title="Concurrent::Promises::FactoryMethods::NewChannelIntegration#select_channel (method)">select_channel</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_ch1'>ch1</span><span class='comma'>,</span> <span class='id identifier rubyid_ch2'>ch2</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::Promises::ResolvableFuture:0x000000000000001f fulfilled>
|
||
</span><span class='id identifier rubyid_result'>result</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Channel:0x000000000000001a size:2>, 1]
|
||
</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='int'>1</span><span class='op'>+</span><span class='int'>1</span> <span class='rbrace'>}</span><span class='period'>.</span><span class='id identifier rubyid_then_push_channel'>then_push_channel</span><span class='lparen'>(</span><span class='id identifier rubyid_ch1'>ch1</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000020 pending>
|
||
</span><span class='id identifier rubyid_result'>result</span> <span class='op'>=</span> <span class='lparen'>(</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_fulfilled_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#fulfilled_future-instance_method" title="Concurrent::Promises::FactoryMethods#fulfilled_future (method)">fulfilled_future</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>%02d</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span> <span class='op'>&</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_select_channel'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods/NewChannelIntegration.html#select_channel-instance_method" title="Concurrent::Promises::FactoryMethods::NewChannelIntegration#select_channel (method)">select_channel</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_ch1'>ch1</span><span class='comma'>,</span> <span class='id identifier rubyid_ch2'>ch2</span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_then'>then</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_format'>format</span><span class='comma'>,</span> <span class='lparen'>(</span><span class='id identifier rubyid_channel'>channel</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='rparen'>)</span><span class='op'>|</span> <span class='id identifier rubyid_format'>format</span> <span class='id identifier rubyid_format'>format</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000021 pending>
|
||
</span><span class='id identifier rubyid_result'>result</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => "02"
|
||
</span></code></pre>
|
||
|
||
<h2>ProcessingActor</h2>
|
||
|
||
<p>There is also a new implementation of actors based on the Channel and the
|
||
ability of promises to simulate processes. The actor runs as a process but also
|
||
does not occupy a thread per actor as the previously-described Concurrent::Actor
|
||
implementation. This implementation is close to Erlang actors, therefore OTP
|
||
can be ported for this actors (and it's planned).</p>
|
||
|
||
<p>The simplest actor is one which just computes without even receiving a
|
||
message.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_actor'>actor</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/ProcessingActor.html" title="Concurrent::ProcessingActor (class)">ProcessingActor</a></span></span><span class='period'>.</span><span class='id identifier rubyid_act'><span class='object_link'><a href="Concurrent/ProcessingActor.html#act-class_method" title="Concurrent::ProcessingActor.act (method)">act</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_an_argument'>an_argument</span> <span class='op'>=</span> <span class='int'>2</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_actor'>actor</span><span class='comma'>,</span> <span class='id identifier rubyid_number'>number</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_number'>number</span> <span class='op'>**</span> <span class='int'>3</span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => #<Concurrent::ProcessingActor:0x0000000000000022 ... termination:pending>
|
||
</span><span class='id identifier rubyid_actor'>actor</span><span class='period'>.</span><span class='id identifier rubyid_termination'>termination</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 8
|
||
</span></code></pre>
|
||
|
||
<p>Let's receive some messages though.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_add_2_messages'>add_2_messages</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/ProcessingActor.html" title="Concurrent::ProcessingActor (class)">ProcessingActor</a></span></span><span class='period'>.</span><span class='id identifier rubyid_act'><span class='object_link'><a href="Concurrent/ProcessingActor.html#act-class_method" title="Concurrent::ProcessingActor.act (method)">act</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_actor'>actor</span><span class='op'>|</span>
|
||
<span class='comment'># Receive two messages then terminate normally with the sum.
|
||
</span> <span class='lparen'>(</span><span class='id identifier rubyid_actor'>actor</span><span class='period'>.</span><span class='id identifier rubyid_receive'>receive</span> <span class='op'>&</span> <span class='id identifier rubyid_actor'>actor</span><span class='period'>.</span><span class='id identifier rubyid_receive'>receive</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_a'>a</span><span class='comma'>,</span> <span class='id identifier rubyid_b'>b</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_a'>a</span> <span class='op'>+</span> <span class='id identifier rubyid_b'>b</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='id identifier rubyid_add_2_messages'>add_2_messages</span><span class='period'>.</span><span class='id identifier rubyid_tell'>tell</span> <span class='int'>1</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000025 pending>
|
||
</span><span class='id identifier rubyid_add_2_messages'>add_2_messages</span><span class='period'>.</span><span class='id identifier rubyid_termination'>termination</span><span class='period'>.</span><span class='id identifier rubyid_resolved?'>resolved?</span> <span class='comment'># => false
|
||
</span><span class='id identifier rubyid_add_2_messages'>add_2_messages</span><span class='period'>.</span><span class='id identifier rubyid_tell'>tell</span> <span class='int'>3</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x0000000000000026 pending>
|
||
</span><span class='id identifier rubyid_add_2_messages'>add_2_messages</span><span class='period'>.</span><span class='id identifier rubyid_termination'>termination</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 4
|
||
</span></code></pre>
|
||
|
||
<p>Actors can also be used to apply backpressure to a producer. Let's start by
|
||
defining an actor which a mailbox of size 2.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_slow_counter'>slow_counter</span> <span class='op'>=</span> <span class='tlambda'>-></span> <span class='lparen'>(</span><span class='id identifier rubyid_actor'>actor</span><span class='comma'>,</span> <span class='id identifier rubyid_count'>count</span><span class='rparen'>)</span> <span class='kw'>do</span>
|
||
<span class='id identifier rubyid_actor'>actor</span><span class='period'>.</span><span class='id identifier rubyid_receive'>receive</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_command'>command</span><span class='comma'>,</span> <span class='id identifier rubyid_number'>number</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span>
|
||
<span class='kw'>case</span> <span class='id identifier rubyid_command'>command</span>
|
||
<span class='kw'>when</span> <span class='symbol'>:add</span>
|
||
<span class='id identifier rubyid_slow_counter'>slow_counter</span><span class='period'>.</span><span class='id identifier rubyid_call'>call</span> <span class='id identifier rubyid_actor'>actor</span><span class='comma'>,</span> <span class='id identifier rubyid_count'>count</span> <span class='op'>+</span> <span class='id identifier rubyid_number'>number</span>
|
||
<span class='kw'>when</span> <span class='symbol'>:done</span>
|
||
<span class='comment'># terminate
|
||
</span> <span class='id identifier rubyid_count'>count</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_actor'>actor</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/ProcessingActor.html" title="Concurrent::ProcessingActor (class)">ProcessingActor</a></span></span><span class='period'>.</span><span class='id identifier rubyid_act_listening'><span class='object_link'><a href="Concurrent/ProcessingActor.html#act_listening-class_method" title="Concurrent::ProcessingActor.act_listening (method)">act_listening</a></span></span><span class='lparen'>(</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/Channel.html" title="Concurrent::Promises::Channel (class)">Channel</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Concurrent/Synchronization/Object.html#new-class_method" title="Concurrent::Synchronization::Object.new (method)">new</a></span></span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span><span class='comma'>,</span>
|
||
<span class='int'>0</span><span class='comma'>,</span>
|
||
<span class='op'>&</span><span class='id identifier rubyid_slow_counter'>slow_counter</span><span class='rparen'>)</span>
|
||
<span class='comment'># => #<Concurrent::ProcessingActor:0x0000000000000027 ... termination:pending>
|
||
</span></code></pre>
|
||
|
||
<p>Now we can create a producer which will push messages only when there is a
|
||
space available in the mailbox. We use promises to free a thread during waiting
|
||
on a free space in the mailbox.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_produce'>produce</span> <span class='op'>=</span> <span class='tlambda'>-></span> <span class='id identifier rubyid_receiver'>receiver</span><span class='comma'>,</span> <span class='id identifier rubyid_i'>i</span> <span class='kw'>do</span>
|
||
<span class='kw'>if</span> <span class='id identifier rubyid_i'>i</span> <span class='op'><</span> <span class='int'>10</span>
|
||
<span class='id identifier rubyid_receiver'>receiver</span><span class='period'>.</span>
|
||
<span class='comment'># send a message to the actor, resolves only after the message is
|
||
</span> <span class='comment'># accepted by the actor's mailbox
|
||
</span> <span class='id identifier rubyid_tell'>tell</span><span class='lparen'>(</span><span class='lbracket'>[</span><span class='symbol'>:add</span><span class='comma'>,</span> <span class='id identifier rubyid_i'>i</span><span class='rbracket'>]</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='comment'># send incremented message when the above message is accepted
|
||
</span> <span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='id identifier rubyid_i'>i</span><span class='op'>+</span><span class='int'>1</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_produce'>produce</span><span class='rparen'>)</span>
|
||
<span class='kw'>else</span>
|
||
<span class='id identifier rubyid_receiver'>receiver</span><span class='period'>.</span><span class='id identifier rubyid_tell'>tell</span><span class='lparen'>(</span><span class='symbol'>:done</span><span class='rparen'>)</span>
|
||
<span class='comment'># do not continue
|
||
</span> <span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_actor'>actor</span><span class='comma'>,</span> <span class='int'>0</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_produce'>produce</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'><span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">run</a></span></span><span class='period'>.</span><span class='id identifier rubyid_wait!'><span class='object_link'><a href="Concurrent/Promises/Future.html#wait!-instance_method" title="Concurrent::Promises::Future#wait! (method)">wait!</a></span></span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x000000000000002a fulfilled>
|
||
</span>
|
||
<span class='id identifier rubyid_actor'>actor</span><span class='period'>.</span><span class='id identifier rubyid_termination'>termination</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => 45
|
||
</span></code></pre>
|
||
|
||
<h1>Use-cases</h1>
|
||
|
||
<h2>Simple background processing</h2>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='lbrace'>{</span> <span class='id identifier rubyid_do_stuff'>do_stuff</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x000000000000002b pending>
|
||
</span></code></pre>
|
||
|
||
<h2>Parallel background processing</h2>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_tasks'>tasks</span> <span class='op'>=</span> <span class='int'>4</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_i'>i</span><span class='op'>|</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_i'>i</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_i'>i</span><span class='op'>|</span> <span class='id identifier rubyid_i'>i</span><span class='op'>*</span><span class='int'>2</span> <span class='rbrace'>}</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x000000000000002c pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000002d pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000002e pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000002f pending>]
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span><span class='op'>*</span><span class='id identifier rubyid_tasks'>tasks</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'><span class='object_link'><a href="Concurrent/Promises/Future.html#value!-instance_method" title="Concurrent::Promises::Future#value! (method)">value!</a></span></span>
|
||
<span class='comment'># => [0, 2, 4, 6]
|
||
</span></code></pre>
|
||
|
||
<h2>Actor background processing</h2>
|
||
|
||
<p>Actors are mainly keep and isolate state, they should stay responsive not being
|
||
blocked by a longer running computations. It desirable to offload the work to
|
||
stateless promises.</p>
|
||
|
||
<p>Lets define an actor which will process jobs, while staying responsive, and
|
||
tracking the number of tasks being processed.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>Computer</span> <span class='op'><</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor.html" title="Concurrent::Actor (module)">Actor</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/RestartingContext.html" title="Concurrent::Actor::RestartingContext (class)">RestartingContext</a></span></span>
|
||
<span class='kw'>def</span> <span class='id identifier rubyid_initialize'>initialize</span>
|
||
<span class='kw'>super</span><span class='lparen'>(</span><span class='rparen'>)</span>
|
||
<span class='ivar'>@jobs</span> <span class='op'>=</span> <span class='lbrace'>{</span><span class='rbrace'>}</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='kw'>def</span> <span class='id identifier rubyid_on_message'>on_message</span><span class='lparen'>(</span><span class='id identifier rubyid_msg'>msg</span><span class='rparen'>)</span>
|
||
<span class='id identifier rubyid_command'>command</span><span class='comma'>,</span> <span class='op'>*</span><span class='id identifier rubyid_args'>args</span> <span class='op'>=</span> <span class='id identifier rubyid_msg'>msg</span>
|
||
<span class='kw'>case</span> <span class='id identifier rubyid_command'>command</span>
|
||
<span class='comment'># new job to process
|
||
</span> <span class='kw'>when</span> <span class='symbol'>:run</span>
|
||
<span class='id identifier rubyid_job'>job</span> <span class='op'>=</span> <span class='id identifier rubyid_args'>args</span><span class='lbracket'>[</span><span class='int'>0</span><span class='rbracket'>]</span>
|
||
<span class='ivar'>@jobs</span><span class='lbracket'>[</span><span class='id identifier rubyid_job'>job</span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='id identifier rubyid_envelope'>envelope</span><span class='period'>.</span><span class='id identifier rubyid_future'>future</span>
|
||
<span class='comment'># Process asynchronously and send message back when done.
|
||
</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='op'>&</span><span class='id identifier rubyid_job'>job</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_chain'><span class='object_link'><a href="Concurrent/Promises/AbstractEventFuture.html#chain-instance_method" title="Concurrent::Promises::AbstractEventFuture#chain (method)">chain</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_job'>job</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_fulfilled'>fulfilled</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_reason'>reason</span><span class='comma'>,</span> <span class='id identifier rubyid_job'>job</span><span class='op'>|</span>
|
||
<span class='kw'>self</span><span class='period'>.</span><span class='id identifier rubyid_tell'>tell</span> <span class='lbracket'>[</span><span class='symbol'>:done</span><span class='comma'>,</span> <span class='id identifier rubyid_job'>job</span><span class='comma'>,</span> <span class='id identifier rubyid_fulfilled'>fulfilled</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_reason'>reason</span><span class='rbracket'>]</span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># Do not make return value of this method to be answer of this message.
|
||
</span> <span class='comment'># We are answering later in :done by resolving the future kept in @jobs.
|
||
</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor.html" title="Concurrent::Actor (module)">Actor</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Behaviour.html" title="Concurrent::Actor::Behaviour (module)">Behaviour</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Behaviour.html#MESSAGE_PROCESSED-constant" title="Concurrent::Actor::Behaviour::MESSAGE_PROCESSED (constant)">MESSAGE_PROCESSED</a></span></span>
|
||
<span class='kw'>when</span> <span class='symbol'>:done</span>
|
||
<span class='id identifier rubyid_job'>job</span><span class='comma'>,</span> <span class='id identifier rubyid_fulfilled'>fulfilled</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_reason'>reason</span> <span class='op'>=</span> <span class='op'>*</span><span class='id identifier rubyid_args'>args</span>
|
||
<span class='id identifier rubyid_future'>future</span> <span class='op'>=</span> <span class='ivar'>@jobs</span><span class='period'>.</span><span class='id identifier rubyid_delete'>delete</span> <span class='id identifier rubyid_job'>job</span>
|
||
<span class='comment'># Answer the job's result.
|
||
</span> <span class='id identifier rubyid_future'>future</span><span class='period'>.</span><span class='id identifier rubyid_resolve'>resolve</span> <span class='id identifier rubyid_fulfilled'>fulfilled</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_reason'>reason</span>
|
||
<span class='kw'>when</span> <span class='symbol'>:status</span>
|
||
<span class='lbrace'>{</span> <span class='label'>running_jobs:</span> <span class='ivar'>@jobs</span><span class='period'>.</span><span class='id identifier rubyid_size'>size</span> <span class='rbrace'>}</span>
|
||
<span class='kw'>else</span>
|
||
<span class='comment'># Continue to fail with unknown message.
|
||
</span> <span class='id identifier rubyid_pass'>pass</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
</code></pre>
|
||
|
||
<p>Create the computer actor and send it 3 jobs.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_computer'>computer</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor.html" title="Concurrent::Actor (module)">Actor</a></span></span><span class='period'>.</span><span class='id identifier rubyid_spawn'><span class='object_link'><a href="Concurrent/Actor.html#spawn-class_method" title="Concurrent::Actor.spawn (method)">spawn</a></span></span> <span class='const'>Computer</span><span class='comma'>,</span> <span class='symbol'>:computer</span>
|
||
<span class='comment'># => #<Concurrent::Actor::Reference:0x0000000000000030 /computer (Computer)>
|
||
</span><span class='id identifier rubyid_results'>results</span> <span class='op'>=</span> <span class='int'>3</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_computer'>computer</span><span class='period'>.</span><span class='id identifier rubyid_ask'>ask</span> <span class='lbracket'>[</span><span class='symbol'>:run</span><span class='comma'>,</span> <span class='tlambda'>-></span> <span class='tlambeg'>{</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span><span class='semicolon'>;</span> <span class='symbol'>:result</span> <span class='rbrace'>}</span><span class='rbracket'>]</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x0000000000000031 pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000032 pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000033 pending>]
|
||
</span><span class='id identifier rubyid_computer'>computer</span><span class='period'>.</span><span class='id identifier rubyid_ask'>ask</span><span class='lparen'>(</span><span class='symbol'>:status</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'>value!</span> <span class='comment'># => {:running_jobs=>3}
|
||
</span><span class='id identifier rubyid_results'>results</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:value!</span><span class='rparen'>)</span> <span class='comment'># => [:result, :result, :result]
|
||
</span></code></pre>
|
||
|
||
<h2>Solving the Thread count limit by thread simulation</h2>
|
||
|
||
<p>Sometimes an application requires to process a lot of tasks concurrently. If
|
||
the number of concurrent tasks is high enough than it is not possible to create
|
||
a Thread for each of them. A partially satisfactory solution could be to use
|
||
Fibers, but that solution locks the application on MRI since other Ruby
|
||
implementations are using threads for each Fiber.</p>
|
||
|
||
<p>This library provides a <span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">Concurrent::Promises::Future#run</a></span> method on a future
|
||
to simulate threads without actually accepting one all the time. The run method
|
||
is similar to <span class='object_link'><a href="Concurrent/Promises/Future.html#flat-instance_method" title="Concurrent::Promises::Future#flat (method)">Concurrent::Promises::Future#flat</a></span> but it will keep flattening
|
||
until it's fulfilled with non future value, then the value is taken as a result
|
||
of the process simulated by <code>run</code>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_body'>body</span> <span class='op'>=</span> <span class='id identifier rubyid_lambda'>lambda</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span>
|
||
<span class='comment'># Some computation step of the process
|
||
</span> <span class='id identifier rubyid_new_v'>new_v</span> <span class='op'>=</span> <span class='id identifier rubyid_v'>v</span> <span class='op'>+</span> <span class='int'>1</span>
|
||
<span class='comment'># Is the process finished?
|
||
</span> <span class='kw'>if</span> <span class='id identifier rubyid_new_v'>new_v</span> <span class='op'><</span> <span class='int'>5</span>
|
||
<span class='comment'># Continue computing with new value, does not have to be recursive.
|
||
</span> <span class='comment'># It just has to return a future.
|
||
</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_new_v'>new_v</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_body'>body</span><span class='rparen'>)</span>
|
||
<span class='kw'>else</span>
|
||
<span class='comment'># The process is finished, fulfill the final value with `new_v`.
|
||
</span> <span class='id identifier rubyid_new_v'>new_v</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='int'>0</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_body'>body</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'><span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">run</a></span></span><span class='period'>.</span><span class='id identifier rubyid_value!'><span class='object_link'><a href="Concurrent/Promises/Future.html#value!-instance_method" title="Concurrent::Promises::Future#value! (method)">value!</a></span></span> <span class='comment'># => 5
|
||
</span></code></pre>
|
||
|
||
<p>This solution works well an any Ruby implementation.</p>
|
||
|
||
<blockquote>
|
||
<p><em>TODO: More examples to be added.</em></p>
|
||
</blockquote>
|
||
|
||
<h2>Cancellation</h2>
|
||
|
||
<h3>Simple</h3>
|
||
|
||
<p>Lets have two processes which will count until cancelled.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Cancellation.html" title="Concurrent::Cancellation (class)">Cancellation</a></span></span><span class='period'>.</span><span class='id identifier rubyid_create'><span class='object_link'><a href="Concurrent/Cancellation.html#create-class_method" title="Concurrent::Cancellation.create (method)">create</a></span></span>
|
||
<span class='comment'># => [#<Concurrent::Cancellation:0x0000000000000034 canceled:false>,
|
||
</span><span class='comment'># #<Concurrent::Cancellation::Token:0x0000000000000035 canceled:false>]
|
||
</span>
|
||
<span class='id identifier rubyid_count_until_cancelled'>count_until_cancelled</span> <span class='op'>=</span> <span class='tlambda'>-></span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_count'>count</span> <span class='kw'>do</span>
|
||
<span class='kw'>if</span> <span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_canceled?'>canceled?</span>
|
||
<span class='id identifier rubyid_count'>count</span>
|
||
<span class='kw'>else</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_count'>count</span><span class='op'>+</span><span class='int'>1</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_count_until_cancelled'>count_until_cancelled</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_futures'>futures</span> <span class='op'>=</span> <span class='const'>Array</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span> <span class='kw'>do</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='int'>0</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_count_until_cancelled'>count_until_cancelled</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'><span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">run</a></span></span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x0000000000000036 pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000037 pending>]
|
||
</span>
|
||
<span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.01</span>
|
||
<span class='id identifier rubyid_source'>source</span><span class='period'>.</span><span class='id identifier rubyid_cancel'>cancel</span> <span class='comment'># => true
|
||
</span><span class='id identifier rubyid_futures'>futures</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:value!</span><span class='rparen'>)</span> <span class='comment'># => [68, 71]
|
||
</span></code></pre>
|
||
|
||
<p>Cancellation can also be used as event or future to log or plan re-execution.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_to_event'>to_event</span><span class='period'>.</span><span class='id identifier rubyid_chain'>chain</span> <span class='kw'>do</span>
|
||
<span class='comment'># log cancellation
|
||
</span> <span class='comment'># plane re-execution
|
||
</span><span class='kw'>end</span>
|
||
</code></pre>
|
||
|
||
<h3>Parallel background processing with cancellation</h3>
|
||
|
||
<p>Each task tries to count to 1000 but there is a randomly failing test. The
|
||
tasks share a cancellation, when one of them fails it cancels the others.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Cancellation.html" title="Concurrent::Cancellation (class)">Cancellation</a></span></span><span class='period'>.</span><span class='id identifier rubyid_create'><span class='object_link'><a href="Concurrent/Cancellation.html#create-class_method" title="Concurrent::Cancellation.create (method)">create</a></span></span>
|
||
<span class='comment'># => [#<Concurrent::Cancellation:0x0000000000000038 canceled:false>,
|
||
</span><span class='comment'># #<Concurrent::Cancellation::Token:0x0000000000000039 canceled:false>]
|
||
</span><span class='id identifier rubyid_tasks'>tasks</span> <span class='op'>=</span> <span class='int'>4</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_i'>i</span><span class='op'>|</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_i'>i</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_i'>i</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_count'>count</span> <span class='op'>=</span> <span class='int'>0</span>
|
||
<span class='int'>1000</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span> <span class='kw'>do</span>
|
||
<span class='kw'>break</span> <span class='id identifier rubyid_count'>count</span> <span class='op'>=</span> <span class='symbol'>:cancelled</span> <span class='kw'>if</span> <span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_canceled?'>canceled?</span>
|
||
<span class='id identifier rubyid_count'>count</span> <span class='op'>+=</span> <span class='int'>1</span>
|
||
<span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.01</span>
|
||
<span class='kw'>if</span> <span class='id identifier rubyid_rand'>rand</span> <span class='op'>></span> <span class='float'>0.95</span>
|
||
<span class='id identifier rubyid_source'>source</span><span class='period'>.</span><span class='id identifier rubyid_cancel'>cancel</span>
|
||
<span class='id identifier rubyid_raise'>raise</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>random error</span><span class='tstring_end'>'</span></span>
|
||
<span class='kw'>end</span>
|
||
<span class='id identifier rubyid_count'>count</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x000000000000003a pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000003b pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000003c pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x000000000000003d pending>]
|
||
</span><span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span><span class='op'>*</span><span class='id identifier rubyid_tasks'>tasks</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_result'><span class='object_link'><a href="Concurrent/Promises/Future.html#result-instance_method" title="Concurrent::Promises::Future#result (method)">result</a></span></span>
|
||
<span class='comment'># => [false,
|
||
</span><span class='comment'># [:cancelled, nil, :cancelled, :cancelled],
|
||
</span><span class='comment'># [nil, #<RuntimeError: random error>, nil, nil]]
|
||
</span></code></pre>
|
||
|
||
<p>Without the randomly failing part it produces following.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Cancellation.html" title="Concurrent::Cancellation (class)">Cancellation</a></span></span><span class='period'>.</span><span class='id identifier rubyid_create'><span class='object_link'><a href="Concurrent/Cancellation.html#create-class_method" title="Concurrent::Cancellation.create (method)">create</a></span></span>
|
||
<span class='comment'># => [#<Concurrent::Cancellation:0x000000000000003e canceled:false>,
|
||
</span><span class='comment'># #<Concurrent::Cancellation::Token:0x000000000000003f canceled:false>]
|
||
</span><span class='id identifier rubyid_tasks'>tasks</span> <span class='op'>=</span> <span class='int'>4</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_i'>i</span><span class='op'>|</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_i'>i</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_i'>i</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_count'>count</span> <span class='op'>=</span> <span class='int'>0</span>
|
||
<span class='int'>1000</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span> <span class='kw'>do</span>
|
||
<span class='kw'>break</span> <span class='id identifier rubyid_count'>count</span> <span class='op'>=</span> <span class='symbol'>:cancelled</span> <span class='kw'>if</span> <span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_canceled?'>canceled?</span>
|
||
<span class='id identifier rubyid_count'>count</span> <span class='op'>+=</span> <span class='int'>1</span>
|
||
<span class='comment'># sleep 0.01
|
||
</span> <span class='comment'># if rand > 0.95
|
||
</span> <span class='comment'># source.cancel
|
||
</span> <span class='comment'># raise 'random error'
|
||
</span> <span class='comment'># end
|
||
</span> <span class='kw'>end</span>
|
||
<span class='id identifier rubyid_count'>count</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span><span class='op'>*</span><span class='id identifier rubyid_tasks'>tasks</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_result'><span class='object_link'><a href="Concurrent/Promises/Future.html#result-instance_method" title="Concurrent::Promises::Future#result (method)">result</a></span></span>
|
||
<span class='comment'># => [true, [1000, 1000, 1000, 1000], nil]
|
||
</span></code></pre>
|
||
|
||
<h2>Throttling concurrency</h2>
|
||
|
||
<p>By creating an actor managing the resource we can control how many threads is
|
||
accessing the resource. In this case one at the time.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_data'>data</span> <span class='op'>=</span> <span class='const'>Array</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='int'>10</span><span class='rparen'>)</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_i'>i</span><span class='op'>|</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>*</span><span class='tstring_end'>'</span></span> <span class='op'>*</span> <span class='id identifier rubyid_i'>i</span> <span class='rbrace'>}</span>
|
||
<span class='comment'># => ["",
|
||
</span><span class='comment'># "*",
|
||
</span><span class='comment'># "**",
|
||
</span><span class='comment'># "***",
|
||
</span><span class='comment'># "****",
|
||
</span><span class='comment'># "*****",
|
||
</span><span class='comment'># "******",
|
||
</span><span class='comment'># "*******",
|
||
</span><span class='comment'># "********",
|
||
</span><span class='comment'># "*********"]
|
||
</span><span class='const'>DB</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor.html" title="Concurrent::Actor (module)">Actor</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils.html" title="Concurrent::Actor::Utils (module)">Utils</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils/AdHoc.html" title="Concurrent::Actor::Utils::AdHoc (class)">AdHoc</a></span></span><span class='period'>.</span><span class='id identifier rubyid_spawn'><span class='object_link'><a href="Concurrent/Actor/AbstractContext.html#spawn-class_method" title="Concurrent::Actor::AbstractContext.spawn (method)">spawn</a></span></span> <span class='symbol'>:db</span><span class='comma'>,</span> <span class='id identifier rubyid_data'>data</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_data'>data</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_lambda'>lambda</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_message'>message</span><span class='op'>|</span>
|
||
<span class='comment'># pretending that this queries a DB
|
||
</span> <span class='id identifier rubyid_data'>data</span><span class='lbracket'>[</span><span class='id identifier rubyid_message'>message</span><span class='rbracket'>]</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_concurrent_jobs'>concurrent_jobs</span> <span class='op'>=</span> <span class='int'>11</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span>
|
||
<span class='const'>DB</span><span class='period'>.</span>
|
||
<span class='comment'># ask the DB with the `v`, only one at the time, rest is parallel
|
||
</span> <span class='id identifier rubyid_ask'>ask</span><span class='lparen'>(</span><span class='id identifier rubyid_v'>v</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='comment'># get size of the string, rejects for 11
|
||
</span> <span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:size</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='comment'># translate error to a value (message of the exception)
|
||
</span> <span class='id identifier rubyid_rescue'>rescue</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_reason'>reason</span><span class='op'>|</span> <span class='id identifier rubyid_reason'>reason</span><span class='period'>.</span><span class='id identifier rubyid_message'>message</span> <span class='rbrace'>}</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span><span class='op'>*</span><span class='id identifier rubyid_concurrent_jobs'>concurrent_jobs</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'><span class='object_link'><a href="Concurrent/Promises/Future.html#value!-instance_method" title="Concurrent::Promises::Future#value! (method)">value!</a></span></span>
|
||
<span class='comment'># => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "undefined method `size' for nil:NilClass"]
|
||
</span></code></pre>
|
||
|
||
<p>Often there is more then one DB connections, then the pool can be used.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_pool_size'>pool_size</span> <span class='op'>=</span> <span class='int'>5</span> <span class='comment'># => 5
|
||
</span>
|
||
<span class='const'>DB_POOL</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor.html" title="Concurrent::Actor (module)">Actor</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils.html" title="Concurrent::Actor::Utils (module)">Utils</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils/Pool.html" title="Concurrent::Actor::Utils::Pool (class)">Pool</a></span></span><span class='period'>.</span><span class='id identifier rubyid_spawn!'><span class='object_link'><a href="Concurrent/Actor/AbstractContext.html#spawn!-class_method" title="Concurrent::Actor::AbstractContext.spawn! (method)">spawn!</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>DB-pool</span><span class='tstring_end'>'</span></span><span class='comma'>,</span> <span class='id identifier rubyid_pool_size'>pool_size</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_index'>index</span><span class='op'>|</span>
|
||
<span class='comment'># DB connection constructor
|
||
</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor.html" title="Concurrent::Actor (module)">Actor</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils.html" title="Concurrent::Actor::Utils (module)">Utils</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Actor/Utils/AdHoc.html" title="Concurrent::Actor::Utils::AdHoc (class)">AdHoc</a></span></span><span class='period'>.</span><span class='id identifier rubyid_spawn'><span class='object_link'><a href="Concurrent/Actor/AbstractContext.html#spawn-class_method" title="Concurrent::Actor::AbstractContext.spawn (method)">spawn</a></span></span><span class='lparen'>(</span>
|
||
<span class='label'>name:</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>connection-</span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_index'>index</span><span class='embexpr_end'>}</span><span class='tstring_end'>"</span></span><span class='comma'>,</span>
|
||
<span class='label'>args:</span> <span class='lbracket'>[</span><span class='id identifier rubyid_data'>data</span><span class='rbracket'>]</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_data'>data</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_lambda'>lambda</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_message'>message</span><span class='op'>|</span>
|
||
<span class='comment'># pretending that this queries a DB
|
||
</span> <span class='id identifier rubyid_data'>data</span><span class='lbracket'>[</span><span class='id identifier rubyid_message'>message</span><span class='rbracket'>]</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_concurrent_jobs'>concurrent_jobs</span> <span class='op'>=</span> <span class='int'>11</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_v'>v</span><span class='op'>|</span>
|
||
<span class='const'>DB_POOL</span><span class='period'>.</span>
|
||
<span class='comment'># ask the DB with the `v`, only one at the time, rest is parallel
|
||
</span> <span class='id identifier rubyid_ask'>ask</span><span class='lparen'>(</span><span class='id identifier rubyid_v'>v</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='comment'># get size of the string, rejects for 11
|
||
</span> <span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:size</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='comment'># translate error to a value (message of the exception)
|
||
</span> <span class='id identifier rubyid_rescue'>rescue</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_reason'>reason</span><span class='op'>|</span> <span class='id identifier rubyid_reason'>reason</span><span class='period'>.</span><span class='id identifier rubyid_message'>message</span> <span class='rbrace'>}</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_zip'>zip</span><span class='lparen'>(</span><span class='op'>*</span><span class='id identifier rubyid_concurrent_jobs'>concurrent_jobs</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_value!'><span class='object_link'><a href="Concurrent/Promises/Future.html#value!-instance_method" title="Concurrent::Promises::Future#value! (method)">value!</a></span></span>
|
||
<span class='comment'># => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "undefined method `size' for nil:NilClass"]
|
||
</span></code></pre>
|
||
|
||
<p>In other cases the DB adapter maintains its internal connection pool and we
|
||
just need to limit concurrent access to the DB's API to avoid the calls being
|
||
blocked.</p>
|
||
|
||
<p>Lets pretend that the <code>#[]</code> method on <code>DB_INTERNAL_POOL</code> is using the internal
|
||
pool of size 3. We create throttle with the same size</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='const'>DB_INTERNAL_POOL</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Array.html" title="Concurrent::Array (class)">Array</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span> <span class='id identifier rubyid_data'>data</span>
|
||
<span class='comment'># => ["",
|
||
</span><span class='comment'># "*",
|
||
</span><span class='comment'># "**",
|
||
</span><span class='comment'># "***",
|
||
</span><span class='comment'># "****",
|
||
</span><span class='comment'># "*****",
|
||
</span><span class='comment'># "******",
|
||
</span><span class='comment'># "*******",
|
||
</span><span class='comment'># "********",
|
||
</span><span class='comment'># "*********"]
|
||
</span>
|
||
<span class='id identifier rubyid_max_tree'>max_tree</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Throttle.html" title="Concurrent::Throttle (class)">Throttle</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Concurrent/Synchronization/Object.html#new-class_method" title="Concurrent::Synchronization::Object.new (method)">new</a></span></span> <span class='int'>3</span>
|
||
<span class='comment'># => #<Concurrent::Throttle:0x0000000000000040 limit:3 can_run:3>
|
||
</span>
|
||
<span class='id identifier rubyid_futures'>futures</span> <span class='op'>=</span> <span class='int'>11</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_i'>i</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_max_tree'>max_tree</span><span class='period'>.</span>
|
||
<span class='comment'># throttled tasks, at most 3 simultaneous calls of [] on the database
|
||
</span> <span class='id identifier rubyid_throttled_future'>throttled_future</span> <span class='lbrace'>{</span> <span class='const'>DB_INTERNAL_POOL</span><span class='lbracket'>[</span><span class='id identifier rubyid_i'>i</span><span class='rbracket'>]</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='comment'># un-throttled tasks, unlimited concurrency
|
||
</span> <span class='id identifier rubyid_then'>then</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_starts'>starts</span><span class='op'>|</span> <span class='id identifier rubyid_starts'>starts</span><span class='period'>.</span><span class='id identifier rubyid_size'>size</span> <span class='rbrace'>}</span><span class='period'>.</span>
|
||
<span class='id identifier rubyid_rescue'>rescue</span> <span class='lbrace'>{</span> <span class='op'>|</span><span class='id identifier rubyid_reason'>reason</span><span class='op'>|</span> <span class='id identifier rubyid_reason'>reason</span><span class='period'>.</span><span class='id identifier rubyid_message'>message</span> <span class='rbrace'>}</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_futures'>futures</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:value!</span><span class='rparen'>)</span>
|
||
<span class='comment'># => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "undefined method `size' for nil:NilClass"]
|
||
</span></code></pre>
|
||
|
||
<h2>Long stream of tasks, applying backpressure</h2>
|
||
|
||
<p>Let's assume that we are querying an API for data and the queries can be faster
|
||
than we are able to process them. This example shows how to use channel as a
|
||
buffer and how to apply backpressure to slow down the queries. </p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>json</span><span class='tstring_end'>'</span></span>
|
||
|
||
<span class='id identifier rubyid_channel'>channel</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises/Channel.html" title="Concurrent::Promises::Channel (class)">Channel</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Concurrent/Synchronization/Object.html#new-class_method" title="Concurrent::Synchronization::Object.new (method)">new</a></span></span> <span class='int'>6</span>
|
||
<span class='comment'># => #<Concurrent::Promises::Channel:0x0000000000000041 size:6>
|
||
</span><span class='id identifier rubyid_source'>source</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Cancellation.html" title="Concurrent::Cancellation (class)">Cancellation</a></span></span><span class='period'>.</span><span class='id identifier rubyid_create'><span class='object_link'><a href="Concurrent/Cancellation.html#create-class_method" title="Concurrent::Cancellation.create (method)">create</a></span></span>
|
||
<span class='comment'># => [#<Concurrent::Cancellation:0x0000000000000042 canceled:false>,
|
||
</span><span class='comment'># #<Concurrent::Cancellation::Token:0x0000000000000043 canceled:false>]
|
||
</span>
|
||
<span class='kw'>def</span> <span class='id identifier rubyid_query_random_text'>query_random_text</span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_channel'>channel</span><span class='rparen'>)</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span> <span class='kw'>do</span>
|
||
<span class='comment'># for simplicity the query is omitted
|
||
</span> <span class='comment'># url = 'some api'
|
||
</span> <span class='comment'># Net::HTTP.get(URI(url))
|
||
</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.1</span>
|
||
<span class='lbrace'>{</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>message</span><span class='tstring_end'>'</span></span> <span class='op'>=></span>
|
||
<span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Lorem ipsum rhoncus scelerisque vulputate diam inceptos</span><span class='tstring_end'>'</span></span>
|
||
<span class='rbrace'>}</span><span class='period'>.</span><span class='id identifier rubyid_to_json'>to_json</span>
|
||
<span class='kw'>end</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='op'>|</span>
|
||
<span class='comment'># The push to channel is fulfilled only after the message is successfully
|
||
</span> <span class='comment'># published to the channel, therefore it will not continue querying until
|
||
</span> <span class='comment'># current message is pushed.
|
||
</span> <span class='id identifier rubyid_channel'>channel</span><span class='period'>.</span><span class='id identifier rubyid_push'>push</span><span class='lparen'>(</span><span class='id identifier rubyid_value'>value</span><span class='rparen'>)</span> <span class='op'>|</span>
|
||
<span class='comment'># It could wait on the push indefinitely if the token is not checked
|
||
</span> <span class='comment'># here with `or` (the pipe).
|
||
</span> <span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_to_future'>to_future</span>
|
||
<span class='kw'>end</span><span class='period'>.</span><span class='id identifier rubyid_flat_future'>flat_future</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid__'>_</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='op'>|</span>
|
||
<span class='comment'># query again after the message is pushed to buffer
|
||
</span> <span class='id identifier rubyid_query_random_text'>query_random_text</span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_channel'>channel</span><span class='rparen'>)</span> <span class='kw'>unless</span> <span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_canceled?'>canceled?</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_words'>words</span> <span class='op'>=</span> <span class='lbracket'>[</span><span class='rbracket'>]</span> <span class='comment'># => []
|
||
</span><span class='id identifier rubyid_words_throttle'>words_throttle</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Throttle.html" title="Concurrent::Throttle (class)">Throttle</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Concurrent/Synchronization/Object.html#new-class_method" title="Concurrent::Synchronization::Object.new (method)">new</a></span></span> <span class='int'>1</span>
|
||
<span class='comment'># => #<Concurrent::Throttle:0x0000000000000044 limit:1 can_run:1>
|
||
</span>
|
||
<span class='kw'>def</span> <span class='id identifier rubyid_count_words_in_random_text'>count_words_in_random_text</span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_channel'>channel</span><span class='comma'>,</span> <span class='id identifier rubyid_words'>words</span><span class='comma'>,</span> <span class='id identifier rubyid_words_throttle'>words_throttle</span><span class='rparen'>)</span>
|
||
<span class='id identifier rubyid_channel'>channel</span><span class='period'>.</span><span class='id identifier rubyid_pop'>pop</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_response'>response</span><span class='op'>|</span>
|
||
<span class='id identifier rubyid_string'>string</span> <span class='op'>=</span> <span class='const'>JSON</span><span class='period'>.</span><span class='id identifier rubyid_load'>load</span><span class='lparen'>(</span><span class='id identifier rubyid_response'>response</span><span class='rparen'>)</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>message</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span>
|
||
<span class='comment'># processing is slower than querying
|
||
</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.2</span>
|
||
<span class='id identifier rubyid_words_count'>words_count</span> <span class='op'>=</span> <span class='id identifier rubyid_string'>string</span><span class='period'>.</span><span class='id identifier rubyid_scan'>scan</span><span class='lparen'>(</span><span class='tstring'><span class='regexp_beg'>/</span><span class='tstring_content'>\w+</span><span class='regexp_end'>/</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_size'>size</span>
|
||
<span class='kw'>end</span><span class='period'>.</span><span class='id identifier rubyid_then_throttled_by'>then_throttled_by</span><span class='lparen'>(</span><span class='id identifier rubyid_words_throttle'>words_throttle</span><span class='comma'>,</span> <span class='id identifier rubyid_words'>words</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_words_count'>words_count</span><span class='comma'>,</span> <span class='id identifier rubyid_words'>words</span><span class='op'>|</span>
|
||
<span class='comment'># safe since throttled to only 1 task at a time
|
||
</span> <span class='id identifier rubyid_words'>words</span> <span class='op'><<</span> <span class='id identifier rubyid_words_count'>words_count</span>
|
||
<span class='kw'>end</span><span class='period'>.</span><span class='id identifier rubyid_then'>then</span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='rparen'>)</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid__'>_</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='op'>|</span>
|
||
<span class='comment'># count words in next message
|
||
</span> <span class='kw'>unless</span> <span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_canceled?'>canceled?</span>
|
||
<span class='id identifier rubyid_count_words_in_random_text'>count_words_in_random_text</span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_channel'>channel</span><span class='comma'>,</span> <span class='id identifier rubyid_words'>words</span><span class='comma'>,</span> <span class='id identifier rubyid_words_throttle'>words_throttle</span><span class='rparen'>)</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_query_processes'>query_processes</span> <span class='op'>=</span> <span class='int'>3</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_channel'>channel</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_method'>method</span><span class='lparen'>(</span><span class='symbol'>:query_random_text</span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'><span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">run</a></span></span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x0000000000000045 pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000046 pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000047 pending>]
|
||
</span>
|
||
<span class='id identifier rubyid_word_counter_processes'>word_counter_processes</span> <span class='op'>=</span> <span class='int'>2</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_channel'>channel</span><span class='comma'>,</span> <span class='id identifier rubyid_words'>words</span><span class='comma'>,</span> <span class='id identifier rubyid_words_throttle'>words_throttle</span><span class='comma'>,</span>
|
||
<span class='op'>&</span><span class='id identifier rubyid_method'>method</span><span class='lparen'>(</span><span class='symbol'>:count_words_in_random_text</span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'><span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">run</a></span></span>
|
||
<span class='kw'>end</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x0000000000000048 pending>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000049 pending>]
|
||
</span>
|
||
<span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.5</span>
|
||
</code></pre>
|
||
|
||
<p>Let it run for a while, then cancel it, and ensure that the runs were all fulfilled
|
||
(therefore ended) after the cancellation. Finally, print the result.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_source'>source</span><span class='period'>.</span><span class='id identifier rubyid_cancel'>cancel</span> <span class='comment'># => true
|
||
</span><span class='id identifier rubyid_query_processes'>query_processes</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:wait!</span><span class='rparen'>)</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x0000000000000045 fulfilled>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000046 fulfilled>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000047 fulfilled>]
|
||
</span><span class='id identifier rubyid_word_counter_processes'>word_counter_processes</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:wait!</span><span class='rparen'>)</span>
|
||
<span class='comment'># => [#<Concurrent::Promises::Future:0x0000000000000048 fulfilled>,
|
||
</span><span class='comment'># #<Concurrent::Promises::Future:0x0000000000000049 fulfilled>]
|
||
</span><span class='id identifier rubyid_words'>words</span> <span class='comment'># => [7, 7, 7, 7]
|
||
</span></code></pre>
|
||
|
||
<p>Compared to using threads directly, this is highly configurable and composable
|
||
solution.</p>
|
||
|
||
<h2>Periodic task</h2>
|
||
|
||
<p>A periodically executed task can be creating by combining <code>schedule</code>, <code>run</code> and <code>Cancellation</code>.</p>
|
||
|
||
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_repeating_scheduled_task'>repeating_scheduled_task</span> <span class='op'>=</span> <span class='tlambda'>-></span> <span class='id identifier rubyid_interval'>interval</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_task'>task</span> <span class='kw'>do</span>
|
||
<span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span>
|
||
<span class='comment'># Schedule the task.
|
||
</span> <span class='id identifier rubyid_schedule'>schedule</span><span class='lparen'>(</span><span class='id identifier rubyid_interval'>interval</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_task'>task</span><span class='rparen'>)</span><span class='period'>.</span>
|
||
<span class='comment'># If successful schedule again.
|
||
</span> <span class='comment'># Alternatively use chain to schedule always.
|
||
</span> <span class='id identifier rubyid_then'>then</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_repeating_scheduled_task'>repeating_scheduled_task</span><span class='period'>.</span><span class='id identifier rubyid_call'>call</span><span class='lparen'>(</span><span class='id identifier rubyid_interval'>interval</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_task'>task</span><span class='rparen'>)</span> <span class='rbrace'>}</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_cancellation'>cancellation</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Cancellation.html" title="Concurrent::Cancellation (class)">Cancellation</a></span></span><span class='period'>.</span><span class='id identifier rubyid_create'><span class='object_link'><a href="Concurrent/Cancellation.html#create-class_method" title="Concurrent::Cancellation.create (method)">create</a></span></span>
|
||
<span class='comment'># => [#<Concurrent::Cancellation:0x000000000000004a canceled:false>,
|
||
</span><span class='comment'># #<Concurrent::Cancellation::Token:0x000000000000004b canceled:false>]
|
||
</span>
|
||
<span class='id identifier rubyid_task'>task</span> <span class='op'>=</span> <span class='tlambda'>-></span> <span class='id identifier rubyid_token'>token</span> <span class='kw'>do</span>
|
||
<span class='int'>5</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span> <span class='kw'>do</span>
|
||
<span class='id identifier rubyid_token'>token</span><span class='period'>.</span><span class='id identifier rubyid_raise_if_canceled'>raise_if_canceled</span>
|
||
<span class='comment'># do stuff
|
||
</span> <span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.01</span>
|
||
<span class='kw'>end</span>
|
||
<span class='kw'>end</span>
|
||
|
||
<span class='id identifier rubyid_result'>result</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Concurrent.html" title="Concurrent (module)">Concurrent</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Concurrent/Promises.html" title="Concurrent::Promises (module)">Promises</a></span></span><span class='period'>.</span><span class='id identifier rubyid_future'><span class='object_link'><a href="Concurrent/Promises/FactoryMethods.html#future-instance_method" title="Concurrent::Promises::FactoryMethods#future (method)">future</a></span></span><span class='lparen'>(</span><span class='float'>0.1</span><span class='comma'>,</span> <span class='id identifier rubyid_token'>token</span><span class='comma'>,</span> <span class='id identifier rubyid_task'>task</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_repeating_scheduled_task'>repeating_scheduled_task</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'><span class='object_link'><a href="Concurrent/Promises/Future.html#run-instance_method" title="Concurrent::Promises::Future#run (method)">run</a></span></span>
|
||
<span class='comment'># => #<Concurrent::Promises::Future:0x000000000000004c pending>
|
||
</span><span class='id identifier rubyid_sleep'>sleep</span> <span class='float'>0.2</span>
|
||
<span class='id identifier rubyid_cancellation'>cancellation</span><span class='period'>.</span><span class='id identifier rubyid_cancel'>cancel</span> <span class='comment'># => true
|
||
</span><span class='id identifier rubyid_result'>result</span><span class='period'>.</span><span class='id identifier rubyid_result'>result</span>
|
||
<span class='comment'># => [false,
|
||
</span><span class='comment'># nil,
|
||
</span><span class='comment'># #<Concurrent::CancelledOperationError: Concurrent::CancelledOperationError>]
|
||
</span></code></pre>
|
||
</div></div>
|
||
|
||
<div id="footer">
|
||
Generated by <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_blank">yard</a>.
|
||
</div>
|
||
|
||
<script>
|
||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||
|
||
ga('create', 'UA-57940973-1', 'auto');
|
||
ga('send', 'pageview');
|
||
|
||
</script>
|
||
|
||
</div>
|
||
</body>
|
||
</html> |