├── .gitignore ├── LICENSE ├── README.md ├── doc ├── edoc-info ├── erlang.png ├── index.html ├── modules-frame.html ├── overview-summary.html ├── packages-frame.html ├── stylesheet.css └── task.html ├── rebar ├── rebar.config ├── src ├── task.app.src └── task.erl └── test └── task_test.erl /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | deps 4 | *.beam 5 | *.dump 6 | ebin 7 | .rebar 8 | .eunit 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, redink 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # task 2 | `Task` is an [Elixir Task](http://elixir-lang.org/getting_started/mix_otp/8.html#8.2-tasks) model partial implement in Erlang. 3 | 4 | Detailed doc, [here](https://github.com/elixir-lang/elixir/blob/v1.0.1/lib/elixir/lib/task.ex). 5 | 6 | ## async and await 7 | 8 | In this repo, the way to spawn a task is with `task:async/3`. Then, a new process will be created, linked and monitored by the caller(that is the task owner). Once the task action finishes, a message will be sent to the caller with the result. 9 | 10 | `task:await/1` and `task:await/2` is used to read the message sent bt the task process. On `await`, `Task` will also setup a monitor to verify if the process exited for any abnormal reason(or in case exits are being trapped by the caller). 11 | 12 | ## usage 13 | ### compile and eunit 14 | $ ./rebar com ; ./rebar eunit -v 15 | ==> task (compile) 16 | ==> task (eunit) 17 | INFO: sh info: 18 | cwd: "/Users/redink/github/task" 19 | cmd: cp -R src/task.erl test/task_test.erl ".eunit" 20 | INFO: Skipped src/task.erl 21 | Compiled test/task_test.erl 22 | INFO: Cover compiling /Users/redink/github/task 23 | ======================== EUnit ======================== 24 | module 'task_test' 25 | task_test: task_test_ (async/1)...ok 26 | task_test: task_test_ (async/2)...[0.010 s] ok 27 | task_test: task_test_ (async/3)...ok 28 | task_test: task_test_ (async/4)...ok 29 | task_test: task_test_ (async_opt/2)...ok 30 | task_test: task_test_ (async_opt/3)...ok 31 | task_test: task_test_ (async_opt/4)...ok 32 | task_test: task_test_ (async_opt/5)...ok 33 | task_test: task_test_ (await/1 exits on timeout)...ok 34 | task_test: task_test_ (await/1 exits on normal exit)...ok 35 | task_test: task_test_ (await/1 exits on task throw)... 36 | =ERROR REPORT==== 13-Jan-2015::17:24:41 === 37 | ** Task <0.84.0> terminating 38 | ** Started from <0.57.0> 39 | ** When function == #Fun 40 | ** arguments == [unknown] 41 | ** Reason for termination == 42 | ** {{nocatch,unknown}, 43 | [{task,do_apply,2,[{file,"src/task.erl"},{line,154}]}, 44 | {task,async_do,3,[{file,"src/task.erl"},{line,134}]}, 45 | {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,237}]}]} 46 | 47 | =ERROR REPORT==== 13-Jan-2015::17:24:41 === 48 | ** Task <0.86.0> terminating 49 | ** Started from <0.57.0> 50 | ** When function == #Fun 51 | ** arguments == [unknown] 52 | ** Reason for termination == 53 | ** unknown 54 | ok 55 | task_test: task_test_ (await/1 exits on task exit)...ok 56 | task_test: task_test_ (await/1 exits on noconnection)...ok 57 | task_test: task_test_ (safe_await/2 exits on noconnection)... 58 | =ERROR REPORT==== 13-Jan-2015::17:24:41 === 59 | ** Task <0.90.0> terminating 60 | ** Started from <0.57.0> 61 | ** When function == #Fun 62 | ** arguments == [unknown] 63 | ** Reason for termination == 64 | ** unknown 65 | ok 66 | task_test: task_test_ (safe_await/2 exits on task exit)...ok 67 | [done in 0.054 s] 68 | module 'task' 69 | ======================================================= 70 | All 15 tests passed. 71 | Cover analysis: /Users/redink/github/task/.eunit/index.html 72 | 73 | ### start 74 | $ erl -pa ./ebin 75 | 76 | ### e.g. 77 | 78 | 1> task:async(erlang, self, []). 79 | {<0.34.0>,#Ref<0.0.0.30>} 80 | 81 | 2> task:await(v(1)). 82 | <0.34.0> 83 | 84 | 3> task:async(erlang, process_info, [erlang:self()]). 85 | {<0.37.0>,#Ref<0.0.0.39>} 86 | 87 | 4> task:await(v(3)). 88 | [{current_function,{io,wait_io_mon_reply,2}}, 89 | {initial_call,{erlang,apply,2}}, 90 | {status,waiting}, 91 | {message_queue_len,0}, 92 | {messages,[]}, 93 | {links,[<0.26.0>,<0.37.0>]}, 94 | {dictionary,[]}, 95 | {trap_exit,false}, 96 | {error_handler,error_handler}, 97 | {priority,normal}, 98 | {group_leader,<0.25.0>}, 99 | {total_heap_size,986}, 100 | {heap_size,376}, 101 | {stack_size,29}, 102 | {reductions,2153}, 103 | {garbage_collection,[{min_bin_vheap_size,46422}, 104 | {min_heap_size,233}, 105 | {fullsweep_after,65535}, 106 | {minor_gcs,6}]}, 107 | {suspending,[]}] 108 | 109 | ### remote node call 110 | 111 | Now, `Task` support remote node call, you can use it like: 112 | 113 | 5> task:async('test@127.0.0.1', erlang, self, []). 114 | {<6206.43.0>,#Ref<0.0.0.42>} 115 | 116 | 6> task:await(v(5)). 117 | <6206.43.0> 118 | 119 | -------------------------------------------------------------------------------- /doc/edoc-info: -------------------------------------------------------------------------------- 1 | %% encoding: UTF-8 2 | {application,task}. 3 | {packages,[]}. 4 | {modules,[task]}. 5 | -------------------------------------------------------------------------------- /doc/erlang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redink/task/7a5977e4c4c7d6e1a835fcedbd99f58b1f0c1200/doc/erlang.png -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The task application 5 | 6 | 7 | 8 | 9 | 10 | 11 | <h2>This page uses frames</h2> 12 | <p>Your browser does not accept frames. 13 | <br>You should go to the <a href="overview-summary.html">non-frame version</a> instead. 14 | </p> 15 | 16 | 17 | -------------------------------------------------------------------------------- /doc/modules-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The task application 5 | 6 | 7 | 8 |

