1## Copyright (C) 2015-2020 Olaf Till <i7tiol@t-online.de> 2## 3## This program is free software; you can redistribute it and/or modify it under 4## the terms of the GNU General Public License as published by the Free Software 5## Foundation; either version 3 of the License, or (at your option) any later 6## version. 7## 8## This program is distributed in the hope that it will be useful, but WITHOUT 9## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 11## details. 12## 13## You should have received a copy of the GNU General Public License along with 14## this program; if not, see <http://www.gnu.org/licenses/>. 15 16## -*- texinfo -*- 17## @deftypefn{Function File} {} rfeval (@var{func}, @dots{}, @var{nout}, @var{isout}, @var{connection}) 18## Evaluate a function at a remote machine. 19## 20## @var{func} is evaluated with arguments @code{@dots{}} and number of 21## output arguments set to @var{nout} at remote machine given by 22## @var{connection}. If @var{isout} is not empty, it must be a logical 23## array with @var{nout} elements, which are true for each of the 24## @var{nout} output arguments which are requested from the function; 25## the other output arguments will be marked as not requested 26## with @code{~} at remote execution. 27## 28## This function can only be successfully called at the client machine. 29## See @code{pconnect} for a description of the @var{connection} 30## variable. @var{connection} must contain one single connection. 31## 32## If an output argument is given to @code{rfeval}, the function waits 33## for completion of the remote function call, retrieves the results and 34## returns them. They will be returned as one cell-array with an entry 35## for each output argument. If some output arguments are marked as not 36## requested by setting some elements of @var{isout} to false, the 37## returned cell-array will only have entries for the requested output 38## arguments. For consistency, the returned cell-array can be empty. To 39## assign the output arguments to single variables, you can for example 40## use: @code{[a, b, c] = returned_cell_array@{:@};}. 41## 42## If no output argument is given to @code{rfeval}, the function does 43## not retrieve the results of the remote function call but returns 44## immediately. It is left to the user to retrieve the results with 45## @code{precv}. The results will be in the same format as if returned 46## by @code{rfeval}. Note that a cell-array, possibly empty, will always 47## have to be retrieved, even if the remote function call should have 48## been performed without output arguments. 49## 50## Parallel execution can be achieved by calling @code{rfeval} several 51## times with different specified server machines before starting to 52## retrieve the results. 53## 54## The specified function handle can refer to a function present at 55## the executing machine or be an anonymous function. See 56## @code{parallel_doc ("limitations")} for possible limitations. 57## 58## @seealso{pconnect, pserver, sclose, install_vars, netcellfun} 59## @end deftypefn 60 61function ret = rfeval (varargin) 62 63 if ((nargs = nargin ()) < 4) 64 print_usage (); 65 endif 66 67 fname = "rfeval"; 68 69 if (! isa (conn = varargin{end}, "pconnections")) 70 error ("%s: `connection' must be a parallel connections object", fname); 71 elseif (numel (conn) != 1) 72 error ("%s: exactly one connection must be specified", fname); 73 elseif (network_get_info (conn).local_machine) 74 error ("%s: client was specified instead of server"); 75 ## reval() checks if specified at server side 76 endif 77 78 if (! is_function_handle (varargin{1})) 79 error ("%s: `func' must be a function handle", fname); 80 endif 81 82 if (! isnumeric (nout = varargin{end - 2}) || ! isscalar (nout) || ... 83 (nout = round (nout)) < 0) 84 error ("%s: `nout' must be a non-negative integer", fname); 85 endif 86 87 if (isempty (isout = varargin{end - 1})) 88 isout = true (1, nout); 89 elseif (! islogical (isout) || numel (isout) != nout) 90 error ("%s: `isout' must be empty or a logical with `nout' elements", 91 fname); 92 endif 93 isout = isout(:); 94 95 ## feval() isn't called remotely since it doesn't resepect ignoring of 96 ## output variables with '~'. So the function handle has to be sent 97 ## separately and a remote temporary variable has to be used for it. 98 ## 99 ## rargs = varargin(1 : end - 3); # could be used with feval 100 func = varargin{1}; 101 rargs = varargin(2 : end - 3); 102 103 if (any (ign = ! isout)) 104 rout = repmat ({"__pserver_tout__{%i},"}, nout, 1); 105 rout(ign) = {"~,"}; 106 rout = cstrcat (rout{:}); 107 rout = sprintf (rout, 1 : sum (isout)); 108 rout = rout(1 : end - 1); # remove final ',' 109 rout = sprintf ("[%s]", rout); 110 if (all (ign)) 111 init_rout = "__pserver_tout__ = {}; "; 112 else 113 init_rout = ""; 114 endif 115 else 116 rout = sprintf ("[__pserver_tout__{1:%i}]", nout); 117 init_rout = ""; 118 endif 119 120 cmd = sprintf ... 121 ("__pserver_tfunc__ = precv (sockets(1)); %s%s = __pserver_tfunc__ (precv (sockets(1)){:}); psend (__pserver_tout__, sockets(1)); clear ('__pserver_tout__');", 122 init_rout, rout); 123 124 reval (cmd, conn); 125 126 ready = false; 127 128 unwind_protect 129 130 psend (func, conn); 131 132 psend (rargs, conn); 133 134 ready = true; 135 136 unwind_protect_cleanup 137 138 if (! ready) 139 sclose (conn); # might already be closed from within reval or psend 140 endif 141 142 end_unwind_protect 143 144 if (nargout () > 0) 145 ret = precv (conn); 146 endif 147 148endfunction 149