2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - DIGITEO - Bernard HUGUENEY
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
14 /* remove warning about : 'std::fill_n': Function call with parameters that may be unsafe */
15 #define _SCL_SECURE_NO_WARNINGS
20 #include "api_scilab.h"
22 #include "gw_parallel.h"
23 #include "dynamic_link.h"
26 #include "localization.h"
27 #include "parameters.h"
28 #include "stack-def.h" /* #define nlgh nsiz*4 */
29 #include "stack-c.h" /* #define Nbvars C2F(intersci).nbvars, Top & cie */
34 /*#include <sstream> for debug purposes only */
35 #include "parallel_run.hxx"
48 We can handle k Rhs et m Lhs. Currently, only real (double) matrix are implemented.
49 In fact, from a performance point of view, only Random-Acces data types where it is possible to "extract" a column in O(1)
50 (plus memcpy for macro a args) are making sense. This rules out most Scilab data types except for matrices of double / int.
53 [R1, ... , Rm] = parallel_run(A1, ... , Ak, f [,Types] [,Dims])
55 If Args are of différent sizes, the smallest are recycled.
57 Types : matrix of <=m strings of the names (as in typeof()) of the m lhs fo 'f'. default to "constant"
58 Rows : matrix of doubles 1 x <=m or 2 x <=m giving the nb of rows (or rows and columns) of the m lhs of f. default to 1
60 /!\ due to matrix data layout in Scilab (i.e. Fortran columnswise storage)
61 , a matrix [a11,a12,a13;a21,a22,a23] contains 3 (ncols) arguments of 2 (nrows) elements;
66 int sci_parallel_run(char *fname,unsigned long fname_len);
71 /* to distinguish scilab variable 'address' from usual int* */
72 typedef int* scilabVar_t;
74 int currentTop; /* Top is fragile : this var mimics Top to avoid touching the real one */
75 char* currentFname= 0; /* name of the current Scilab function, to be used un error messages btw, what is the api_scilab equivalent of get_fname()?*/
77 SciErr err; /* store error status from api_scilab functions */
79 /* often, we handle nb of dimensions (rows and cols) at once.
80 * I'd have gone for std::size_t but Scilab uses int :( */
81 typedef std::pair<int, int> dim_t;
83 /* A scilab variable description is a typename and a matric dimension, just to add a default constructor on the inherited struct
84 * character string a static (no dynamic alloc/destruction)
86 struct scilabDesc_t :std::pair<char const*, dim_t>
88 scilabDesc_t(char const* name="constant", dim_t dim=dim_t(1,1)) /* default variable is a scalar i.e. 1x1 matrix of typename "constant" */
89 :std::pair<char const*, dim_t>(name, dim)
92 /* for debug purposes only
93 std::string toString() const
95 std::stringstream buf;
96 buf<<std::pair<char const*, dim_t>::first<<" :"<<std::pair<char const*, dim_t>::second.first
97 <<" x "<<std::pair<char const*, dim_t>::second.second;
103 /* types to be used in unions of function pointers */
104 typedef void (*functionToCall_t)(char const*const*, char*const*);
105 typedef void (*wrapperFunction_t)(double const*, double*);
106 typedef void (*loadedFunction_t)();
107 typedef void (*simpleFunction_t)(int );
109 /* ptr to data type */
119 /* a functional version of getVarType that can be composed
120 * @param int* address of the scilab var
121 * @return the type (0 on error)
123 int getVarType(scilabVar_t var)
126 err= getVarType(pvApiCtx, var, &res);
130 /* return the typename (as in typeof() )
131 * @param int* address of the scilab var
133 * only implements the current valid types for parallel_run args.
135 char const* getTypeName(scilabVar_t var)
138 switch(getVarType(var))
147 res="unimplemented type in getTypeName";
152 /* get nb of Rows & Cols of a Scilab var : a fonctional version of getVarDimension that can be composed.
153 * @param the address of the variable
154 * @return the dimensions in a std::pair<int,int> of nb of rows, nb of columns. (0,0) on error.
156 dim_t getRowsCols(scilabVar_t var)
159 err= getVarDimension(pvApiCtx, var, &res.first, &res.second);
162 /* get nb of Rows of a Scilab var (would not be needed with tr1)
163 * @param the address of the variable
164 * @return the nb of rows, 0 on error.
166 int getRows(scilabVar_t var)
168 return getRowsCols(var).first;
170 /* get nb of Columns of a Scilab var (would not be needed with tr1)
171 * @param the address of the variable
172 * @return the nb of columns, 0 on error.
174 int getCols(scilabVar_t var)
176 return getRowsCols(var).second;
179 /* test if the scilab var can be a function (either because it *is* one or because it can be the name of a function : 1x1 string marix)
180 * @param var the scilab var
181 * @return bool true iff it can be a function
183 bool isFunctionOrString(scilabVar_t var){
185 switch(getVarType(var))
187 case sci_u_function :
188 case sci_c_function :
195 res= (getRows(var) == 1) && (getCols(var) == 1);
205 /* get ptr to data of a Scilab variable
206 * @param var 'address' of the scilab var
207 * @return ptr to the data
209 unionOfPtrs_t getData(scilabVar_t var)
211 unionOfPtrs_t res={0};
212 switch(getVarType(var)) // unhandled data types should be caught during arg validation
216 if(!isVarComplex(pvApiCtx, var))
219 err= getMatrixOfDouble(pvApiCtx, var, &unused, &unused, &res.doublePtr);
222 {/* TODO suggest workaround in tutorial */
224 // std::cerr<<"complex data not yet implemented var @"<<var<<std::endl;
230 // std::cerr<<"getData() string data not yet implemented"<<std::endl;
235 // std::cerr<<"getData() data type"<<getVarType(var)<<" not yet implemented"<<std::endl;
241 /* get size of an element in a scilab matrix data structure.
242 * @param var 'address' of the scilab var
243 * @return the size in bytes, 0 on error.
245 std::size_t getSizeOfElement(scilabVar_t var)
248 switch(getVarType(var))
255 default : /* returns 0 */
257 // std::cerr<<"getSizeOfElt() @"<<var<<":data type not yet implemented"<<std::endl;
263 /* get size of a columns in a scilab matrix data structure.
264 * @param var 'address' of the scilab var
265 * @return the size in bytes, 0 on error.
267 std::size_t getSizeOfColumn(scilabVar_t var)
269 return getSizeOfElement(var) * getRows(var) ;
272 /* get size of the data in a scilab matrix data structure.
273 * @param var 'address' of the scilab var
274 * @return the size in bytes, 0 on error.
276 std::size_t getSizeOfData(scilabVar_t var)
278 return getSizeOfColumn(var) * getCols(var) ;
281 /* computes a dimension that is either
282 * a slice (one column), or the concatenation of n matrix
283 * For n=1, the dimension is untouched.
284 * @param d dimension to slice or concatenate
285 * @return the new dimension
287 dim_t sliceOrConcat(dim_t d, std::size_t n=1)
293 d.second= 1; /* slice : one column */
301 d.second = static_cast<int>(n);
307 /* Get the description from a scilab variable.
308 * If n is provided, it instead returns the description of
309 * either a slice (n==0) or a concatenation (n>1) of the variable.
310 * @param var scilab variable address
311 * @param n either slice or concatenation
312 * @return the description
314 scilabDesc_t getDesc(scilabVar_t var, std::size_t n=1)
316 return scilabDesc_t( getTypeName(var),sliceOrConcat(getRowsCols(var), n));
319 /* allocate a scilab variable according to a provided description.
320 * If n is provided, it instead allocates
321 * either a slice (n==0) or a concatenation (n>1) of the variable.
322 * @param d scilab variable description
323 * @param n either slice or concatenation
324 * @return the variable address
325 * only real matrices are implemented yet.
327 scilabVar_t allocVar(scilabDesc_t d, std::size_t n=1)
330 if(std::strcmp(d.first, "constant")==0)
333 dim_t toAlloc(sliceOrConcat(d.second, n));
334 err= allocMatrixOfDouble(pvApiCtx, ++currentTop, toAlloc.first, toAlloc.second, &unused);
336 // std::cerr<<"alloc var :"<<d.toString()<<" @"<<(currentTop)<<" with Nbvars="<<Nbvars<<std::endl;
337 err= getVarAddressFromPosition(pvApiCtx, currentTop, &res);
339 else /* unhandled type should be caught at arg validation time */
341 // std::cerr<<"allocVar() "<<d.first<<" data type not yet implemented"<<std::endl;
345 /* Ensures that the complete expected var is filled with reasonable default values
346 * when the returned var was smaller than expected
348 * i.e. we just copied the data from a resultVar @ varData
349 * when we were expecting an expectedVar : we fill the rest.
351 * @param d scilab variable description
352 * @param n either slice or concatenation
353 * @return the variable address
354 * only real matrices are implemented yet.
356 void fillUndefinedData(void* varData, scilabDesc_t resultVar, scilabDesc_t expectedVar)
358 if(!std::strcmp(resultVar.first, expectedVar.first))
360 if(!strcmp(resultVar.first, "constant"))
362 std::size_t const nbFilled(resultVar.second.first * resultVar.second.second);
363 std::fill_n(static_cast<double *>(varData)+ nbFilled
364 , expectedVar.second.first * expectedVar.second.second - nbFilled
365 ,std::numeric_limits<double>::quiet_NaN() );
371 wrapper on a native c function or a scilab macro called on scilab variables.
374 - scilab variable for the function (external native function name, sci_c_function (buggy) or macro name)scilab matrices of arguments
377 upon construction, allocate scilab result variables and computes all necessary meta data.
381 typedef std::vector<std::size_t> sizes_container;
383 /* wrapper contructor
384 * @param args_begin iterator to the first args of parallel_run
385 * @param function_it iterator to the function argument of parallel_run
386 * @param args_end iterator past the end of parallel_run args
387 * @param function_lhs number of lhs (of parallel_run and of the function: it is the same)
389 template<typename VarsIt>
390 wrapper(VarsIt begin, VarsIt functionIt, VarsIt end, std::size_t functionLhs)
392 registerArgs(begin, functionIt);
393 n= *std::max_element(argsNb.begin(), argsNb.end());
394 getFunction(*functionIt);
395 allocCompleteResults(begin, functionIt+1, end, functionLhs);
398 /* the member function performing the call to the function (foreign function of Scilab macro)
399 * @ param args array of ptrs to args data
400 * @ param res array of ptrs to res data
402 void operator()(char const** args, char ** res)
404 (*this.*(this->fPtr))(args, res);
407 /* It is idiomatic to pass functors by value in C++, but our wrapper is heavy,
408 * so we provide a lightweight handle */
411 handle(wrapper& r) : w(r)
414 /* just forward to the underlying wrapper */
415 void operator()(char const** args, char ** res) const
423 return handle(*this);
425 /* @return begin iterator to the array of pointers to arguments data */
426 char const*const* argsDataBegin() const
428 return &argsData[0].bytePtr;
430 /* @return begin iterator to the array of arguments sizes */
431 std::size_t const* argsSizesBegin() const
433 return &argsSizes[0];
435 /* @return begin iterator to the array of arguments number of elements (they are not requires to have the same nb of elements */
436 std::size_t const* argsNbBegin() const
440 /* @return nb of tasks (calls) to perform = max(args_nb_begin(), args_nb_begin()+rhs) */
441 std::size_t tasksNb() const
445 /* @return begin iterator to the array of pointers to result data */
446 char * const* resDataBegin()
448 return &resData[0].bytePtr;
450 /* @return begin iterator to the array of results sizes */
451 std::size_t const* resSizesBegin() const
455 /* @return nb of rhs vars of the function */
456 std::size_t nbRhs() const
458 return rhsDesc.size();
460 /* @return true if the underlying function is a foreign function, false if it is a Scilab macro */
461 bool isForeignFunction() const
463 return function.toCall !=0 ;
467 /* ptr to foreign function (types used where storing, calling or wrapping) */
469 functionToCall_t toCall;
470 loadedFunction_t toLoad;
471 wrapperFunction_t wrapper;
473 /* register a matrix of arguments to be used as rhs) */
474 template<typename ArgIt>
475 void registerArgs(ArgIt it, ArgIt end)
477 std::transform(it, end, std::back_inserter(argsData), &getData);
478 std::transform(it, end, std::back_inserter(argsSizes), &getSizeOfColumn);
479 std::transform(it, end, std::back_inserter(argsNb), &getCols);
480 std::transform(it, end, std::back_inserter(rhsDesc), std::bind2nd(std::ptr_fun(&getDesc), 0)); /* get a slice as model for function rhs*/
483 /* alloc the scilab variables that will hold the complete collection of results
484 * @param first_arg_position only used to compute the args positions for error messages
485 * @param res_types_begin, res_types_end iterator range on the args describing result, can be empty
486 * @param nb_lhs number of lhs */
487 template<typename VarPtrIt>
488 void allocCompleteResults(VarPtrIt begin, VarPtrIt resBegin, VarPtrIt resEnd, std::size_t nbLhs)
490 lhsDesc.resize(nbLhs);
491 if(resBegin != resEnd)
493 if(getVarType(*resBegin) == sci_strings)
495 // std::cerr<<"we have a type lhs arg\n";
498 if(resBegin != resEnd)
500 if(getVarType(*resBegin) == sci_matrix)
502 // std::cerr<<"we have a dim lhs arg\n";
503 dim_t const tmp(getRowsCols(*resBegin));
504 double const*const data(getData(*resBegin).doublePtr);
510 // std::cerr<<"we have rows and cols\n";
511 for(int i(0); i < tmp.first && i< tmp.first*tmp.second; ++i)
513 lhsDesc[i].second.first= static_cast<int>(data[i]);
514 lhsDesc[i].second.second=static_cast<int>(data[i+tmp.first]);
520 // std::cerr<<"we have rows \n";
521 for(int i(0); i< tmp.first && i< tmp.first*tmp.second; ++i)
523 lhsDesc[i].second.first= static_cast<int>(data[i]);
529 Scierror(999,_("%s: Wrong size of input argument #%d: Number of columns are incompatible ")
530 ,currentFname, std::distance(begin, resBegin));
536 /* we want to have to result var at hand before calling the scilab macro so we must create it now before the args */
537 std::transform(lhsDesc.begin(), lhsDesc.end(), std::back_inserter(scilabCollectionsOfLhs), std::bind2nd(std::ptr_fun(&allocVar), n));
538 /* we store addr of result data and the size for the parallel wrapper */
539 std::transform(scilabCollectionsOfLhs.begin(), scilabCollectionsOfLhs.end(), std::back_inserter(resData), &getData);
540 std::transform(scilabCollectionsOfLhs.begin(), scilabCollectionsOfLhs.end(), std::back_inserter(resSizes), &getSizeOfColumn);
543 /* extract the function form the scilab variable (i.e.string) reprensenting it.
544 * @param v the variable
545 * @return nothing useful but GetRhsVar() macro wants to be able to return an int :(
547 int getFunction(scilabVar_t var) {
549 switch(getVarType(var))
551 case sci_c_function : {
553 GetRhsVar(2, EXTERNAL_DATATYPE, unused, unused+1, &scilabFunction);
554 fPtr = &wrapper::macro<false>;
559 getAllocatedSingleString(pvApiCtx, var, &funName);
561 found=SearchInDynLinks(funName, &function.toLoad);
562 fPtr= &wrapper::nativeFunction;
565 /* should check amongst defined macros with getmacroslist (cf dans core/src/c/getvariablesname.c) and check that type is sci_XXX */
567 scilabFunctionName= funName;
568 scilabFunctionNameLength= std::strlen(scilabFunctionName);
569 fPtr= &wrapper::macro<true>;
576 /* performs the Scilab macro call
577 * @param byName bool template parameter tells if the macro is called by name or by ptr (ptr is currently broken).
578 * @param args array of ptrs to args data
579 * @param res array of ptr to res data
581 template<bool byName>
582 void macro(char const** args, char ** res) {
583 /* rhs models from */
584 int saveNbvars= Nbvars, saveTop= currentTop;
585 for( std::vector<scilabDesc_t>::const_iterator it(rhsDesc.begin())
586 ; it != rhsDesc.end(); ++it, ++args)
588 scilabVar_t scilabArg= allocVar(*it); /* create a var for a slice (col)of the parallel_run Rhs arg */
589 memcpy(getData(scilabArg).bytePtr, *args, getSizeOfData(scilabArg));
592 int sciRhs = static_cast<int>(rhsDesc.size());
593 int sciLhs = static_cast<int>(lhsDesc.size());
595 std::size_t dummyVars(0); /* alloc safety variable to ensure space on the stack upon return*/
596 int sciArgPos = saveTop+1;
597 for( ;sciRhs+dummyVars < sciLhs+maxSafetyLhs; ++dummyVars, ++Nbvars)
600 err= allocMatrixOfDouble(pvApiCtx, ++currentTop, 0, 0, &unused);
602 Nbvars = Rhs + Lhs + sciRhs;
604 ? C2F(scistring)(&sciArgPos, scilabFunctionName, &sciLhs, &sciRhs, static_cast<unsigned long>(scilabFunctionNameLength))
605 : C2F(scifunction)(&sciArgPos, &scilabFunction, &sciLhs, &sciRhs)
607 // result r is now on first position on stack
609 Nbvars = static_cast<int>(Rhs + Lhs + sciRhs + dummyVars);
610 int resPos = Rhs + Lhs + 1; //+1
612 for( std::vector<scilabDesc_t>::iterator it(lhsDesc.begin())
613 ; it != lhsDesc.end(); ++it, ++resPos, ++res)
615 scilabVar_t scilabRes;
618 err= getVarAddressFromPosition(pvApiCtx, resPos, &scilabRes);
620 scilabDesc_t resDesc;
621 if(!success || err.iErr)
622 {/* there was an error getting the result variable */
623 resDesc= *it; /* pretend we got the right type */
624 resDesc.second.first = resDesc.second.second= 0;/* but 0 elements */
627 { /* copy the returned data */
628 memcpy(*res, getData(scilabRes).bytePtr, getSizeOfData(scilabRes));
629 resDesc= getDesc(scilabRes);
631 fillUndefinedData(*res, resDesc, *it);
637 void nativeFunction(char const** args, char ** res)
639 function.toCall(args, res);
642 /* we prealloc as much scilab var more than requested lhs in case the scilab macro call back returns more thant requested.*/
643 static unsigned int const maxSafetyLhs = 20;
645 std::size_t n; /* nb of calls to perform */
646 sizes_container argsSizes, argsNb, resSizes; /* sizes of arguments, nb of elements for each argument, sizes for results */
647 std::vector<unionOfPtrs_t> argsData; /* ptrs to args data */
648 std::vector<unionOfPtrs_t> resData; /* ptrs to res data */
650 /* the member function to call, dispatches to macro of foreign function */
651 void(wrapper::*fPtr)(char const** args, char ** res);
653 int scilabFunction; /* the scilab function 'ptr' for scifunction */
654 char* scilabFunctionName;/* the scilab function name for scistring */
655 std::size_t scilabFunctionNameLength;/* the scilab function name length for scistring */
657 /* store models of scilab lhs and rhs variables */
658 std::vector<scilabDesc_t> lhsDesc, rhsDesc;
659 std::vector<scilabVar_t> scilabCollectionsOfLhs; /* lhs vars of the parallel_run function : collections of the lhs form the function*/
662 /* Checks if the function parallel_run arguments are valid.
663 * 1 or more matrices of doubles
664 * 1 matrix of 1 string
665 * 0 or 1 matrix of strings and/or 1 matrix of doubles with 1 ou 2 columns
666 * 0 or 1 configuration plist
668 * @retun true is the args are valid */
669 bool check_args(void) {
671 if(Rhs<2) { return false; }
672 bool before_function(true), at_least_one_arg(false);
674 for( int pos(1); pos <= Rhs && ok; ++pos) {
676 err= getVarAddressFromPosition(pvApiCtx, pos, &addr);
678 err= getVarType( pvApiCtx, addr, &type );
679 if (before_function) {
682 /* check not complex "%s: This feature has not been implemented.\n" */
683 ok= !isVarComplex( pvApiCtx, addr);
684 at_least_one_arg= true;
688 /* check dim = 1x1 */
690 err= getMatrixOfString(pvApiCtx, addr, &rows, &cols, 0,0);
691 ok= (rows == 1) && (cols == 1);
693 case sci_c_function:{
694 before_function= false;
698 Scierror(999,_("%s: Wrong type for input argument #%d: A string expected.\n"),currentFname, 1);
708 /* check not complex and ncol <=2 */
709 ok= !isVarComplex( pvApiCtx, addr);
713 err= getMatrixOfDouble(pvApiCtx, addr, &rows, &cols, &unused);
718 case sci_mlist :{ /* config plist (which is a mlist) */
728 return ok && at_least_one_arg && (!before_function);
732 /* Get configuration options form the config parameter
735 * @param config_arg_pos position where the config arg can be
739 * @param nb_workers int config value for the key "nb_workers"
740 * @param shared_memory bool config value for the key "shared_memory"
741 * @param dynamic_scheduling bool config value for the key "dynamic_scheduling"
742 * @param chunk_size int config value for the key "chunk_size"
743 * @param prologue char* config value for the key "prologue"
744 * @param prologue char* config value for the key "epilogue"
746 * @return bool true if there was a configuration argument in position config_arg_pos.
748 bool getConfigParameters
749 (int config_arg_pos, int& nb_workers, bool& shared_memory, bool& dynamic_scheduling, int& chunk_size, char const*& prologue, char const*& epilogue){
752 getVarAddressFromPosition(pvApiCtx, config_arg_pos, &addr);
753 bool has_config_arg(checkPList(pvApiCtx, addr) != 0);
756 getIntInPList(pvApiCtx, addr, "nb_workers", &nb_workers, &found, nb_workers, log, CHECK_NONE);
758 getIntInPList(pvApiCtx, addr, "shared_memory", &tmp, &found, shared_memory ? 1:0, log,CHECK_NONE);
759 shared_memory= (tmp!=0);
760 getIntInPList(pvApiCtx, addr, "dynamic_scheduling", &tmp, &found, dynamic_scheduling ? 1:0, log,CHECK_NONE);
761 dynamic_scheduling= (tmp!=0);
762 getIntInPList(pvApiCtx, addr, "chunk_size", &chunk_size, &found, chunk_size, log,CHECK_NONE);
763 getStringInPList(pvApiCtx, addr, "prologue", const_cast<char**>(&prologue), &found, const_cast<char*>(prologue), log, CHECK_NONE);
764 getStringInPList(pvApiCtx, addr, "epilogue", const_cast<char**>(&epilogue), &found, const_cast<char*>(epilogue), log, CHECK_NONE);
766 return has_config_arg;
770 A simple wrapper just wraps prologues and epilogues, each taking only a scalar argument (the process number).
772 struct simple_wrapper{
774 * @param the macro or foreign function name, empty string allowed: the function then does nothing.
776 explicit simple_wrapper(char const* name):fun(name)
779 /* the operator : calls the function or macro passing a scalar argument on the stack
780 * @param i the scalar to pass on the stack as a real value.
782 void operator()(int i)
785 { /* do nothing on empty name */
788 loadedFunction_t toLoad;
789 simpleFunction_t toCall;
791 int found= SearchInDynLinks(const_cast<char*>(fun), &function.toLoad);
799 err= allocMatrixOfDouble(pvApiCtx, ++Top, 1, 1, &tmpPtr);
800 *tmpPtr= static_cast<double>(i);
803 C2F(scistring)(&(Top), const_cast<char*>(fun), &lhs, &rhs, static_cast<unsigned long>(strlen(fun)));
813 /* Calling point from Scilab.
814 * checking args and contruction a wrapper around function call of a foreign function or a Scilab macro.
815 * this wrapper (in fact, a handle) is then passed to another wrapper that will parallelize the calls.
816 * the parallel wrapper is independant of Scilab (thanks to this wrapper) and is implemented in parallel_wrapper.hpp.
818 * Calling parallel_run is
820 * 2 constructing wrapper pre allocating result Scilab vars and abstracting arrays of args and results pointers
821 * (in parallel_wrapper )
822 * 3 contructing a parallel_wrapper
823 * 4 calling the parallel_wrapper according to config options (i.e. nb of workers)
824 * 4.1 for each call to be made, adjusting the args and res ptr
825 * 4.2 calling the wrapper
828 int sci_parallel_run(char *fname,unsigned long fname_len)
830 typedef std::vector<scilabVar_t> varsContainer_t;
831 currentFname=fname; //get_fname(fname, fname_len); uses a static buffer :(
834 Nbvars = max(Rhs, Top);
836 Nbvars = std::max(Rhs, Top);
840 Scierror(999,_("%s: Wrong number of input argument(s).\n"),fname);/* need a better error message */
846 int nbArgsToHandle(Rhs);
847 /* parameters default values */
848 int nbWorkers(0), chunkSize(1);
849 bool sharedMemory(false), dynamicScheduling(false);
850 char const* prologueName("");
851 char const* epilogueName("");
852 /* If there is a config parameter, use it to update the parameters value */
853 if( getConfigParameters(Rhs, nbWorkers, sharedMemory, dynamicScheduling, chunkSize, prologueName, epilogueName))
857 varsContainer_t stack(nbArgsToHandle);
858 for(int i(0); i!= nbArgsToHandle; ++i)
860 err= getVarAddressFromPosition(pvApiCtx, i+1, &stack[i]);
862 varsContainer_t::iterator functionArg= std::find_if(stack.begin(), stack.end(), &isFunctionOrString);
863 wrapper w(stack.begin(), functionArg, stack.end(), Lhs);
864 bool const withThreads(w.isForeignFunction() && sharedMemory);
865 simple_wrapper prologue(prologueName), epilogue(epilogueName);
867 make_parallel_wrapper(w.argsDataBegin(), w.argsSizesBegin(), w.argsNbBegin(), w.nbRhs(), w.tasksNb()
868 , w.resDataBegin(), w.resSizesBegin()
869 , Lhs, w.getHandle(), prologue, epilogue)(withThreads, nbWorkers, dynamicScheduling, chunkSize);
871 for(int i(0); i != Lhs; ++i)
873 LhsVar(i + 1) = Rhs + i + 1;
876 PutLhsVar(); /* to be moved to gateway */