Modules

9 | 10 |
task
11 | 12 | -------------------------------------------------------------------------------- /doc/overview-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The task application 6 | 7 | 8 | 9 | 10 |

The task application

11 | 12 |
13 | 14 |

Generated by EDoc, Jan 13 2015, 17:30:55.

15 | 16 | 17 | -------------------------------------------------------------------------------- /doc/packages-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The task application 5 | 6 | 7 | 8 |

Packages

9 |
10 | 11 | -------------------------------------------------------------------------------- /doc/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* standard EDoc style sheet */ 2 | body { 3 | font-family: Verdana, Arial, Helvetica, sans-serif; 4 | margin-left: .25in; 5 | margin-right: .2in; 6 | margin-top: 0.2in; 7 | margin-bottom: 0.2in; 8 | color: #000000; 9 | background-color: #ffffff; 10 | } 11 | h1,h2 { 12 | margin-left: -0.2in; 13 | } 14 | div.navbar { 15 | background-color: #add8e6; 16 | padding: 0.2em; 17 | } 18 | h2.indextitle { 19 | padding: 0.4em; 20 | background-color: #add8e6; 21 | } 22 | h3.function,h3.typedecl { 23 | background-color: #add8e6; 24 | padding-left: 1em; 25 | } 26 | div.spec { 27 | margin-left: 2em; 28 | background-color: #eeeeee; 29 | } 30 | a.module,a.package { 31 | text-decoration:none 32 | } 33 | a.module:hover,a.package:hover { 34 | background-color: #eeeeee; 35 | } 36 | ul.definitions { 37 | list-style-type: none; 38 | } 39 | ul.index { 40 | list-style-type: none; 41 | background-color: #eeeeee; 42 | } 43 | 44 | /* 45 | * Minor style tweaks 46 | */ 47 | ul { 48 | list-style-type: square; 49 | } 50 | table { 51 | border-collapse: collapse; 52 | } 53 | td { 54 | padding: 3 55 | } 56 | -------------------------------------------------------------------------------- /doc/task.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module task 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module task

13 | 14 | 15 | 16 |

Function Index

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
async/1
async/2
async/3
async/4
async_do/3
async_opt/2
async_opt/3
async_opt/4
async_opt/5
await/1
await/2
safe_await/2
safe_await/3
31 | 32 |

Function Details

33 | 34 |

async/1

35 |
36 |

async(Fun::function()) -> {pid(), reference()}

37 |
38 | 39 |

async/2

40 |
41 |

async(Node::atom(), Fun::function()) -> {pid(), reference()}

42 |
43 | 44 |

async/3

45 |
46 |

async(Mod::atom(), Fun::atom(), Args::[term()]) -> {pid(), reference()}

47 |
48 | 49 |

async/4

50 |
51 |

async(Node::atom(), Mod::atom(), Fun::atom(), Args::[term()]) -> {pid(), reference()}

52 |
53 | 54 |

async_do/3

55 |
56 |

async_do(TaskOwner::pid(), TaskOwnerInfo::{node(), pid() | atom()}, MFA::{atom(), atom(), [term()]}) -> term()

57 |
58 | 59 |

async_opt/2

60 |
61 |

async_opt(Fun::function(), Opts::[term()]) -> {pid(), reference()}

62 |
63 | 64 |

async_opt/3

65 |
66 |

async_opt(Node::atom(), Fun::function(), Opts::[term()]) -> {pid(), reference()}

67 |
68 | 69 |

async_opt/4

70 |
71 |

async_opt(Mod::atom(), Fun::atom(), Args::[term()], Opts::[term()]) -> {pid(), reference()}

72 |
73 | 74 |

async_opt/5

75 |
76 |

async_opt(Node::atom(), Mod::atom(), Fun::atom(), Args::[term()], Opts::[term()]) -> {pid(), reference()}

77 |
78 | 79 |

await/1

80 |
81 |

await(X1::{pid(), reference()}) -> any() | no_return()

82 |
83 | 84 |

await/2

85 |
86 |

await(X1::{pid(), reference()}, TimeOut::non_neg_integer()) -> any() | no_return()

87 |
88 | 89 |

safe_await/2

90 |
91 |

safe_await(TaskRef::{pid(), reference()}, DefaultResult::term()) -> any()

92 |
93 | 94 |

safe_await/3

95 |
96 |

safe_await(TaskRef::{pid(), reference()}, DefaultResult::term(), TimeOut::non_neg_integer()) -> any()

97 |
98 |
99 | 100 | 101 |

Generated by EDoc, Jan 13 2015, 17:30:55.

102 | 103 | 104 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redink/task/7a5977e4c4c7d6e1a835fcedbd99f58b1f0c1200/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {cover_enabled, true}. -------------------------------------------------------------------------------- /src/task.app.src: -------------------------------------------------------------------------------- 1 | {application, task, 2 | [ 3 | {description, "Elixir Task model partial implement in Erlang"}, 4 | {vsn, git}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {modules, []}, 11 | {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /src/task.erl: -------------------------------------------------------------------------------- 1 | -module(task). 2 | 3 | -export([async/3, 4 | async/4, 5 | async/1, 6 | async/2, 7 | await/1, 8 | await/2]). 9 | 10 | -export([async_opt/4, 11 | async_opt/5, 12 | async_opt/2, 13 | async_opt/3]). 14 | 15 | -export([safe_await/2, 16 | safe_await/3]). 17 | 18 | -export([async_do/3]). 19 | 20 | -spec async(function()) -> {pid(), reference()}. 21 | async(Fun) when erlang:is_function(Fun) -> 22 | async(erlang, apply, [Fun, []]). 23 | 24 | -spec async(atom(), function()) -> {pid(), reference()}. 25 | async(Node, Fun) when erlang:is_function(Fun) -> 26 | async(Node, erlang, apply, [Fun, []]). 27 | 28 | -spec async(atom(), atom(), [term()]) -> {pid(), reference()}. 29 | async(Mod, Fun, Args) -> 30 | Me = erlang:self(), 31 | Pid = proc_lib:spawn_link(?MODULE, async_do, 32 | [Me, get_info(Me), {Mod, Fun, Args}]), 33 | Ref = erlang:monitor(process, Pid), 34 | erlang:send(Pid, {Me, Ref}), 35 | {Pid, Ref}. 36 | 37 | -spec async(atom(), atom(), atom(), [term()]) -> {pid(), reference()}. 38 | async(Node, Mod, Fun, Args) -> 39 | Me = erlang:self(), 40 | Pid = proc_lib:spawn_link(Node, ?MODULE, async_do, 41 | [Me, get_info(Me), {Mod, Fun, Args}]), 42 | Ref = erlang:monitor(process, Pid), 43 | erlang:send(Pid, {Me, Ref}), 44 | {Pid, Ref}. 45 | 46 | -spec async_opt(function(), [term()]) -> {pid(), reference()}. 47 | async_opt(Fun, Opts) when erlang:is_function(Fun) -> 48 | async_opt(erlang, apply, [Fun, []], Opts). 49 | 50 | -spec async_opt(atom(), function(), [term()]) -> {pid(), reference()}. 51 | async_opt(Node, Fun, Opts) when erlang:is_function(Fun) -> 52 | async_opt(Node, erlang, apply, [Fun, []], Opts). 53 | 54 | -spec async_opt(atom(), atom(), 55 | [term()], [term()]) -> {pid(), reference()}. 56 | async_opt(Mod, Fun, Args, Opts) -> 57 | Me = erlang:self(), 58 | Pid = proc_lib:spawn_opt(?MODULE, async_do, 59 | [Me, get_info(Me), {Mod, Fun, Args}], 60 | [link | Opts]), 61 | Ref = erlang:monitor(process, Pid), 62 | erlang:send(Pid, {Me, Ref}), 63 | {Pid, Ref}. 64 | 65 | -spec async_opt(atom(), atom(), atom(), 66 | [term()], [term()]) -> {pid(), reference()}. 67 | async_opt(Node, Mod, Fun, Args, Opts) -> 68 | Me = erlang:self(), 69 | Pid = proc_lib:spawn_opt(Node, ?MODULE, async_do, 70 | [Me, get_info(Me), {Mod, Fun, Args}], 71 | [link | Opts]), 72 | Ref = erlang:monitor(process, Pid), 73 | erlang:send(Pid, {Me, Ref}), 74 | {Pid, Ref}. 75 | 76 | -spec await({pid(), reference()}) -> any() | no_return(). 77 | await({Pid, Ref}) -> 78 | await({Pid, Ref}, 5000). 79 | 80 | -spec await({pid(), reference()}, 81 | non_neg_integer()) -> any() | no_return(). 82 | await({Pid, Ref}, TimeOut) -> 83 | receive 84 | {Ref, Reply} -> 85 | erlang:demonitor(Ref, [flush]), 86 | Reply; 87 | {'DOWN', Ref, _, _, noconnection} -> 88 | erlang:exit({nodedown, erlang:node(Pid), 89 | {?MODULE, await, [{Pid, Ref}, TimeOut]}}); 90 | {'DOWN', Ref, _, _, Reason} -> 91 | erlang:exit({Reason, 92 | {?MODULE, await, [{Pid, Ref}, TimeOut]}}) 93 | after TimeOut -> 94 | erlang:demonitor(Ref, [flush]), 95 | erlang:exit({timeout, 96 | {?MODULE, await, [{Pid, Ref}, TimeOut]}}) 97 | end. 98 | 99 | -spec safe_await({pid(), reference()}, term()) -> any(). 100 | safe_await(TaskRef, DefaultResult) -> 101 | safe_await(TaskRef, DefaultResult, 5000). 102 | 103 | -spec safe_await({pid(), reference()}, 104 | term(), non_neg_integer()) -> any(). 105 | safe_await(TaskRef, DefaultResult, TimeOut) -> 106 | case catch await(TaskRef, TimeOut) of 107 | {'EXIT', _} -> 108 | DefaultResult; 109 | Any -> 110 | Any 111 | end. 112 | 113 | -spec async_do(pid(), {node(), pid() | atom()}, 114 | {atom(), atom(), [term()]}) -> term(). 115 | async_do(TaskOwner, TaskOwnerInfo, MFA) -> 116 | initial_call(MFA), 117 | Ref = 118 | receive 119 | {TaskOwner, Ref1} -> 120 | Ref1 121 | end, 122 | erlang:send(TaskOwner, {Ref, do_apply(TaskOwnerInfo, MFA)}). 123 | 124 | get_info(Pid) -> 125 | Name = 126 | case erlang:process_info(Pid, [registered_name]) of 127 | [{registered_name, []}] -> 128 | Pid; 129 | [{registered_name, RegisteredName}] -> 130 | RegisteredName 131 | end, 132 | {erlang:node(Pid), Name}. 133 | 134 | initial_call(MFA) -> 135 | erlang:put('$initial_call', get_initial_call(MFA)). 136 | 137 | get_initial_call({Mod, Fun, Args}) -> 138 | {Mod, Fun, erlang:length(Args)}. 139 | 140 | do_apply(TaskOwnerInfo, {Mod, Fun, Args} = MFA) -> 141 | try 142 | erlang:apply(Mod, Fun, Args) 143 | catch 144 | error: Value -> 145 | task_exit(TaskOwnerInfo, MFA, 146 | {Value, erlang:get_stacktrace()}); 147 | throw: Value -> 148 | task_exit(TaskOwnerInfo, MFA, 149 | {{nocatch, Value}, erlang:get_stacktrace()}); 150 | exit: Value -> 151 | task_exit(TaskOwnerInfo, MFA, Value) 152 | end. 153 | 154 | task_exit(_, _, normal) -> 155 | erlang:exit(normal); 156 | task_exit(_, _, shutdown) -> 157 | erlang:exit(shutdown); 158 | task_exit(_, _, Reason) when erlang:tuple_size(Reason) =:=2 159 | andalso 160 | erlang:element(2, Reason) =:= shutdown -> 161 | erlang:exit(Reason); 162 | task_exit(TaskOwnerInfo, MFA, Reason) -> 163 | {Fun, Args} = get_running(MFA), 164 | 165 | error_logger:format( 166 | "** Task ~p terminating~n" ++ 167 | "** Started from ~p~n" ++ 168 | "** When function == ~p~n" ++ 169 | "** arguments == ~p~n" ++ 170 | "** Reason for termination == ~n" ++ 171 | "** ~p~n", [erlang:self(), 172 | get_from(TaskOwnerInfo), 173 | Fun, Args, Reason]), 174 | erlang:exit(Reason). 175 | 176 | get_from({Node, PidOrName}) when Node =:= erlang:node() -> 177 | PidOrName; 178 | get_from(Other) -> 179 | Other. 180 | 181 | get_running({Mod, Fun, Args}) -> 182 | {erlang:make_fun(Mod, Fun, erlang:length(Args)), Args}. 183 | -------------------------------------------------------------------------------- /test/task_test.erl: -------------------------------------------------------------------------------- 1 | -module(task_test). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | -export([wait_and_send/2]). 6 | 7 | wait_and_send(TaskOwner, Atom) -> 8 | erlang:send(TaskOwner, ready), 9 | receive 10 | true -> 11 | true 12 | end, 13 | erlang:send(TaskOwner, Atom). 14 | 15 | task_test_() -> 16 | {setup, 17 | fun() -> 18 | ok 19 | end, 20 | fun(_) -> 21 | [ 22 | {"async/1", 23 | fun() -> 24 | Me = erlang:self(), 25 | {Pid, _} = Task = task:async(fun() -> wait_and_send(Me, done) end), 26 | 27 | receive 28 | ready -> 29 | ok 30 | end, 31 | 32 | erlang:send(Pid, true), 33 | 34 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 35 | ?assertEqual(done, task:await(Task)) 36 | end}, 37 | {"async/2", 38 | fun() -> 39 | Me = erlang:self(), 40 | net_kernel:start(['test@127.0.0.1']), 41 | {Pid, _} = Task = task:async('test@127.0.0.1', fun() -> wait_and_send(Me, done) end), 42 | 43 | ?assertEqual('test@127.0.0.1', erlang:node(Pid)), 44 | receive 45 | ready -> 46 | ok 47 | end, 48 | 49 | erlang:send(Pid, true), 50 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 51 | ?assertEqual(done, task:await(Task)) 52 | end}, 53 | {"async/3", 54 | fun() -> 55 | {Pid, _} = Task = task:async(?MODULE, wait_and_send, [self(), done]), 56 | 57 | receive 58 | ready -> 59 | ok 60 | end, 61 | 62 | erlang:send(Pid, true), 63 | 64 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 65 | ?assertEqual(done, task:await(Task)) 66 | end}, 67 | {"async/4", 68 | fun() -> 69 | net_kernel:start(['test@127.0.0.1']), 70 | {Pid, _} = Task = task:async('test@127.0.0.1', ?MODULE, wait_and_send, [self(), done]), 71 | 72 | ?assertEqual('test@127.0.0.1', erlang:node(Pid)), 73 | receive 74 | ready -> 75 | ok 76 | end, 77 | 78 | erlang:send(Pid, true), 79 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 80 | ?assertEqual(done, task:await(Task)) 81 | end}, 82 | {"async_opt/2", 83 | fun() -> 84 | Opts = [{fullsweep_after, 10}, {priority, high}], 85 | Me = erlang:self(), 86 | {Pid, _} = Task = task:async_opt(fun() -> wait_and_send(Me, done) end, Opts), 87 | ?assertEqual({priority, high}, erlang:process_info(Pid, priority)), 88 | ?assertEqual(10, proplists:get_value(fullsweep_after, erlang:element(2, erlang:process_info(Pid, garbage_collection)))), 89 | 90 | receive 91 | ready -> 92 | ok 93 | end, 94 | 95 | erlang:send(Pid, true), 96 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 97 | ?assertEqual(done, task:await(Task)) 98 | end}, 99 | {"async_opt/3", 100 | fun() -> 101 | net_kernel:start(['test@127.0.0.1']), 102 | Opts = [{fullsweep_after, 10}, {priority, high}], 103 | Me = erlang:self(), 104 | {Pid, _} = Task = task:async_opt('test@127.0.0.1', fun() -> wait_and_send(Me, done) end, Opts), 105 | 106 | ?assertEqual('test@127.0.0.1', erlang:node(Pid)), 107 | ?assertEqual({priority, high}, erlang:process_info(Pid, priority)), 108 | ?assertEqual(10, proplists:get_value(fullsweep_after, erlang:element(2, erlang:process_info(Pid, garbage_collection)))), 109 | 110 | receive 111 | ready -> 112 | ok 113 | end, 114 | 115 | erlang:send(Pid, true), 116 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 117 | ?assertEqual(done, task:await(Task)) 118 | end}, 119 | {"async_opt/4", 120 | fun() -> 121 | Opts = [{fullsweep_after, 10}, {priority, high}], 122 | {Pid, _} = Task = task:async_opt(?MODULE, wait_and_send, [self(), done], Opts), 123 | ?assertEqual({priority, high}, erlang:process_info(Pid, priority)), 124 | ?assertEqual(10, proplists:get_value(fullsweep_after, erlang:element(2, erlang:process_info(Pid, garbage_collection)))), 125 | 126 | receive 127 | ready -> 128 | ok 129 | end, 130 | 131 | erlang:send(Pid, true), 132 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 133 | ?assertEqual(done, task:await(Task)) 134 | end}, 135 | {"async_opt/5", 136 | fun() -> 137 | net_kernel:start(['test@127.0.0.1']), 138 | Opts = [{fullsweep_after, 10}, {priority, high}], 139 | {Pid, _} = Task = task:async_opt('test@127.0.0.1', ?MODULE, wait_and_send, [self(), done], Opts), 140 | 141 | ?assertEqual('test@127.0.0.1', erlang:node(Pid)), 142 | ?assertEqual({priority, high}, erlang:process_info(Pid, priority)), 143 | ?assertEqual(10, proplists:get_value(fullsweep_after, erlang:element(2, erlang:process_info(Pid, garbage_collection)))), 144 | 145 | receive 146 | ready -> 147 | ok 148 | end, 149 | 150 | erlang:send(Pid, true), 151 | ?assertEqual(true, lists:member(Pid, erlang:element(2, erlang:process_info(self(), links)))), 152 | ?assertEqual(done, task:await(Task)) 153 | end}, 154 | {"await/1 exits on timeout", 155 | fun() -> 156 | Task = {undefined, erlang:make_ref()}, 157 | ?_assertEqual(catch task:await(Task, 0), {'EXIT', {timeout, {task, await, [Task, 0]}}}) 158 | end}, 159 | {"await/1 exits on normal exit", 160 | fun() -> 161 | Task = task:async(erlang, exit, [normal]), 162 | ?_assertEqual(catch task:await(Task), {'EXIT', {normal, {task, await, [Task, 5000]}}}) 163 | end}, 164 | {"await/1 exits on task throw", 165 | fun() -> 166 | erlang:process_flag(trap_exit, true), 167 | Task = task:async(erlang, throw, [unknown]), 168 | ?_assertMatch({'EXIT',{{{nocatch,unknown},_},{task,await,[Task, 5000]}}}, catch task:await(Task)) 169 | end}, 170 | {"await/1 exits on task exit", 171 | fun() -> 172 | erlang:process_flag(trap_exit, true), 173 | Task = task:async(erlang, exit, [unknown]), 174 | ?_assertEqual(catch task:await(Task), {'EXIT', {unknown, {task, await, [Task, 5000]}}}) 175 | end}, 176 | {"await/1 exits on noconnection", 177 | fun() -> 178 | Ref = erlang:make_ref(), 179 | Pid = erlang:self(), 180 | Task = {Pid, Ref}, 181 | 182 | erlang:send(Pid, {'DOWN', Ref, Pid, Pid, noconnection}), 183 | 184 | ?_assertEqual(catch task:await(Task), {'EXIT',{nodedown,nonode@nohost,{task,await,[Task,5000]}}}) 185 | end}, 186 | {"safe_await/2 exits on noconnection", 187 | fun() -> 188 | Ref = erlang:make_ref(), 189 | Pid = erlang:self(), 190 | Task = {Pid, Ref}, 191 | 192 | erlang:send(Pid, {'DOWN', Ref, Pid, Pid, noconnection}), 193 | 194 | ?_assertEqual({false, []}, task:safe_await(Task, {false, []})) 195 | end}, 196 | {"safe_await/2 exits on task exit", 197 | fun() -> 198 | erlang:process_flag(trap_exit, true), 199 | Task = task:async(erlang, exit, [unknown]), 200 | ?_assertEqual({false, []}, task:safe_await(Task, {false, []})) 201 | end} 202 | ] 203 | end 204 | }. 205 | --------------------------------------------------------------------------------