5c95b112912eef0962d90d87e240e7d750b7ec34
[scilab.git] / scilab / modules / parallel / sci_gateway / cpp / sci_parallel_run.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - DIGITEO - Bernard HUGUENEY
4  *
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
10  *
11  */
12
13 #ifdef _MSC_VER
14 /* remove warning about : 'std::fill_n': Function call with parameters that may be unsafe */
15 #define _SCL_SECURE_NO_WARNINGS
16 #endif
17
18 extern "C" {
19 #include <stdio.h>
20 #include "api_scilab.h"
21 #include "stack-c.h"
22 #include "gw_parallel.h"
23 #include "dynamic_link.h"
24 #include "MALLOC.h"
25 #include "Scierror.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 */
30 }
31
32 #include <cstdlib>
33 #include <cstring>
34 /*#include <sstream> for debug purposes only */
35 #include "parallel_run.hxx"
36
37
38
39 #include <utility>
40 #include <vector>
41 #include <iostream>
42 #include <algorithm>
43 #include <functional>
44 #include <limits>
45 #include <iterator>
46 /*
47  *
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.
51
52
53  [R1, ... , Rm] = parallel_run(A1, ... , Ak, f [,Types] [,Dims])
54
55  If Args are of diffĂ©rent sizes, the smallest are recycled.
56
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
59
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;
62 */
63
64 extern "C"
65 {
66     int sci_parallel_run(char *fname,unsigned long fname_len);
67 }
68
69 namespace
70 {
71     /* to distinguish scilab variable 'address' from usual int* */
72     typedef int* scilabVar_t;
73
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()?*/
76
77     SciErr err; /* store error status from api_scilab functions */
78
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;
82
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)
85     */
86     struct scilabDesc_t :std::pair<char const*, dim_t>
87     {
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)
90         {
91         }
92 /* for debug purposes only
93         std::string toString() const
94         {
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;
98             return buf.str();
99             }
100 */
101     };
102
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 );
108
109     /* ptr to data type */
110     typedef union {
111         double* doublePtr;
112         int* intPtr;
113         char* bytePtr;
114         char** strArrayPtr;
115         void* opaquePtr;
116         int sciCFunction;
117     } unionOfPtrs_t;
118
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)
122      */
123     int getVarType(scilabVar_t var)
124     {
125         int res(0);
126         err= getVarType(pvApiCtx, var, &res);
127         return res;
128     }
129
130     /* return the typename (as in typeof() )
131      * @param int* address of the scilab var
132      * @return the name
133      * only implements the current valid types for parallel_run args.
134      */
135     char const* getTypeName(scilabVar_t var)
136     {
137         char const* res;
138         switch(getVarType(var))
139         {
140         case sci_matrix:
141         {
142             res= "constant";
143             break;
144         }
145         default:
146         {
147             res="unimplemented type in getTypeName";
148         }
149         }
150         return res;
151     }
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.
155      */
156     dim_t getRowsCols(scilabVar_t var)
157     {
158         dim_t res(0,0);
159         err= getVarDimension(pvApiCtx, var, &res.first, &res.second);
160         return res;
161     }
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.
165      */
166     int getRows(scilabVar_t var)
167     {
168         return getRowsCols(var).first;
169     }
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.
173      */
174     int getCols(scilabVar_t var)
175     {
176         return getRowsCols(var).second;
177     }
178
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
182      */
183     bool isFunctionOrString(scilabVar_t var){
184         bool res;
185         switch(getVarType(var))
186         {
187         case sci_u_function :
188         case sci_c_function :
189         {
190             res= true;
191             break;
192         }
193         case sci_strings :
194         {
195             res= (getRows(var) == 1) && (getCols(var) == 1);
196             break;
197         }
198         default :
199         {
200             res= false;
201         }
202         }
203         return res;
204     }
205     /* get ptr to data of a Scilab variable
206      * @param var 'address' of the scilab var
207      * @return ptr to the data
208      */
209     unionOfPtrs_t getData(scilabVar_t var)
210     {
211         unionOfPtrs_t res={0};
212         switch(getVarType(var)) // unhandled data types should be caught during arg validation
213         {
214         case sci_matrix :
215         {
216             if(!isVarComplex(pvApiCtx, var))
217             {
218                 int unused;
219                 err= getMatrixOfDouble(pvApiCtx, var, &unused, &unused, &res.doublePtr);
220             }
221             else
222             {/* TODO suggest workaround in tutorial */
223
224 //              std::cerr<<"complex data not yet implemented var @"<<var<<std::endl;
225             }
226             break;
227         }
228         case sci_strings :
229         {
230 //          std::cerr<<"getData() string data not yet implemented"<<std::endl;
231             break;
232         }
233         default :
234         {
235 //          std::cerr<<"getData() data type"<<getVarType(var)<<" not yet implemented"<<std::endl;
236 //          abort();
237         }
238         }
239         return res;
240     }
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.
244      */
245     std::size_t getSizeOfElement(scilabVar_t var)
246     {
247         std::size_t res(0);
248         switch(getVarType(var))
249         {
250         case sci_matrix :
251         {
252             res= sizeof(double);
253             break;
254         }
255         default : /* returns 0 */
256         {
257 //    std::cerr<<"getSizeOfElt() @"<<var<<":data type not yet implemented"<<std::endl;
258         }
259         }
260         return res;
261     }
262
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.
266      */
267     std::size_t getSizeOfColumn(scilabVar_t var)
268     {
269         return getSizeOfElement(var) * getRows(var) ;
270     }
271
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.
275      */
276     std::size_t getSizeOfData(scilabVar_t var)
277     {
278         return getSizeOfColumn(var) * getCols(var) ;
279     }
280
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
286      */
287     dim_t sliceOrConcat(dim_t d, std::size_t n=1)
288     {
289         switch(n)
290         {
291         case 0 :
292         {
293             d.second= 1; /* slice : one column */
294             break;
295         }
296         case 1:
297             break;
298         default :
299         {
300             d.first *= d.second;
301             d.second = static_cast<int>(n);
302         }
303         }
304         return d;
305     }
306
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
313      */
314     scilabDesc_t getDesc(scilabVar_t var, std::size_t n=1)
315     {
316         return scilabDesc_t( getTypeName(var),sliceOrConcat(getRowsCols(var), n));
317     }
318
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.
326      */
327     scilabVar_t allocVar(scilabDesc_t d, std::size_t n=1)
328     {
329         scilabVar_t res(0);
330         if(std::strcmp(d.first, "constant")==0)
331         {
332             double* unused;
333             dim_t toAlloc(sliceOrConcat(d.second, n));
334             err= allocMatrixOfDouble(pvApiCtx, ++currentTop, toAlloc.first, toAlloc.second, &unused);
335             ++Nbvars;
336 //          std::cerr<<"alloc var :"<<d.toString()<<" @"<<(currentTop)<<" with Nbvars="<<Nbvars<<std::endl;
337             err= getVarAddressFromPosition(pvApiCtx, currentTop, &res);
338         }
339         else /* unhandled type should be caught at arg validation time */
340         {
341 //          std::cerr<<"allocVar() "<<d.first<<" data type not yet implemented"<<std::endl;
342         }
343         return res;
344     }
345     /* Ensures that the complete expected var is filled with reasonable default values
346      * when the returned var was smaller than expected
347      *
348      * i.e. we just copied the data from a resultVar @ varData
349      * when we were expecting an expectedVar : we fill the rest.
350      *
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.
355      */
356     void fillUndefinedData(void* varData, scilabDesc_t resultVar, scilabDesc_t expectedVar)
357     {
358         if(!std::strcmp(resultVar.first, expectedVar.first))
359         {
360             if(!strcmp(resultVar.first, "constant"))
361             {
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() );
366
367             }
368         }
369     }
370     /*
371       wrapper on a native c function or a scilab macro called on scilab variables.
372
373       constructed on :
374       - scilab variable for the function (external native function name, sci_c_function (buggy) or macro name)scilab matrices of arguments
375       - expected lhs
376
377       upon construction, allocate scilab result variables and computes all necessary meta data.
378     */
379     struct wrapper {
380
381         typedef std::vector<std::size_t> sizes_container;
382
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)
388          */
389         template<typename VarsIt>
390         wrapper(VarsIt begin, VarsIt functionIt, VarsIt end, std::size_t functionLhs)
391         {
392             registerArgs(begin, functionIt);
393             n= *std::max_element(argsNb.begin(), argsNb.end());
394             getFunction(*functionIt);
395             allocCompleteResults(begin, functionIt+1, end, functionLhs);
396         }
397
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
401          */
402         void operator()(char const** args, char ** res)
403         {
404             (*this.*(this->fPtr))(args, res);
405         }
406
407         /* It is idiomatic to pass functors by value in C++, but our wrapper is heavy,
408          * so we provide a lightweight handle */
409         struct handle
410         {
411             handle(wrapper& r) : w(r)
412             {
413             }
414             /* just forward to the underlying wrapper */
415             void operator()(char const** args, char ** res) const
416             {
417                 w(args, res);
418             }
419             wrapper& w;
420         };
421         handle getHandle()
422         {
423             return handle(*this);
424         }
425         /* @return begin iterator to the array of pointers to arguments data */
426         char const*const* argsDataBegin() const
427         {
428             return &argsData[0].bytePtr;
429         }
430         /* @return begin iterator to the array of arguments sizes */
431         std::size_t const* argsSizesBegin() const
432         {
433             return &argsSizes[0];
434         }
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
437         {
438             return &argsNb[0];
439         }
440         /* @return nb of tasks (calls) to perform = max(args_nb_begin(), args_nb_begin()+rhs) */
441         std::size_t tasksNb() const
442         {
443             return n;
444         }
445         /* @return begin iterator to the array of pointers to result data */
446         char * const* resDataBegin()
447         {
448             return &resData[0].bytePtr;
449         }
450         /* @return begin iterator to the array of results sizes */
451         std::size_t const* resSizesBegin() const
452         {
453             return &resSizes[0];
454         }
455         /* @return nb of rhs vars of the function */
456         std::size_t nbRhs() const
457         {
458             return rhsDesc.size();
459         }
460         /* @return true if the underlying function is a foreign function, false if it is a Scilab macro */
461         bool isForeignFunction() const
462         {
463             return function.toCall !=0 ;
464         }
465
466     private:
467         /* ptr to foreign function (types used where storing, calling or wrapping) */
468         union{
469             functionToCall_t toCall;
470             loadedFunction_t toLoad;
471             wrapperFunction_t wrapper;
472         } function;
473         /* register a matrix of arguments to be used as rhs) */
474         template<typename ArgIt>
475         void registerArgs(ArgIt it, ArgIt end)
476         {
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*/
481         }
482
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)
489         {
490             lhsDesc.resize(nbLhs);
491             if(resBegin != resEnd)
492             {
493                 if(getVarType(*resBegin) == sci_strings)
494                 {
495                     //    std::cerr<<"we have a type lhs arg\n";
496                     ++resBegin;
497                 }
498                 if(resBegin != resEnd)
499                 {
500                     if(getVarType(*resBegin) == sci_matrix)
501                     {
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);
505                         switch(tmp.second)
506                         {
507
508                         case 2:
509                         {
510                             //            std::cerr<<"we have rows and cols\n";
511                             for(int i(0); i < tmp.first && i< tmp.first*tmp.second; ++i)
512                             {
513                                 lhsDesc[i].second.first= static_cast<int>(data[i]);
514                                 lhsDesc[i].second.second=static_cast<int>(data[i+tmp.first]);
515                             }
516                             break;
517                         }
518                         case 1:
519                         {
520                             //            std::cerr<<"we have rows \n";
521                             for(int i(0); i< tmp.first && i< tmp.first*tmp.second; ++i)
522                             {
523                                 lhsDesc[i].second.first= static_cast<int>(data[i]);
524                             }
525                             break;
526                         }
527                         default :
528                         {
529                             Scierror(999,_("%s: Wrong size of input argument #%d: Number of columns are incompatible ")
530                                      ,currentFname, std::distance(begin, resBegin));
531                         }
532                         }
533                     }
534                 }
535             }
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);
541         }
542
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 :(
546          */
547         int getFunction(scilabVar_t var) {
548             function.toCall= 0;
549             switch(getVarType(var))
550             {
551             case sci_c_function : {
552                 int unused[2];
553                 GetRhsVar(2, EXTERNAL_DATATYPE, unused, unused+1, &scilabFunction);
554                 fPtr = &wrapper::macro<false>;
555                 break;
556             }
557             case sci_strings : {
558                 char* funName;
559                 getAllocatedSingleString(pvApiCtx, var, &funName);
560                 int found;
561                 found=SearchInDynLinks(funName, &function.toLoad);
562                 fPtr= &wrapper::nativeFunction;
563                 if(found == -1)
564                 {
565                     /* should check amongst defined macros with getmacroslist (cf dans core/src/c/getvariablesname.c) and check that type is sci_XXX */
566                     function.toCall=0;
567                     scilabFunctionName= funName;
568                     scilabFunctionNameLength= std::strlen(scilabFunctionName);
569                     fPtr= &wrapper::macro<true>;
570                 }
571             }
572             }
573             return 0;
574         }
575
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
580          */
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)
587             {
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));
590             }
591
592             int  sciRhs = static_cast<int>(rhsDesc.size());
593             int  sciLhs = static_cast<int>(lhsDesc.size());
594
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)
598             {
599                 double* unused;
600                 err= allocMatrixOfDouble(pvApiCtx, ++currentTop, 0, 0, &unused);
601             }
602             Nbvars = Rhs + Lhs + sciRhs;
603             bool success(byName
604                           ? C2F(scistring)(&sciArgPos, scilabFunctionName, &sciLhs, &sciRhs, static_cast<unsigned long>(scilabFunctionNameLength))
605                           : C2F(scifunction)(&sciArgPos, &scilabFunction, &sciLhs, &sciRhs)
606                          );
607             // result r is now on first position on stack
608             {
609                 Nbvars = static_cast<int>(Rhs + Lhs + sciRhs + dummyVars);
610                 int resPos = Rhs + Lhs + 1; //+1
611
612                 for( std::vector<scilabDesc_t>::iterator it(lhsDesc.begin())
613                          ; it != lhsDesc.end(); ++it, ++resPos, ++res)
614                 {
615                     scilabVar_t scilabRes;
616                     if(success)
617                     {
618                         err= getVarAddressFromPosition(pvApiCtx, resPos, &scilabRes);
619                     }
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 */
625                     }
626                     else
627                     { /* copy the returned data */
628                         memcpy(*res, getData(scilabRes).bytePtr, getSizeOfData(scilabRes));
629                         resDesc= getDesc(scilabRes);
630                     }
631                     fillUndefinedData(*res, resDesc, *it);
632                 }
633                 Nbvars= saveNbvars;
634                 currentTop=saveTop;
635             }
636         }
637         void nativeFunction(char const** args, char ** res)
638         {
639             function.toCall(args, res);
640         }
641
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;
644
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 */
649
650         /* the member function to call, dispatches to macro of foreign function */
651         void(wrapper::*fPtr)(char const** args, char ** res);
652
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 */
656
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*/
660
661     };
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
667      *
668      * @retun true is the args are valid */
669     bool check_args(void) {
670         {
671             if(Rhs<2) { return false; }
672             bool before_function(true), at_least_one_arg(false);
673             bool ok(true);
674             for( int pos(1); pos <= Rhs && ok; ++pos) {
675                 int* addr;
676                 err= getVarAddressFromPosition(pvApiCtx, pos, &addr);
677                 int type;
678                 err= getVarType( pvApiCtx, addr, &type );
679                 if (before_function) {
680                     switch (type) {
681                     case sci_matrix : {
682                         /* check not complex "%s: This feature has not been implemented.\n" */
683                         ok= !isVarComplex( pvApiCtx, addr);
684                         at_least_one_arg= true;
685                         break;
686                     }
687                     case sci_strings : {
688                         /* check dim = 1x1 */
689                         int rows, cols;
690                         err= getMatrixOfString(pvApiCtx, addr, &rows, &cols, 0,0);
691                         ok= (rows == 1) && (cols == 1);
692                     }/* no break */
693                     case sci_c_function:{
694                         before_function= false;
695                         break;
696                     }
697                     default : {
698                         Scierror(999,_("%s: Wrong type for input argument #%d: A string expected.\n"),currentFname, 1);
699                         ok= false;
700                     }
701                     }
702                 } else {
703                     switch (type) {
704                     case sci_strings :{
705                         break;
706                     }
707                     case sci_matrix :{
708                         /* check not complex and ncol <=2 */
709                         ok= !isVarComplex( pvApiCtx, addr);
710                         if(ok) {
711                             int rows, cols;
712                             double* unused;
713                             err= getMatrixOfDouble(pvApiCtx, addr, &rows, &cols, &unused);
714                             ok= (cols <= 2);
715                             break;
716                         }
717                     }
718                     case sci_mlist :{ /* config plist (which is a mlist) */
719                         break;
720                     }
721
722                     default : {
723                         ok= false;
724                     }
725                     }
726                 }
727             }
728             return ok && at_least_one_arg && (!before_function);
729         }
730     }
731
732     /* Get configuration options form the config parameter
733      *
734      * in :
735      * @param config_arg_pos position where the config arg can be
736      *
737      * in/out:
738      *
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"
745      *
746      * @return bool true if there was a configuration argument in position config_arg_pos.
747      */
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){
750         int log(0);
751         int* addr = NULL;
752         getVarAddressFromPosition(pvApiCtx, config_arg_pos, &addr);
753         bool has_config_arg(checkPList(pvApiCtx, addr) != 0);
754         if(has_config_arg) {
755             int found;
756             getIntInPList(pvApiCtx, addr, "nb_workers", &nb_workers, &found, nb_workers, log,  CHECK_NONE);
757             int tmp;
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);
765         }
766         return has_config_arg;
767     }
768
769     /*
770       A simple wrapper just wraps prologues and epilogues, each taking only a scalar argument (the process number).
771     */
772     struct simple_wrapper{
773         /* the constructor
774          * @param the macro or foreign function name, empty string allowed: the function then does nothing.
775          */
776         explicit simple_wrapper(char const* name):fun(name)
777         {
778         }
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.
781          */
782         void operator()(int i)
783         {
784             if(*fun)
785             { /* do nothing on empty name */
786                 union
787                 {
788                     loadedFunction_t toLoad;
789                     simpleFunction_t toCall;
790                 } function;
791                 int found= SearchInDynLinks(const_cast<char*>(fun), &function.toLoad);
792                 if(found != -1)
793                 {
794                     function.toCall(i);
795                 }
796                 else
797                 {
798                     double* tmpPtr;
799                     err= allocMatrixOfDouble(pvApiCtx, ++Top, 1, 1, &tmpPtr);
800                     *tmpPtr= static_cast<double>(i);
801                     ++Nbvars;
802                     int lhs(0), rhs(1);
803                     C2F(scistring)(&(Top), const_cast<char*>(fun), &lhs, &rhs, static_cast<unsigned long>(strlen(fun)));
804                     --Nbvars;
805                     --Top;
806                 }
807             }
808         }
809         char const* fun;
810     };
811 }
812
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.
817  *
818  * Calling parallel_run is
819  * 1 checking args
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
826  *
827  */
828 int sci_parallel_run(char *fname,unsigned long fname_len)
829 {
830     typedef std::vector<scilabVar_t> varsContainer_t;
831     currentFname=fname; //get_fname(fname, fname_len); uses a static buffer :(
832     currentTop= Rhs;
833 #ifdef _MSC_VER
834     Nbvars = max(Rhs, Top);
835 #else
836     Nbvars = std::max(Rhs, Top);
837 #endif
838     if( !check_args())
839     {
840         Scierror(999,_("%s: Wrong number of input argument(s).\n"),fname);/* need a better error message */
841         PutLhsVar();
842         return 0;
843     }
844
845
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))
854     {
855         --nbArgsToHandle;
856     }
857     varsContainer_t stack(nbArgsToHandle);
858     for(int i(0); i!= nbArgsToHandle; ++i)
859     {
860         err= getVarAddressFromPosition(pvApiCtx, i+1, &stack[i]);
861     }
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);
866
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);
870
871     for(int i(0); i != Lhs; ++i)
872     {
873         LhsVar(i + 1) = Rhs + i + 1;
874     }
875
876     PutLhsVar(); /* to be moved to gateway */
877
878     return 0;
879 }