Oxygen Basic

Information => Open Forum => Topic started by: JRS on September 19, 2014, 10:39:16 PM

Title: SBT
Post by: JRS on September 19, 2014, 10:39:16 PM
I'm working on a new Script BASIC extension module to run one or more instances of SB as an extension module function call. (sbt.so) The created instance uses libscriba.so which is the Script BASIC runtime in a shared object. Charles's DLLC for Windows already does this and more. This is primarily for Linux.

embed.sb
Code: [Select]
DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"

SB_New "test.sb", "JRS"

test.sb
Code: [Select]
cmd = COMMAND()

PRINT "ARG = ",cmd,"\n"

FOR x = 1 TO 5
PRINT x,"\n"
NEXT

Output


jrs@laptop:~/sb/sb22/sbt$ time scriba embed.sb
ARG = JRS
1
2
3
4
5

real   0m0.005s
user   0m0.004s
sys   0m0.004s
jrs@laptop:~/sb/sb22/sbt$


The resulting time is scriba loading and processing the embed.sb script which instantiates another SB object that runs test.sb.  8)

Threading support will be included as an optional run mode. (Load/Run, Load Only, Load/Run as thread)
Title: Re: SBT
Post by: JRS on September 20, 2014, 02:39:28 AM
Here is the beginning of my proof of concept sbt.so extension module. (in C BASIC)

Code: C
  1. // Script BASIC Thread extension module
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <math.h>
  8. #include <time.h>
  9. #include "../../basext.h"
  10. #include "../../scriba.h"
  11. #include "cbasic.h"
  12.  
  13.  
  14. /****************************
  15.  Extension Module Functions
  16. ****************************/
  17.  
  18. besVERSION_NEGOTIATE
  19.   RETURN_FUNCTION((int)INTERFACE_VERSION);
  20. besEND
  21.  
  22. besSUB_START
  23.   DIM AS long PTR p;
  24.   besMODULEPOINTER = besALLOC(sizeof(long));
  25.   IF (besMODULEPOINTER EQ NULL) THEN_DO RETURN_FUNCTION(0);
  26.   p = (long PTR)besMODULEPOINTER;
  27.   RETURN_FUNCTION(0);
  28. besEND
  29.  
  30. besSUB_FINISH
  31.   DIM AS long PTR p;
  32.   p = (long PTR)besMODULEPOINTER;
  33.   IF (p EQ NULL) THEN_DO RETURN_FUNCTION(0);
  34.   RETURN_FUNCTION(0);
  35. besEND
  36.  
  37.  
  38. /*****************************
  39.  Create Script BASIC Instance
  40. *****************************/
  41.  
  42. besFUNCTION(SB_New)
  43.   DIM AS pSbProgram pProgram;
  44.   DIM AS char PTR sbfilename;
  45.   DIM AS char PTR sbcmdline;
  46.   besARGUMENTS("z[z]")
  47.     AT sbfilename, AT sbcmdline
  48.   besARGEND
  49.   pProgram = scriba_new(malloc,free);
  50.   scriba_SetFileName(pProgram, sbfilename);
  51.   scriba_LoadSourceProgram(pProgram);
  52.   scriba_Run(pProgram, sbcmdline);
  53.   scriba_destroy(pProgram);
  54.   besRETURNVALUE = NULL;
  55. besEND
  56.  
Title: Re: SBT
Post by: JRS on September 20, 2014, 09:39:19 AM
Charles,

I'm creating a higher level wrapper to the SB embedding API with the sbt ext. module. I want to get the basic embedding functions working before enabling threads. Do see any issues with IUP's message pump having multiple instances if not being run in a thread? Any guidance or code you wish to contribute to this DLLC (like) for Linux effort as it unfolds would be much appreciated.

John
Title: Re: SBT
Post by: Charles Pegge on September 20, 2014, 12:38:52 PM

Hi John,

You would only ever need one instance of IUP in one application. Preferably, only one thread from your SB extn should be calling IUP. But I am not a thread expert. If you could think of an app that uses IUP, something that needs to work in the background while IUP runs the GUI. ..
Title: Re: SBT
Post by: Mike Lobanovsky on September 20, 2014, 02:23:22 PM
You would only ever need one instance of IUP in one application.

This ain't so, Charles. There are a hellofalot of graphics apps around that use dozens of worker threads with scheduled access to a common drawing canvas to draw arcs or ellipses in segments and sectors, or images, in tiles. For instance, think of JPEG images or fast ray tracers. IUP isn't just a windowing system but also a powerful drawing library AFAIK, similar to SDL.
Title: Re: SBT
Post by: JRS on September 20, 2014, 02:26:14 PM
I got it working. (finally)

Code: Text
  1. // Script BASIC Thread extension module
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <math.h>
  8. #include <time.h>
  9. #include "../../basext.h"
  10. #include "../../scriba.h"
  11. #include "cbasic.h"
  12.  
  13.  
  14. /****************************
  15.  Extension Module Functions
  16. ****************************/
  17.  
  18. besVERSION_NEGOTIATE
  19.   RETURN_FUNCTION((int)INTERFACE_VERSION);
  20. besEND
  21.  
  22. besSUB_START
  23.   DIM AS long PTR p;
  24.   besMODULEPOINTER = besALLOC(sizeof(long));
  25.   IF (besMODULEPOINTER EQ NULL) THEN_DO RETURN_FUNCTION(0);
  26.   p = (long PTR)besMODULEPOINTER;
  27.   RETURN_FUNCTION(0);
  28. besEND
  29.  
  30. besSUB_FINISH
  31.   DIM AS long PTR p;
  32.   p = (long PTR)besMODULEPOINTER;
  33.   IF (p EQ NULL) THEN_DO RETURN_FUNCTION(0);
  34.   RETURN_FUNCTION(0);
  35. besEND
  36.  
  37.  
  38. /**********************
  39.  Script BASIC Instance
  40. **********************/
  41.  
  42. besFUNCTION(SB_New)
  43.   DIM AS pSbProgram pProgram;
  44.   pProgram = scriba_new(malloc,free);
  45.   besRETURN_LONG(pProgram);
  46. besEND
  47.  
  48. besFUNCTION(SB_Load)  
  49.   DIM AS int sbobj;
  50.   DIM AS char PTR sbfilename;  
  51.   DIM AS int rtnval;
  52.   besARGUMENTS("iz")
  53.     AT sbobj, AT sbfilename
  54.   besARGEND
  55.   rtnval = scriba_SetFileName(sbobj, sbfilename);
  56.   scriba_LoadSourceProgram(sbobj);
  57.   besRETURN_LONG(rtnval);
  58. besEND
  59.  
  60. besFUNCTION(SB_Run)  
  61.   DIM AS int sbobj;
  62.   DIM AS char PTR sbcmdline;
  63.   DIM AS int rtnval;
  64.   besARGUMENTS("iz")
  65.     AT sbobj, AT sbcmdline
  66.   besARGEND
  67.   rtnval = scriba_Run(sbobj, sbcmdline);
  68.   besRETURN_LONG(rtnval);
  69. besEND
  70.  
  71. besFUNCTION(SB_Destroy)
  72.   DIM AS int sbobj;
  73.   besARGUMENTS("i")
  74.     AT sbobj
  75.   besARGEND
  76.   scriba_destroy(sbobj);
  77.   RETURN_FUNCTION(0);
  78. besEND
  79.  

Code: Text
  1. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  2. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  3. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  4. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  5.  
  6. sb = SB_New()
  7. SB_Load sb, "test.sb"
  8. SB_Run sb, "JRS"
  9. SB_Destroy sb
  10.  

Output

jrs@laptop:~/sb/sb22/sbt$ time scriba embed.sb
ARG = JRS
1
2
3
4
5

real   0m0.006s
user   0m0.004s
sys    0m0.000s
jrs@laptop:~/sb/sb22/sbt$

Title: Re: SBT
Post by: Mike Lobanovsky on September 20, 2014, 02:40:51 PM
Patience wears away stones. :)
Title: Re: SBT
Post by: JRS on September 20, 2014, 03:01:30 PM
Quote
Patience wears away stones.

Questioning your own stupidity sometimes has merit.  ::)

Next up is passing strings as SB scripts. This will also act as an on-the-fly expression evaluator. I also plan on testing if the MT module works without have to set up webdata like in sbhttpd. The end goal is that any instance can access the functions and variables of any other instance. Each SB instance can independently load whatever ext. modules it wishes to use. If sbt in IMPORTed then it can use the embedding API to access any of the other instances as long as it has a handle to the SB program object. All theory at this point.  8)

Title: Re: SBT
Post by: Mike Lobanovsky on September 20, 2014, 03:34:54 PM
The end goal is that any instance can access the functions and variables of any other instance.

IIRC it ain't possible. The worker threads can access either global memory (that's the top-level memory of main thread) or their own thread pools. Criss cross access ain't allowed.
Title: Re: SBT
Post by: JRS on September 20, 2014, 03:52:33 PM
Quote
IIRC it ain't possible.

Yes it is. SB instances contain their own pSBData structure and use libscriba.so for the runtime. This already works in DLLC so I'm not breaking new ground here. I have sbhttpd as a working model if I exclude the HTTP / service parts of it.

Title: Re: SBT
Post by: Mike Lobanovsky on September 20, 2014, 05:04:05 PM
Are you sure cross thread data interchange doesn't go through a process level (i.e. global memory) proxy -- for example, that very libscriba.so -- rather than directly as you say?

Then anyway, what you call instances might not be actual threads but rather separate processes, after all. I'm not familiar with DLLC though. Let's see what Charles has to say on the subject.
Title: Re: SBT
Post by: JRS on September 20, 2014, 06:07:50 PM
For this first phase, the SB instances will not be running in a thread but as static sub-processes that get run one at a time. They are just static memory structures when not being called. The MT extension module manages a share memory/variable space each process (threaded or not) can access and be seen by the other SB processes. You can't make embedding calls from the host if the process is running as a thread. MT is the only way to share memory space with threaded instances.
Title: Re: SBT
Post by: JRS on September 20, 2014, 06:41:22 PM
It seems that the Script BASIC scriba_LoadProgramString() function requires the binary (tokenized ready to run) code as a string. :(  I would need to make a few other calls to create the binary program string. (phase II)

I've moved on to calling functions and get/set variables in the started instance from the host SB.
Title: Re: SBT
Post by: Charles Pegge on September 20, 2014, 08:42:13 PM
Mike,

DLLC supports callbacks, by storing the callback messages in channels, which form a shared table, accessible to all SB progs. Using the channel system, a callback can optionally be relayed to an SB procedure - an interprative callback.

DLLC also enables SB programs to run in separate threads, in parallel with the main.

As I recall, it is necessary to use separate threads for the relayed callbacks, otherwise GPF!  SB does not like running more than one procedure at a time in the same thread.

In many instances it may be more practical to periodically monitor the channel table for callback messages, without resorting to threads and all their complications.
Title: Re: SBT
Post by: Mike Lobanovsky on September 21, 2014, 01:07:31 AM
Hi Charles,

Thanks for clarifying the DLLC mechanics.

DLLC supports callbacks, by storing the callback messages in channels, which form a shared table, accessible to all SB progs.
That's exactly what I meant by the glabal memory proxy.

Quote
SB does not like running more than one procedure at a time in the same thread.
That might be indicative of yet some global(s) in the SB engine left out without thread safe access precautions. But then it's only easy to overlook some minute detail in such a complex mechanism as a variant-based interpreter.
Title: Re: SBT
Post by: JRS on September 21, 2014, 01:18:25 AM
Quote
But then it's only easy to overlook some minute detail in such a complex mechanism as a variant-based interpreter.

SB is forgiving for the most part but when it decides to fight with you, it can get bloody.
Title: Re: SBT
Post by: JRS on September 26, 2014, 10:48:51 PM
Charles,

If you have time I could use your guidance with our famous scriba_CallArgEx embedded indirect script calling function, The MyICall example in the trial extension model works but it's an extension API call not an embedded API call which I'm trying to do. I took a peek at your DLLC code but your calling the function by address and not a scriba.h  scriba_CallArgEx C call. Here is were I'm at with the SB_Sub function in the SBT extension module. Can you show me how I can substitute besHOOK_CALLSCRIBAFUNCTION with the embedding scriba_CallArgEx call?

Code: [Select]
besFUNCTION(SB_Address)
  DIM AS int sbobj, funcsernum;
  DIM AS char PTR funcname;
  besARGUMENTS("iz")
    AT sbobj, AT funcname
  besARGEND
  funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  besRETURN_LONG(funcsernum);
besEND

besFUNCTION(SB_Sub)
  DIM AS VARIABLE Argument;
  DIM AS VARIABLE pArgument;
  DIM AS VARIABLE FunctionResult;
  DIM AS unsigned long ulEntryPoint;
  DIM AS unsigned long i;
  DIM AS int sbobj;
  Argument = besARGUMENT(1);
  besDEREFERENCE(Argument);
  sbobj = LONGVALUE(Argument);
  Argument = besARGUMENT(2);
  besDEREFERENCE(Argument);
  ulEntryPoint = LONGVALUE(Argument);
  pArgument = besNEWARRAY(0,besARGNR-3);
  for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
     pArgument->Value.aValue[i-3] = besARGUMENT(i);
     }
  besHOOK_CALLSCRIBAFUNCTION(ulEntryPoint, pArgument->Value.aValue,  besARGNR-2, &FunctionResult);
  for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
     pArgument->Value.aValue[i-3] = NULL;
     }
  besRELEASE(pArgument);
  besRELEASE(FunctionResult);
besEND

Here is Dave's C code used in his COM extension module that does what I want but it hard to see through the COM wrapper.

Code: [Select]
VARIANT __stdcall SBCallBackEx(int EntryPoint, VARIANT *pVal)
{
#pragma EXPORT

  pSupportTable pSt = g_pSt;
  VARIABLE FunctionResult;
  _variant_t vRet;

  if(pSt==NULL){
  MessageBox(0,"pSupportTable is not set?","",0);
  return vRet.Detach();
  }
 
    USES_CONVERSION;
char buf[1024]={0};
    HRESULT hr;
long lResult;
long lb;
long ub;
    SAFEARRAY *pSA = NULL;

//we only accept variant arrays..
if (V_VT(pVal) == (VT_ARRAY | VT_VARIANT | VT_BYREF)) //24588
pSA = *(pVal->pparray);
//else if (V_ISARRAY(pVal) && V_ISBYREF(pVal)) //array of longs here
didnt work out maybe latter
// pSA = *(pVal->pparray);
else
{
if (V_VT(pVal) == (VT_ARRAY | VT_VARIANT))
pSA = pVal->parray;
else
return vRet.Detach();//"Type Mismatch [in] Parameter."
};

    long dim = SafeArrayGetDim(pSA);
if(dim != 1) return vRet.Detach();

lResult = SafeArrayGetLBound(pSA,1,&lb);
lResult = SafeArrayGetUBound(pSA,1,&ub);

lResult=SafeArrayLock(pSA);
    if(lResult) return vRet.Detach();

    _variant_t vOut;
_bstr_t cs;

int sz = ub-lb+1;
    VARIABLE pArg = besNEWARRAY(0,sz);

//here we proxy the array of COM types into the array of script basic
types element by element.
// note this we only support longs and strings. floats will be rounded,
objects converted to objptr()
//  bytes and integers are ok too..basically just not float and
currency..which SB doesnt support anyway..
    for (long l=lb; l<=ub; l++) {
if( SafeArrayGetElement(pSA, &l, &vOut) == S_OK ){
if(vOut.vt == VT_BSTR){
char* cstr = __B2C(vOut.bstrVal);
int slen = strlen(cstr);
pArg->Value.aValue[l] = besNEWMORTALSTRING(slen);
memcpy(STRINGVALUE(pArg->Value.aValue[l]),cstr,slen);
free(cstr);
}
else{
if(vOut.vt == VT_DISPATCH){
//todo register handle? but how do we know the lifetime of it..
//might only be valid until this exits, or forever?
}
pArg->Value.aValue[l] = besNEWMORTALLONG;
LONGVALUE(pArg->Value.aValue[l]) = vOut.lVal;
}
}
    }

  lResult=SafeArrayUnlock(pSA);
  if (lResult) return vRet.Detach();
 
  besHOOK_CALLSCRIBAFUNCTION(EntryPoint,
pArg->Value.aValue,
                             sz,
                             &FunctionResult);

  for (long l=0; l <= sz; l++) {
besRELEASE(pArg->Value.aValue[l]);
     pArg->Value.aValue[l] = NULL;
  }

  if(FunctionResult->vType == VTYPE_STRING){
char* myStr = GetCString(FunctionResult);
vRet.SetString(myStr);
free(myStr);
  }
  else{
  switch( TYPE(FunctionResult) )
  {   
case VTYPE_DOUBLE:
case VTYPE_ARRAY:
case VTYPE_REF:
MessageBoxA(0,"Arguments of script basic types [double, ref, array] not
supported","Error",0);
break;
default:
vRet = LONGVALUE(FunctionResult);
  }
  }

  besRELEASE(pArg);
  besRELEASE(FunctionResult);

  return vRet.Detach();
}
Title: Re: SBT
Post by: Charles Pegge on September 27, 2014, 02:49:24 PM

Hi John,

SB pointy-pointys are so confusing.

I'm trying to familiarise myself with hook functions, but appear to be getting a null pointer to them, in the ExecuteObject (pEo).

This other way of calling requires pProgram:

pst->scriba_CallArgEx)(pSbProgram pProgram, unsigned long lEntryNode, pSbData ReturnValue, unsigned long cArgs, pSbData Args);





Title: Re: SBT
Post by: JRS on September 27, 2014, 03:07:34 PM
Quote
This other way of calling requires pProgram:

I pass the pProgram pointer to every SBT function I call after SB_New().

pst->scriba_CallArgEx)(pSbProgram pProgram, unsigned long lEntryNode, pSbData ReturnValue, unsigned long cArgs, pSbData Args);

Is this a typo above?  I'm assuming that the above is a prototype definition of the function and not what I would use to call it.

Thanks for helping me get embedded SB script function calling working. As Peter said in the docs, this is the most complex function in the API.

Title: SBT
Post by: JRS on September 27, 2014, 05:11:20 PM
Charles,

This hacked example works. The missing piece is creating the argData structure and not having to use scriba_NewSbArgs. This uses the pProgram I'm passing and the the function serial number from SB_Address() of SUB test in the embedded instance.

Code: Text
  1. besFUNCTION(SB_Address)
  2.   DIM AS int sbobj, funcsernum;
  3.   DIM AS char PTR funcname;
  4.   besARGUMENTS("iz")
  5.     AT sbobj, AT funcname
  6.   besARGEND
  7.   funcsernum = scriba_LookupFunctionByName(sbobj, funcname);
  8.   besRETURN_LONG(funcsernum);
  9. besEND
  10.  
  11. besFUNCTION(SB_Sub)
  12.   DIM AS VARIABLE Argument;
  13. //DIM AS VARIABLE pArgument;
  14. //DIM AS VARIABLE FunctionResult;
  15.   DIM AS SbData ReturnData;
  16.   DIM AS pSbData ArgData;
  17.   DIM AS unsigned long ulEntryPoint;
  18.   DIM AS unsigned long i;
  19.   DIM AS int sbobj, ok;
  20.   Argument = besARGUMENT(1);
  21.   besDEREFERENCE(Argument);
  22.   sbobj = LONGVALUE(Argument);
  23.   Argument = besARGUMENT(2);
  24.   besDEREFERENCE(Argument);
  25.   ulEntryPoint = LONGVALUE(Argument);
  26. //pArgument = besNEWARRAY(0,besARGNR-3);
  27. //for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  28. //   pArgument->Value.aValue[i-3] = besARGUMENT(i);
  29. //   }
  30. //besHOOK_CALLSCRIBAFUNCTION(ulEntryPoint, pArgument->Value.aValue,  besARGNR-2, &FunctionResult);
  31.   ArgData = scriba_NewSbArgs(sbobj, "i r s", 10, .20, "Thirty");
  32.   ok = scriba_CallArgEx(sbobj, ulEntryPoint, &ReturnData, 3, ArgData);  
  33.  
  34. //for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  35. //   pArgument->Value.aValue[i-3] = NULL;
  36. //   }
  37. //besRELEASE(pArgument);
  38. //besRELEASE(FunctionResult);
  39. scriba_DestroySbArgs(sbobj, ArgData, 3);
  40. besEND
  41.  

Code: Text
  1. DECLARE SUB SB_New ALIAS "SB_New" LIB "sbt"
  2. DECLARE SUB SB_Configure ALIAS "SB_Configure" LIB "sbt"
  3. DECLARE SUB SB_Load ALIAS "SB_Load" LIB "sbt"
  4. DECLARE SUB SB_Run ALIAS "SB_Run" LIB "sbt"
  5. DECLARE SUB SB_NoRun ALIAS "SB_NoRun" LIB "sbt"
  6. DECLARE SUB SB_Address ALIAS "SB_Address" LIB "sbt"
  7. DECLARE SUB SB_Sub ALIAS "SB_Sub" LIB "sbt"
  8. DECLARE SUB SB_Destroy ALIAS "SB_Destroy" LIB "sbt"
  9.  
  10. sb = SB_New()
  11. ok = SB_Configure(sb, "/etc/scriba/basic.conf")
  12. ok = SB_Load(sb, "test3.sb")
  13. ok = SB_NoRun(sb)
  14. faddr = SB_Address(sb, "main::test")
  15. SB_Sub(sb, faddr)
  16. SB_Destroy(sb)
  17.  

Code: Text
  1. ' Test3 - Host indirect call
  2.  
  3. SUB test(arg1,arg2,arg3)
  4.   PRINT arg1,"\n"
  5.   PRINT FORMAT("%g",arg2),"\n"
  6.   PRINT arg3,"\n"
  7. END SUB
  8.  

Output

jrs@laptop:~/sb/sb22/sbt$ scriba embed.sb
10
0.2
Thirty
jrs@laptop:~/sb/sb22/sbt$
Title: Re: SBT
Post by: JRS on September 27, 2014, 07:49:50 PM
Charles,

This is where all the action is.

Code: Text
  1. =H scriba_NewSbArgs()
  2.  
  3. Whenever you want to handle the variable values that are returned by the scriba subroutine
  4. you have to call R<scriba_CallArgEx()>. This function needs the arguments passed in an array of T<SbDtata> type.
  5.  
  6. This function is a usefuly tool to convert C variables to an array of T<SbData>
  7.  
  8. /*FUNCTION*/
  9. SCRIBA_MAIN_LIBSPEC pSbData scriba_NewSbArgs(pSbProgram pProgram,
  10.                          char *pszFormat, ...
  11.   ){
  12. /*noverbatim
  13. The arguments passed are
  14.  
  15. =itemize
  16. =item T<pProgram> is the class variable
  17. =item T<pszFormat> is the format string
  18. =noitemize
  19.  
  20. The format string is case insensitive. The characters T<u>, T<i>, T<r>, T<b> and T<s> have meaning.
  21. All other characters are ignored. The format characters define the type of the arguments
  22. from left to right.
  23.  
  24. =itemize
  25. =item T<u> means to pass an T<undef> to the SUB. This format character is exceptional that it does not
  26. consume any function argument.
  27. =item T<i> means that the next argument has to be T<long> and it is passed to the BASIC SUB as an integer.
  28. =item T<r> means that the next argument has to be T<double> and it is passed to the BASIC SUB as a real.
  29. =item T<s> means that the next argument has to be T<char *> and it is passed to the BASIC SUB as a string.
  30. =item T<b> means that the next two arguments has to be T<long cbBuffer> and T<unsigned char *Buffer>.
  31. The T<cbBuffer> defines the leng of the T<Buffer>.
  32. =noitemize
  33.  
  34. Example:
  35.  
  36. =verbatim
  37.  
  38. pSbData MyArgs;
  39.  
  40.  
  41.   MyArgs = scriba_NewSbArgs(pProgram,"i i r s b",13,14,3.14,"string",2,"two character string");
  42.   if( MyArgs == NULL )error("memory alloc");
  43.  
  44.   scriba_CallArgEx(pProgram,lEntry,NULL,5,MyArgs);
  45.  
  46. =noverbatim
  47.  
  48. This example passes five arguments to the ScriptBasic subroutine. Note that the last one is only
  49. two character string, the rest of the characters are ignored.
  50.  
  51. CUT*/
  52.   va_list marker;
  53.   unsigned long cArgs,i;
  54.   char *s;
  55.   char *arg;
  56.   pSbData p;
  57.  
  58.   if( pszFormat == NULL )return NULL;
  59.  
  60.   cArgs = 0;
  61.   s = pszFormat;
  62.   while( *s ){
  63.     switch( *s++ ){
  64.       case 'U': /* undef argument */
  65.       case 'u': /* It eats no actual C level caller argument */
  66.  
  67.       case 'B': /* byte argument   */
  68.       case 'b': /* it eats two arguments: a length and the pointer to the byte stream */
  69.  
  70.       case 'S': /* string argument */
  71.       case 's':
  72.       case 'I': /* Integer argument */
  73.       case 'i':
  74.       case 'R': /* Real number argument */
  75.       case 'r':
  76.         cArgs ++;
  77.         break;
  78.       default:; /* ignore all non-format characters */
  79.       }
  80.     }
  81.   p = alloc_Alloc(sizeof(SbData)*cArgs,pProgram->pMEM);
  82.   if( p == NULL )return NULL;  
  83.  
  84.   i = 0;
  85.   va_start(marker,pszFormat);
  86.   s = pszFormat;
  87.   while( *s ){
  88.     switch( *s++ ){
  89.       case 'U':
  90.       case 'u':
  91.         p[i].type = SBT_UNDEF;
  92.         i++;
  93.         break;
  94.       case 'B': /* byte stream argument */
  95.       case 'b':
  96.         p[i].type = SBT_STRING;
  97.         p[i].size = va_arg(marker, long);
  98.         arg = va_arg(marker, char *);
  99.         if( arg == NULL && p[i].size != 0 ){
  100.           p[i++].type = SBT_UNDEF;
  101.           break;
  102.           }
  103.         p[i].size =  strlen(arg);
  104.         if( p[i].size ){
  105.           p[i].v.s = alloc_Alloc(p[i].size,pProgram->pMEM);
  106.           if( p[i].v.s == NULL ){
  107.             while( i ){
  108.               if( p[i].type == SBT_STRING && p[i].v.s )alloc_Free(p[i].v.s,pProgram->pMEM);
  109.               i--;
  110.               }
  111.             alloc_Free(p,pProgram->pMEM);
  112.             return NULL;
  113.             }
  114.           memcpy(p[i].v.s,arg,p[i].size);
  115.           }else{
  116.           p[i].v.s = NULL;
  117.           }
  118.         i++;
  119.         break;
  120.       case 'S': /* string argument */
  121.       case 's':
  122.         p[i].type = SBT_STRING;
  123.         arg = va_arg(marker, char *);
  124.         if( arg == NULL )arg = "";
  125.         p[i].size = strlen(arg);
  126.         if( p[i].size ){
  127.           p[i].v.s = alloc_Alloc(p[i].size,pProgram->pMEM);
  128.           if( p[i].v.s == NULL ){
  129.             while( i ){
  130.               if( p[i].type == SBT_STRING && p[i].v.s )alloc_Free(p[i].v.s,pProgram->pMEM);
  131.               i--;
  132.               }
  133.             alloc_Free(p,pProgram->pMEM);
  134.             return NULL;
  135.             }
  136.           memcpy(p[i].v.s,arg,p[i].size);
  137.           }else{
  138.           p[i].v.s = NULL;
  139.           }
  140.         i++;
  141.         break;
  142.       case 'I': /* Integer argument */
  143.       case 'i':
  144.         p[i].type = SBT_LONG;
  145.         p[i].v.l = va_arg(marker, long);
  146.         i++;
  147.         break;
  148.       case 'R': /* Real number argument */
  149.       case 'r':
  150.         p[i].type = SBT_DOUBLE;
  151.         p[i].v.d = va_arg(marker, double);
  152.         i++;
  153.         break;
  154.       }
  155.     }
  156.  
  157.   return p;
  158.   }
  159.  
  160. /*POD
  161. =H scriba_CallArgEx()
  162.  
  163. This is the most sophisticated function of the ones that call a ScriptBasic subroutine.
  164. This function is capable handling parameters to scriba subroutines, and returning the
  165. modified argument variables and the return value.
  166.  
  167. /*FUNCTION*/
  168. SCRIBA_MAIN_LIBSPEC int scriba_CallArgEx(pSbProgram pProgram,
  169.                      unsigned long lEntryNode,
  170.                      pSbData ReturnValue,
  171.                      unsigned long cArgs,
  172.                      pSbData Args
  173.   ){
  174. /*noverbatim
  175. The arguments:
  176. =itemize
  177. =item T<pProgram> is the program object pointer.
  178. =item T<lEntryNode> is the entry node index where the BASIC subroutine or function starts
  179.       (See R<scriba_Call()> note on how to get the entry node value.)
  180. =item T<ReturnValue> is the return value of the function or subroutine
  181. =item T<cArgs> is the number of argments passed to the function
  182. =item T<Args> argument data array
  183. =noitemize
  184. CUT*/
  185.   int iError;
  186.   VARIABLE vArgs;
  187.   VARIABLE vReturn;
  188.   unsigned long i;
  189.  
  190.   if( cArgs )
  191.     vArgs = memory_NewArray(pProgram->pEXE->pMo,0,cArgs-1);
  192.   else
  193.     vArgs = NULL;
  194.  
  195.   if( vArgs ){
  196.     for( i = 0 ; i < cArgs ; i ++ ){
  197.       switch( Args[i].type ){
  198.         case SBT_UNDEF:
  199.           vArgs->Value.aValue[i] = NULL;
  200.           break;
  201.         case SBT_STRING:
  202.           vArgs->Value.aValue[i] = memory_NewString(pProgram->pEXE->pMo,Args[i].size);
  203.           memcpy(STRINGVALUE(vArgs->Value.aValue[i]),Args[i].v.s,Args[i].size);
  204.           alloc_Free(Args[i].v.s,pProgram->pMEM);
  205.           break;
  206.         case SBT_LONG: /* Integer argument */
  207.           vArgs->Value.aValue[i] = memory_NewLong(pProgram->pEXE->pMo);
  208.           LONGVALUE(vArgs->Value.aValue[i]) = Args[i].v.l;
  209.           break;
  210.         case SBT_DOUBLE: /* Real number argument */
  211.           vArgs->Value.aValue[i] = memory_NewDouble(pProgram->pEXE->pMo);
  212.           DOUBLEVALUE(vArgs->Value.aValue[i]) = Args[i].v.d;
  213.           break;
  214.         }
  215.       }
  216.     }
  217.  
  218.   execute_ExecuteFunction(pProgram->pEXE,lEntryNode,cArgs,vArgs ? vArgs->Value.aValue : NULL ,&vReturn,&iError);
  219.   scriba_UndefSbData(pProgram,ReturnValue);
  220.  
  221.   if( ! iError && vReturn ){
  222.     switch( vReturn->vType ){
  223.       case VTYPE_LONG:
  224.         ReturnValue->type = SBT_LONG;
  225.         ReturnValue->v.l = LONGVALUE(vReturn);
  226.         break;
  227.       case VTYPE_DOUBLE:
  228.         ReturnValue->type = SBT_DOUBLE;
  229.         ReturnValue->v.d = DOUBLEVALUE(vReturn);
  230.         break;
  231.       case VTYPE_STRING:
  232.         ReturnValue->type = SBT_STRING;
  233.         /* we allocate a one byte longer buffer and append a terminating zero */
  234.         ReturnValue->size=STRLEN(vReturn);/* size is w/o the terminating zero */
  235.         ReturnValue->v.s = alloc_Alloc(ReturnValue->size+1,pProgram->pMEM);
  236.         if( ReturnValue->v.s ){
  237.           memcpy(ReturnValue->v.s,STRINGVALUE(vReturn),ReturnValue->size);
  238.           ReturnValue->v.s[ReturnValue->size] = (char)0;
  239.           }
  240.         break;
  241.       default:
  242.         ReturnValue->type = SBT_UNDEF;
  243.         break;
  244.       }
  245.     }
  246.  
  247.   if( vArgs && ! iError ){
  248.     for( i = 0 ; i < cArgs ; i ++ ){
  249.       if( vArgs->Value.aValue[i] == NULL ){
  250.         Args[i].type = SBT_UNDEF;
  251.         continue;
  252.         }
  253.       switch( vArgs->Value.aValue[i]->vType ){
  254.         case VTYPE_LONG:
  255.           Args[i].type = SBT_LONG;
  256.           Args[i].v.l = LONGVALUE(vArgs->Value.aValue[i]);
  257.           break;
  258.         case VTYPE_DOUBLE:
  259.           Args[i].type = SBT_DOUBLE;
  260.           Args[i].v.d = DOUBLEVALUE(vArgs->Value.aValue[i]);
  261.           break;
  262.         case VTYPE_STRING:
  263.           /* we allocate a one byte longer buffer and append a terminating zero */
  264.           Args[i].type = SBT_STRING;
  265.           Args[i].size=STRLEN(vArgs->Value.aValue[i]);/* size is w/o the terminating zero */
  266.           Args[i].v.s = alloc_Alloc(Args[i].size+1,pProgram->pMEM);
  267.           if( Args[i].v.s ){
  268.             memcpy(Args[i].v.s,STRINGVALUE(vArgs->Value.aValue[i]),Args[i].size);
  269.             Args[i].v.s[Args[i].size] = (char)0;
  270.             }
  271.           break;
  272.         default:
  273.           Args[i].type = SBT_UNDEF;
  274.           break;
  275.         }
  276.       }
  277.     }
  278.  
  279.   memory_ReleaseVariable(pProgram->pEXE->pMo,vArgs);
  280.   memory_ReleaseVariable(pProgram->pEXE->pMo,vReturn); /*Tomasz Lacki realised its missing*/
  281.   return iError;
  282.   }
  283.  
Title: Re: SBT
Post by: JRS on September 27, 2014, 08:42:19 PM
Charles,

I think the ext. module API method of getting the arguments is okay and setting the value element of the argData array, What I don't see is the type of element or it's size being assigned. Is that done within the wrapper besHOOK_CALLSCRIBAFUNCTION?

With so many variadic functions used in SB, the transfer of data from arguments to SB data structures should be easier. The scriba_NewSbArgs function was a step in that direction by Peter but that assumes your interface is being done at the C level.

Title: Re: SBT
Post by: Charles Pegge on September 28, 2014, 07:42:17 PM

Hi John,

I still can't get the hook functions pointer. - hacking into SB's data structures.

But I presume besHOOK_CALLSCRIBAFUNCTION works the same way as the CallArgEx function. SbData arrays can be created directly, setting data values and types directly - (except for SB strings!)
Title: Re: SBT
Post by: JRS on September 28, 2014, 07:59:33 PM
Are you saying that SB doesn't require type or size elements of the sbData structure for LONG & REAL and only for STRING? With the besHOOK_CALLSCRIBAFUNCTION macro/function I can pass long, real, string and arrays giving it nothing other than a pointer to an argument. How can that be?

Title: Re: SBT
Post by: Charles Pegge on September 28, 2014, 08:13:01 PM
No, You always have to set the type as well as the value (by union).

Similarly, on returned data, the type has to cased before looking at the value.
Title: Re: SBT
Post by: JRS on September 28, 2014, 08:22:59 PM
The MyICall function in the trial extension module example that comes with SB to help explian how to use the ext, module API works perfect. It works just like the native ADDRESS /ICALL functions. If you look at the code I REM'ed out (ext. API) the only element of the sbData structure being set is the value. Somehow the besHOOK_CALLSCRIBAFUNCTION knows the type and size or if it is an array in whole being passed.  If you can tell me how I can use my passed pProgram pointer with besHOOK_CALLSCRIBAFUNCTION then all my problems are solved.

Title: Re: SBT
Post by: JRS on September 28, 2014, 10:48:11 PM
Charles,

hook_CallScribaFunction (http://www.scriptbasic.org/docs/dg/devguide_2.12.51.html) is called by the macro besHOOK_CALLSCRIBAFUNCTION. This function exposes pExecuteObject pEo . Is this the same as the pProgram pointer I'm passing?
Title: Re: SBT
Post by: Charles Pegge on September 29, 2014, 01:48:29 AM
scriba_callArgEx:

not the same, since it passes sbData structures, rather than individual values.

typedef struct _SbData {
  unsigned char type;
  unsigned long size;
  union {
    double d;
    long   l;
    unsigned char *s;
    } v;
  } SbData, *pSbData;


int (*scriba_CallArgEx)(pSbProgram pProgram, unsigned long lEntryNode, pSbData ReturnValue, unsigned long cArgs, pSbData Args);

     #define besScribaCallArgEx(F0,F1,F2,F3,F4) (pSt->scriba_CallArgEx((F0),(F1),(F2),(F3),(F4)))


besHOOK_CALLSCRIBAFUNCTION params: :o

typedef unsigned char BYTE, *PBYTE;

typedef struct _FixSizeMemoryObject {

  union _fsmoval{
    PBYTE  pValue;
    long   lValue;
    double dValue;
    struct _FixSizeMemoryObject **aValue;
    } Value;
  unsigned long Size;
  BYTE sType;
             
  BYTE vType;
  BYTE State;
 
 
 
 
  struct _FixSizeMemoryObject *next;     
  union {
    struct _FixSizeMemoryObject *prev;   
    struct _FixSizeMemoryObject **rprev;
    }link;
  long ArrayLowLimit, ArrayHighLimit;   
                                         
  } FixSizeMemoryObject, *pFixSizeMemoryObject,
    *MortalList, **pMortalList;


from your iCall:

besHOOK_CALLSCRIBAFUNCTION

  besHOOK_CALLSCRIBAFUNCTION(ulEntryPoint,
                             pArgument->Value.aValue,
                             besARGNR-1,
                             &FunctionResult);

It uses the pEo to access the pHookers function table

(pSt->pEo->pHookers->HOOK_CallScribaFunction(pSt->pEo,(ulEntryPoint),(pArgument->Value.aValue),((pParameters ? pParameters->ArrayHighLimit : 0)-1),(&FunctionResult)))


Unfortunately, I appear to have an incomplete pEo / ExecuteObject with many nulls, including the pHookers pointer.
Title: Re: SBT
Post by: JRS on September 29, 2014, 10:25:35 AM
Quote
Unfortunately, I appear to have an incomplete pEo / ExecuteObject with many nulls, including the pHookers pointer.

I wouldn't mind if I had to call a function to build sbArgs from scriba but that doesn't seem possible other than from C. (no way to build args on the fly)

Peter said he wouldn't be able to look at this until this upcoming weekend. (plate overflowing) :-(


Title: Re: SBT
Post by: JRS on September 29, 2014, 01:10:56 PM
I'm confused. If SB is passing mortal variables as arguments, why can't we used that definition with scriba_CallArgEx? How different is the argData and sbData in relationship to themselves? Everything we need is being passed. Why should it have to be redefined using something like scriba_NewSbArgs?

1. Native ICALL works fine,
2. Trial example MyICall works fine.
3. Missing embedded API arg to sbData routine.
Title: Re: SBT
Post by: Charles Pegge on September 29, 2014, 02:19:07 PM

Those are questions for Peter Verhas. It is a very complex system and extremely demanding for a third party to track.
Title: Re: SBT
Post by: JRS on September 29, 2014, 02:42:19 PM
Thanks Charles for having a look. I'll keep chipping away at it until Peter has time to answer these questions.

Your right, as simple as SB looks from a user standpoint, understanding the magic under the covers is still a challenge. (I've been at it since 2006)
Title: Re: SBT
Post by: JRS on September 29, 2014, 11:25:31 PM
What has me wondering is the hooker aspect of SB. (maybe this topic will get Mike's interest.  :-* ) Was this created to couple data objects in their different forms? Is this only for the extension module side of the SB API? I'm going to try to clone some of the working ICALL code into the SB_Sub() call and manually parse the arguments in stdarg syntax. I'm hoping Peter has some SB function I haven't discovered yet to make all this less painful.
Title: Re: SBT
Post by: Charles Pegge on September 30, 2014, 03:04:59 AM
There are only  10 pHooker references in execute.c, compared with 45 in basext.c. So they are mainly used for the benefit of extension modules.

They  are a form of OOP methods table.

ExecuteObjects are used extensively throughout the source code though.

Title: Re: SBT
Post by: JRS on September 30, 2014, 09:40:27 AM
Using both the ext. and embedding API in unison is SB running on all cylinders. On a positive note, it gives you a much better picture of SB as a whole. As you have already discovered writing DLLC.

 
Title: SBT
Post by: JRS on April 25, 2015, 08:55:54 AM
Charles,

I'm trying to call a SB function, passing arguments and return a result as an embedded extension module call. Can you give me a hand on the All BASIC forum? You are a member there. I'm trying to rally Peter Verhas and AIR to help as well.

John
Title: Re: SBT
Post by: JRS on April 26, 2015, 06:29:31 PM
Hi Charles,

I'm not having much luck solving the scriba_CallArgEx (http://www.allbasic.info/forum/index.php?topic=387.msg4279#msg4279) mystery. Can you post how you did it in DLLC? I really could use a hand with this if you have time.

John
Title: Re: SBT
Post by: Charles Pegge on April 27, 2015, 12:37:27 AM
Hi John,

Is this a pProgram problem?
Title: Re: SBT
Post by: JRS on April 27, 2015, 06:55:27 AM
Hi John,

Is this a pProgram problem?

No, that is working fine. It's dynamically building the ArgData array from the passed wrapper function arguments. It's ICALL as a ext. module function. (see details on All BASIC)

The goal with this project is to show how the Script BASIC API works using itself.  8) I have already discovered a couple bugs which I have informed Peter Verhas of.
Title: Re: SBT
Post by: Charles Pegge on April 28, 2015, 04:23:44 AM
Hi John,

Since we solved that little problem of how to pass string arguments to an SB function, I have not seen any further difficulties with CallArgEx. For integers and floats, Argument blocks can be built from scratch. But when implementing callbacks to an SB function, I think there is a serious potential hazard: to avoid concurrency in the same pProgram.
Title: Re: SBT
Post by: JRS on April 28, 2015, 07:54:39 AM
Charles,

I'm not doing anything special with callbacks or anything else. All I need help with is taking the passed arguments in my wrapper function and build the argument array. Please take a look at the All BASIC thread I have going as it should make things perfectly clear.

Thanks!

John
Title: Re: SBT
Post by: Charles Pegge on April 28, 2015, 10:59:19 AM
Are you using Hook functions? Last time I attempted to use them, I found null pointers in the chain. ie: structures not initialised.

However, using the scriba_ functions, with a valid pProgram:

Code: OxygenBasic
  1.   sbfun=scriba_LookupFunctionByName(pProgram,"main::callbacka")
  2.   if sbfun=0 then print "Unable to locate callbackA" : goto ending
  3.   string s={"One","Two","Three","Four"}
  4.   args=scriba_NewSbArgs(pProgram,"s s s s",s[0],s[1],s[2],s[3])
  5.   count=4
  6.   iError=scriba_CallArgEx(pProgram, sbfun, @ReturnData, count, args)
  7.   scriba_DestroySbArgs(pProgram,args,4)
  8.  

Passing integers {100,200,300,400}, is simpler: Here it is done with an array of longs, instead of using a set of SB data blocks: (2 signifies integer type)


Code: OxygenBasic
  1.   sbfun=scriba_LookupFunctionByName(pProgram,"main::callbacka")
  2.   if sbfun=0 then print "Unable to locate callbackA" : goto ending
  3.   long a={2,4,100,0, 2,4,200,0, 2,4,300,0, 2,4,400,0} : args=@a
  4.   count=4
  5.   iError=scriba_CallArgEx(pProgram, sbfun, @ReturnData, count, args)
  6.  

By the way, I've tested this with your 64bit libScriba.dll (GCC 2013) :)
Title: Re: SBT
Post by: JRS on April 28, 2015, 12:14:50 PM
Sweet! (and 64 bit Windows to boot)

Well, at least we know it works. The trick is to grab the argdata being passed in the wrapper function and share the argument array after the first two with scriba_CallArgEx. The next thing is returning the results of the dynamic SB script function being called. I'm assuming at this point it is nothing more than passing back a pointer to ReturnData.

Title: Re: SBT
Post by: Charles Pegge on April 28, 2015, 12:37:43 PM
Yes, quite easy. You must check the format of the returned data, and use accordingly.

NB! scriba_DestroySbArgs should not be used on the returned data pointer. It will crash.

Code: OxygenBasic
  1.   'READ RETURNED VALUE
  2.  '-------------------
  3.  '
  4.  select ReturnData.typ
  5.     case SBT_UNDEF  : pr+= "Undefined "
  6.     case SBT_DOUBLE : pr+="double "  ReturnData.dbl
  7.     case SBT_LONG   : pr+="long "    ReturnData.lng
  8.     case SBT_STRING : pr+="zstring " ReturnData.str
  9.     case SBT_ZCHAR  : pr+="zchar "   ReturnData.lng
  10.   end select
  11.  
Title: Re: SBT
Post by: JRS on April 28, 2015, 12:39:58 PM
Thanks Charles!

Any chance you could use my C example on All BASIC and make a working function from that?

Title: Re: SBT
Post by: JRS on April 29, 2015, 12:47:43 PM
Hi Charles,

I think I have some progress (http://www.allbasic.info/forum/index.php?topic=387.msg4293#msg4293) to report.  8)

It seems my seg fault issue is returning back to the hosting SB. On a positive note it's getting through the scriba_CallArgEx without error. (passing long/double only)
Title: Re: SBT
Post by: JRS on April 29, 2015, 09:38:09 PM
I got it working mostly but as you remember assigning  C strings to ArgData was a trick you mastered. I have the return value working but it's currently hard coded to allow LONG values only.

That is my two remaining issues I could use your help with.

(See All BASIC for details)
Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 06:17:40 AM
Hi John,

you just need to switch instead of select, and remember to break at the end of each case.

Otherwise, you know more about this stuff than I do :)
Title: Re: SBT
Post by: JRS on April 30, 2015, 08:04:12 AM
Not sure how to respond to that.  :-\

I worked with you on DLLC trying solve this issue of passing C string references to an ArgData array. All I'm asking is how did you end up resolving it?

Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 08:30:02 AM
John, I don't have your test environment though I'm hoping to get tooled-up for C again fairly soon

Reading returned string data was never a problem. You should be able to see it as a regular char*, using printf
Title: Re: SBT
Post by: JRS on April 30, 2015, 09:05:04 AM
I have no problems getting the passed argument string(s) from the SB host. My problem (seg faults) is assigning the passed C string to an ArgData array structure. (.v.s). I remember something about SB defaults to multi-threaded support mode which means that SB is using Peter Verhas's MyAlloc memory manager. Where I left off with you on this was showing you how the scriba_CallArg() function had no problems passing C strings. The next thing I remember is you resolved your issue and DLLC was born.

Quote
I don't have your test environment

This is core SB API and you should be able to (re)test this in O2, MinGW-TDM, or just about anything that can call a C library. (remember SB embedded code challenge?) In this round I'm using SB itself as the host. What better way to show how the Script BASIC API works than using SB itself?
Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 09:38:07 AM
You won't be able to make an arg structure,insert a string pointer into it, and get away with it, whereas it is okay to do this with ints and doubles.

Have you tried inserting some string literals?:

Forcing values is a very useful diagnostic.

Code: [Select]
  args=scriba_NewSbArgs(pProgram,"s s s s",s[0],"one","two","three","four")
  count=4
  iError=scriba_CallArgEx(pProgram, sbfun, @ReturnData, count, args)
Title: Re: SBT
Post by: JRS on April 30, 2015, 09:59:00 AM
My issue is dynamically (at runtime) assigning one of the eight reserved ArgData structures ([zero] through [seven]) with either a LONG, DOUBLE (real), STRING/ZSTR or SB Array definition. I tried to use  scriba_NewSbArgs to created a single ArgData array string  element and assign it to my predefined ArgData array but it didn't work for me. (pSbData vs. SbData mismatch)
Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 10:11:57 AM
scriba_NewSbArgs creates the Arg block, and returns the Args pointer to you for direct use. There must be some extra invisible pointers, or positional sensitivity, which make any copies of the Arg block invalid.
Title: Re: SBT
Post by: JRS on April 30, 2015, 10:26:54 AM
How did you get around this issue in DLLC?
Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 10:49:13 AM

I've just checked: DLLC only supports integer arguments for its  SBcallback relays.

Code: OxygenBasic
  1.   function SbCallBackRelay(...) as sys,external
  2.   =============================================
  3.   indexbase 1
  4.   sys          i,n,v,er
  5.   v=pdata[2] 'eax/rax register.
  6.  SbCallBackInfo c at (@sbcb[v])
  7.   sbData ReturnData
  8.   sbData ArgData at (c.idat+v*8*sizeof SbData)
  9.   n=c.nParams
  10.   for i=1 to n 'max 8
  11.    ArgData[i].typ =SBT_LONG
  12.     ArgData[i].lng =param[i]
  13.   next
  14.   if c.proc
  15.     er=call c.ArgEx  c.pProgram, c.Proc, @ReturnData, n, @ArgData
  16.     return ArgData.lng
  17.   else
  18.     return c.Return
  19.   end if
  20.   end function
  21.  
Title: Re: SBT
Post by: JRS on April 30, 2015, 11:12:37 AM
Quote
I've just checked: DLLC only supports integer arguments for its  SBcallback relays.

So you're not a wizard after all.  ;)

There has to be a way ... SB does it fine as long as it's part of the loaded script.  ::)

I think the answer is hidden within scriba_NewSbArgs and how strings are assigned to ArgData structues.
Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 11:56:56 AM
No, I'm a Vulcan, like Spock.

It looks like a design flaw. ArgData blocks not completely inedependent
Title: Re: SBT
Post by: JRS on April 30, 2015, 11:59:15 AM
Quote
No, I'm a Vulcan, like Spock.

That explains it. No obvious logic to this.  :-*

Please look at this (http://www.oxygenbasic.org/forum/index.php?topic=1206.msg11615#msg11615) previous post by me in this thread.
Title: Re: SBT
Post by: JRS on April 30, 2015, 02:22:04 PM
It seems I was correct about assigning strings to ArgData structures.
scriba.c
Code: C
  1.       case 'S': /* string argument */
  2.       case 's':
  3.         p[i].type = SBT_STRING;
  4.         arg = va_arg(marker, char *);
  5.         if( arg == NULL )arg = "";
  6.         p[i].size = strlen(arg);
  7.         if( p[i].size ){
  8.           p[i].v.s = alloc_Alloc(p[i].size,pProgram->pMEM);
  9.           if( p[i].v.s == NULL ){
  10.             while( i ){
  11.               if( p[i].type == SBT_STRING && p[i].v.s )alloc_Free(p[i].v.s,pProgram->pMEM);
  12.               i--;
  13.               }
  14.             alloc_Free(p,pProgram->pMEM);
  15.             return NULL;
  16.             }
  17.           memcpy(p[i].v.s,arg,p[i].size);
  18.           }else{
  19.           p[i].v.s = NULL;
  20.           }
  21.         i++;
  22.         break;
  23.  

I'm wondering if scriba_NewSbString() (http://www.scriptbasic.org/docs/dg/devguide_3.3.9.html) is the proper way for the embedding API to create the SBT_STRING ArgData structure pointer.

Title: Re: SBT
Post by: JRS on April 30, 2015, 04:06:05 PM
Since these embedding routines (scriba_NewSbString, ...) return a pSbData pointer, maybe I should define my ArgData array structure as pSbData rather than SbData like I'm doing now. I'm also thinking to maintain thread safe management of all SB variables, the scriba_NewSbLong, scriba_NewSbDouble, scriba_NewSbString and scriba_DestroySbData routines should be used and not  assign the ArgData[0]v.* structure element directly. (Read-Only)

At this point I'm trying to understand why these functions exist and when they should be used. I have to believe Peter created them to ease the pain of what is under the SB API (macro/define) covers and gluing it all together.
Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 04:45:25 PM
John,

I confirm you can use scriba_NewSbString to create individual string arguments, but you will need to copy them into a single block, yet scriba_DestroySbData for each of them after callArgEx.

Better to use scriba_NewSbArgs, since it builds the arguments in one contiguous block, returning one base pointer, as required by CallArgEx
Title: Re: SBT
Post by: JRS on April 30, 2015, 05:03:48 PM
Excellent!

Can whip up an example (in O2 is fine) showing your proof of concept?

Title: Re: SBT
Post by: Charles Pegge on April 30, 2015, 05:07:52 PM
For individual String Args / hand-built argument block:

Code: OxygenBasic
  1.   'TEST 4
  2.  'RELOCATE STRING ARGS
  3.  sbData da[4]
  4.   sys a[4]
  5.   a[0] = scriba_NewSbString(pProgram,"ten")
  6.   copy @da[0],a[0],sizeof da
  7.   a[1] = scriba_NewSbString(pProgram,"twenty")
  8.   copy @da[1],a[1],sizeof da
  9.   a[2] = scriba_NewSbString(pProgram,"thirty")
  10.   copy @da[2],a[2],sizeof da
  11.   a[3] = scriba_NewSbString(pProgram,"forty")
  12.   copy @da[3],a[3],sizeof da
  13.   args=@da
  14.  
  15.   count=4
  16.   iError=scriba_CallArgEx(pProgram, sbfun, @ReturnData, count, args)
  17.   'scriba_DestroySbArgs(pProgram,args,4)
  18.  '
  19.  for i=0 to 3
  20.     scriba_DestroySbData(pProgram,a[i])
  21.   next
  22.  
  23.  
Title: Re: SBT
Post by: JRS on April 30, 2015, 05:20:31 PM
Thanks Charles!

I'll give this method a try.

Is this an either or choice?

Code: OxygenBasic
  1.   'scriba_DestroySbArgs(pProgram,args,4)
  2.  '
  3.  for i=0 to 3
  4.     scriba_DestroySbData(pProgram,a[i])
  5.   next
  6.  
Title: Re: SBT
Post by: JRS on April 30, 2015, 06:08:12 PM
Quote
Better to use scriba_NewSbArgs, since it builds the arguments in one contiguous block, returning one base pointer, as required by CallArgEx

I agree if there was a way to wrap a SB variadic function. I think we are bound to using the scriba_NewSbString (and like SB make variable type functions) method and building an ArgData array manually. (dynamically)

Update

Got it working. Still need to return a generic script result.

Thanks Charles! You have been reinstated to super hero status.  ;D
Title: Re: SBT
Post by: Charles Pegge on May 01, 2015, 12:57:18 AM
John,

I think this is how the whole thing should go, including the returned arg and its destruction.

Code: OxygenBasic
  1.   'CALL A FUNCTION WITH ARGS AND RETURN VALUE
  2.  '------------------------------------------
  3.  '
  4.  string funn="main::callbacka" 'LOWERCASE OF FUNCTION NAME
  5.  pr+="call " funn cr
  6.  
  7.   '
  8.  sbfun=scriba_LookupFunctionByName(pProgram,funn)
  9.   if sbfun=0 then print "Unable to locate " funn : goto ending
  10.   sbData *arg
  11.   sbData *argr
  12.   sys    count=4
  13.   @arg = scriba_NewSbArgs(pProgram," s s s s", "one","two","three","four")
  14.   @argr = scriba_NewSbArgs(pProgram,"u",0)
  15.   scriba_CallArgEx(pProgram, sbfun, argr, count, arg)
  16.  
  17.   'READ RETURN VALUE
  18.  '-----------------
  19.  '
  20.  #semicolon separator
  21.  
  22.   switch (argr.typ) {
  23.     case SBT_UNDEF  : pr+= "Undefined "       ; break ;
  24.     case SBT_DOUBLE : pr+="double "  argr.dbl ; break ;
  25.     case SBT_LONG   : pr+="long "    argr.lng ; break ;
  26.     case SBT_STRING : pr+="zstring " argr.str ; break ;
  27.     case SBT_ZCHAR  : pr+="zchar "   argr.lng ; break ;
  28.   }
  29.   '
  30.  'DESTROY ARGUMENTS AND RETURNED ARG
  31.  scriba_DestroySbArgs(pProgram, arg,count)
  32.   scriba_DestroySbArgs(pProgram, argr,1)
  33.  
Title: Re: SBT
Post by: JRS on May 01, 2015, 01:42:48 AM
For O2, C or other compiled hosts, looks GREAT!

It won't work in an SB extension module as I'm wrapping SB functions and not calling them directly. (no dynamic variadic function support)

I still don't understand how to return a dynamic value as a ArgData mortal variable. I haven't had any luck doing multiple besRETURN_ in an IF block for this.  :o

Title: Re: SBT
Post by: Charles Pegge on May 01, 2015, 02:40:20 AM
In C terms, because argr is an sbdata pointer, you will need to use arrow notation to access the returned members:

argr->lng instead of argr.lng
argr->str instead of argr.str

array notation:

argr[0].lng is equivalent to argr->lng

Title: Re: SBT
Post by: JRS on May 01, 2015, 08:01:40 AM
Thanks Charles!

Did you review the latest working version on All BASIC and do you see any problems with the way I'm doing this?


Working !!! (see All BASIC forum)

Title: Re: SBT
Post by: JRS on May 01, 2015, 02:17:45 PM
For individual String Args / hand-built argument block:

Code: OxygenBasic
  1.   'TEST 4
  2.  'RELOCATE STRING ARGS
  3.  sbData da[4]
  4.   sys a[4]
  5.   a[0] = scriba_NewSbString(pProgram,"ten")
  6.   copy @da[0],a[0],sizeof da
  7.   a[1] = scriba_NewSbString(pProgram,"twenty")
  8.   copy @da[1],a[1],sizeof da
  9.   a[2] = scriba_NewSbString(pProgram,"thirty")
  10.   copy @da[2],a[2],sizeof da
  11.   a[3] = scriba_NewSbString(pProgram,"forty")
  12.   copy @da[3],a[3],sizeof da
  13.   args=@da
  14.  
  15.   count=4
  16.   iError=scriba_CallArgEx(pProgram, sbfun, @ReturnData, count, args)
  17.   'scriba_DestroySbArgs(pProgram,args,4)
  18.  '
  19.  for i=0 to 3
  20.     scriba_DestroySbData(pProgram,a[i])
  21.   next
  22.  
  23.  

Since I'm not creating a[] and assigning my ArgData array directly , I have no a[] to destroy with scriba_DestroySbData. Is my understanding correct that when SB_CallSubArgs exits, ArgData gets destroyed doing the job for us?

Title: Re: SBT
Post by: Charles Pegge on May 01, 2015, 02:31:57 PM
Hi John,

Glad you were able to crack the return data.

If you create sbNewStrings, my understanding is that you have to destroy each one afterwards. That is why I kept the handles in a[..]
Title: Re: SBT
Post by: JRS on May 01, 2015, 02:45:03 PM
Quote
If you create sbNewStrings, my understanding is that you have to destroy each one afterwards. That is why I kept the handles in a[..]

My understanding of the scriba_DestroySbData function is to release the memory used to temporarily hold the variable structures used in the wrapper function. Since I'm using the destination ArgData[] as the storage area and it get destroyed when the function exits, all is good, correct?

On the other hand if we care about Peter's MyAlloc memory manager, the function should probably be called.
Title: Re: SBT
Post by: Charles Pegge on May 01, 2015, 03:15:23 PM
I did a quick test to see what happened to the arg strings after CallArgex, and yes, they appear to be already released!
Title: Re: SBT
Post by: JRS on May 01, 2015, 03:31:54 PM
That is really good news!

It could also explain my core dumps with the following attempt as releasing the ArgData structures after assignment.

Code: C
  1. besFUNCTION(SB_CallSubArgs)
  2.   DIM AS VARIABLE Argument;
  3.   DIM AS SbData ArgData[8];
  4.   DIM AS SbData TmpArg;
  5.   DIM AS SbData FunctionResult;
  6.   DIM AS unsigned long sbobj;
  7.   DIM AS int i, slen, fnsn;
  8.  
  9.   Argument = besARGUMENT(1);
  10.   besDEREFERENCE(Argument);
  11.   sbobj = LONGVALUE(Argument);
  12.  
  13.   Argument = besARGUMENT(2);
  14.   besDEREFERENCE(Argument);
  15.   fnsn = LONGVALUE(Argument);
  16.  
  17.   for( i=3 ; i <= (unsigned)besARGNR ; i++ ){
  18.     Argument = besARGUMENT(i);
  19.     besDEREFERENCE(Argument);
  20.     switch( slen=TYPE(Argument) ){
  21.       case VTYPE_LONG:
  22.         TmpArg = scriba_NewSbLong(sbobj, LONGVALUE(Argument));
  23.         ArgData[i-3] = *TmpArg;
  24.         scriba_DestroySbData(sbobj,TmpArg);
  25.         break;
  26.       case VTYPE_DOUBLE:
  27.         TmpArg = scriba_NewSbDouble(sbobj, DOUBLEVALUE(Argument));
  28.         ArgData[i-3] = *TmpArg;
  29.         scriba_DestroySbData(sbobj,TmpArg);
  30.         break;
  31.       case VTYPE_STRING:
  32.         TmpArg = scriba_NewSbString(sbobj, STRINGVALUE(Argument));
  33.         ArgData[i-3] = *TmpArg;
  34.         scriba_DestroySbData(sbobj,TmpArg);
  35.         break;
  36.     }
  37.   }
  38.  
  39.   scriba_CallArgEx(sbobj, fnsn, &FunctionResult, besARGNR-2, &ArgData);
  40.  
  41.   switch (FunctionResult.type) {
  42.     case SBT_LONG   :
  43.         besRETURN_LONG(FunctionResult.v.l);
  44.         break ;
  45.     case SBT_DOUBLE :
  46.         besRETURN_DOUBLE(FunctionResult.v.d);
  47.         break ;
  48.     case SBT_STRING :
  49.         besRETURN_STRING(FunctionResult.v.s);
  50.         break ;  
  51.   }
  52. besEND
  53.  

Thanks for the conformation/comfort in knowing.
Title: Re: SBT
Post by: JRS on May 02, 2015, 10:40:37 AM
Charles,

So far the SBT (Script BASIC Tutorial) extension/embedding module stands with nothing on the issues list.  :)

I'm combining the SB_Set(type) functions to a single SB_SetVar function and then putting the happy C BASIC face on the interface that is still showing its C side. I hope to have that done in a couple hours.

The next goal is to get SB preprocessor support working so I can use this for the next part of the tutorial which is building a GUI (IUP) editor/debugger. (like Dave's effort without the COM) In conjunction I would like to get the SB embedded objects running in a threaded model. You have already been down this road with DLLC and your wealth of SB knowledge would help make this go smoother.

The SBT project is becoming fragmented trying to run parallel threads here on O2 and All BASIC. You are a long time member on All BASIC and it would be great if you can join AIR, Mike, Markus, Tony and I along with any of the other BASIC developers that might join in.

I have had e-mail contact with Peter Verhas but I think he is only able to lurk at the moment due to other commitments.

Title: Re: SBT
Post by: JRS on May 04, 2015, 09:52:02 AM
Hi Charles,

I have been looking at Dave's debugger.c/h (sb_engine.dll) and there is a lot of good stuff in there. I might be missing something but I can't see why I can't scriba_NoRun the script and use some of Dave's debug code to single step through the script. That is my first goal at the moment. I don't think an internal preprocessor is needed as we aren't trying to inject a debugger as a filter of sorts in normal code execution flow.

I think I want to do a TRACE(ON|OFF) internal preprocessor for the that part of the tutorial. Debugging (interactive mode) should be a core feature of the SBT API.

Can you take a peek at Dave's sb_engine.dll code and see how I might adapt a single step feature to SBT?
Title: Re: SBT
Post by: Charles Pegge on May 05, 2015, 10:12:35 AM
Hi John,

I cannot accommodate any further coding activities, than I already have. Sorry.
Title: Re: SBT
Post by: JRS on May 05, 2015, 11:03:15 AM
I see this debugging API (internal preprocessor) as a way to make SB an interactive BASIC. If someone were interested in that direction, this would be the way to do it. With Dave's IDE/Debugger + VB classic / COM and the sdbg console debugger I have going on Linux, I think this is far as I want to go with this on a tutorial level. Preprocessors are advanced SB usage.

Thanks Charles for all the help and code you have contributed to the Script BASIC project!

Title: Re: SBT
Post by: JRS on May 06, 2015, 10:24:04 PM
I finally got the Script BASIC remote console debugger working and with arrays.  8)

All BASIC Example (http://www.allbasic.info/forum/index.php?topic=387.msg4327#msg4327)
Title: Re: SBT
Post by: JRS on May 07, 2015, 11:25:12 AM
Got it working on Windows 32 (XP) as well.

(see All BASIC forum)
Title: Re: SBT
Post by: JRS on May 14, 2015, 12:09:45 AM
Charles,

I got SBT working multi-threaded.

(see All BASIC forum)
Title: Re: SBT
Post by: Charles Pegge on May 14, 2015, 01:56:53 AM
Well done! Is it collision-proof ?
Title: Re: SBT
Post by: JRS on May 14, 2015, 02:11:05 AM
(http://a.abcnews.com/images/Technology/ht_proton_collision_wy_111213_wmain.jpg)

Quote
Is it collision-proof ?

Don't know. Just fired up the reactor.  ;D

I did some Gtk test with sbhttpd/cURL some time ago and it seemed to handle multiple event loop handlers. I should know more tomorrow and if MT works.

Found the old post. January 04, 2011

Quote
Here is an update on my threaded Gtk adventures.

I was having a problem with GTK-Server / SB missing events within the gtk_server_callback WAIT routine in my scripts. I ended up adding the gdk_threads_enter/leave routine to GTK-Server's C code.

gtk-server.c
Code: C
  1.     /* Have we found the GTK-signal call? */
  2.     else if (!strcmp("gtk_server_callback", gtk_api_call)) {
  3.         /* Yes, find the first argument */
  4.         if ((arg = parse_data(list, ++item)) != NULL){
  5.             while (1){
  6.                 /* Let GTK wait for an event <<-- JRS --->> Thread enter/leave added */
  7.                 if (!strcmp (arg, "WAIT") || !strcmp (arg, "wait") || !strcmp(arg, "1")) {
  8.                     #if GTK_SERVER_GTK1x || GTK_SERVER_GTK2x
  9.                     gdk_threads_enter ();
  10.                     gtk_main_iteration();
  11.                     gdk_threads_leave ();
  12.                     #elif GTK_SERVER_XF
  13.                     Current_Object.object = fl_do_forms();
  14.                     Current_Object.state = (long)Current_Object.object;
  15.                     #endif
  16.  

This is my current working code.

mtgtk.sb
Code: Script BASIC
  1. PRINT "Status: 200\nContent-Type: text/html\n\n"
  2.  
  3. DECLARE SUB MTSET ALIAS "setmtvariable" LIB "mt"
  4. DECLARE SUB MTGET ALIAS "getmtvariable" LIB "mt"
  5.  
  6. IF MTGET("tid") = undef THEN
  7.   MTSET("tid",1)
  8.   tid = MTGET("tid")
  9. ELSE
  10.   tid = MTGET("tid") + 1
  11.   MTSET("tid",tid)
  12. END IF
  13.  
  14. DECLARE SUB DLL ALIAS "_idll" LIB "gtk-server"
  15. DECLARE SUB DEFINE ALIAS "_idll_define" LIB "gtk-server"
  16.  
  17. DEFINE "gtk_init NONE NONE 2 NULL NULL"
  18. DEFINE "gtk_window_new delete-event WIDGET 1 INT"
  19. DEFINE "gtk_window_set_title NONE NONE 2 WIDGET STRING"
  20. DEFINE "gtk_table_new NONE WIDGET 3 INT INT BOOL"
  21. DEFINE "gtk_container_add NONE NONE 2 WIDGET WIDGET"
  22. DEFINE "gtk_button_new_with_label clicked WIDGET 1 STRING"
  23. DEFINE "gtk_table_attach_defaults NONE NONE 6 WIDGET WIDGET INT INT INT INT"
  24. DEFINE "gtk_widget_show_all NONE NONE 1 WIDGET"
  25. DEFINE "gtk_server_callback NONE STRING 1 STRING"
  26. DEFINE "gtk_widget_destroy NONE NONE 1 WIDGET"
  27. DEFINE "g_thread_init NONE NONE 1 NULL"
  28. DEFINE "g_thread_exit NONE NONE 1 NULL"
  29. DEFINE "gdk_threads_init NONE NONE 0"
  30. DEFINE "gdk_threads_enter NONE NONE 0"
  31. DEFINE "gdk_threads_leave NONE NONE 0"
  32.  
  33. DLL("g_thread_init")
  34. DLL("gdk_threads_init")
  35. DLL("gdk_threads_enter")
  36. DLL("gtk_init NULL NULL")
  37. win = DLL("gtk_window_new 0")
  38. DLL("gtk_window_set_title " & win & " \"SB Gtk " & tid & "\"")
  39. tbl = DLL("gtk_table_new 10 10 1")
  40. DLL("gtk_container_add " & win & " " & tbl)
  41. but = DLL("gtk_button_new_with_label \"Quit\"")
  42. DLL("gtk_table_attach_defaults " & tbl & " " & but & " 5 9 5 9")
  43. DLL("gtk_widget_show_all " & win)
  44. DLL("gdk_threads_leave")
  45.  
  46. REPEAT
  47.   event = DLL("gtk_server_callback WAIT")
  48. UNTIL event = win OR event = but
  49.  
  50. DLL("gdk_threads_enter")
  51. DLL("gtk_widget_destroy " & win)
  52. DLL("gtk_server_callback 2")
  53. DLL("gdk_threads_leave")
  54. DLL("g_thread_exit NULL")
  55.  
  56. END
  57.  

(http://files.allbasic.info/AllBasic/mtdemo.png)

This is a screen shot of starting 4 Gtk threaded windows from a scriba script.

mtdemo.sb
Code: Script BASIC
  1. INCLUDE curl.bas
  2.  
  3. ch = curl::init()
  4.  
  5. curl::option(ch,"NOBODY")
  6. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtgtk.sb")
  7. curl::perform(ch)
  8.  
  9. curl::finish(ch)
  10.  

My initial testing is looking positive and I will try something more complex other than just a window with a quit button.

FYI If I run a GTK-Server SB script in a non-threaded environment, the gdk_threads_enter/leave I coded into gtl_server_callback WAIT doesn't seem to have any side effects.

I'm amazed that this even works at all. Gtk running in a threaded environment using a scripted API interface is pushing the envelope with an interpreter. When I tried creating the second thread for the first time and got a Linux GUI sub-system error, I thought I was toast. I hope this effort encourages the BCX team to have a look at threading Gtk.

I would love to see how this runs on a multi-core processor. I'm not seeing any abnormal CPU or memory use when running in a threaded Gtk environment.
Title: Re: SBT
Post by: JRS on May 14, 2015, 04:44:35 PM
Here is an example of using the MT (http://www.scriptbasic.org/docs/mt/mod_mt_toc.html) extension module to communicate between threads and the host script.

ttmain.sb
Code: Script BASIC
  1. IMPORT mt.bas
  2. IMPORT sbt.inc
  3.  
  4. SB_ThreadStart("tt1.sb", "JRS","/etc/scriba/basic.conf")
  5. PRINT "SB Host\n"
  6. LINE INPUT wait
  7. PRINT mt::GetVariable("thread_status"),"\n"
  8.  

tt1.sb
Code: Script BASIC
  1. ' Test Thread
  2.  
  3. IMPORT mt.bas
  4. IMPORT sbt.inc
  5.  
  6. cmd = COMMAND()
  7. PRINT cmd,"\n"
  8.  
  9. FOR x = 1 TO 10
  10.   PRINT "Thread 1: ",x,"\n"
  11. NEXT
  12.  
  13. mt::SetVariable "thread_status","Completed"
  14.  
  15. SB_ThreadEnd
  16.  

Output

jrs@laptop:~/sb/sb22/sbt$ scriba ttmain.sb
SB Host
JRS
Thread 1: 1
Thread 1: 2
Thread 1: 3
Thread 1: 4
Thread 1: 5
Thread 1: 6
Thread 1: 7
Thread 1: 8
Thread 1: 9
Thread 1: 10

Completed
jrs@laptop:~/sb/sb22/sbt$
Title: Re: SBT
Post by: JRS on May 15, 2015, 07:21:17 PM
Charles,

I gave IUP a try with SBT but it only allowed one thread at a time. Here is the error message I got. Can you give me an idea in general terms how you solved the collision issue in DLLC? This will give me the info and direction I need to fix this on Linux.


jrs@laptop:~/sb/sb22/sbt$ scriba iupmain.sb
SB Host

(scriba:1886): Gdk-WARNING **: scriba: Fatal IO error 11 (Resource temporarily unavailable) on X server :0.0.

jrs@laptop:~/sb/sb22/sbt$ scriba iupmain.sb
SB Host

(scriba:2627): Gdk-ERROR **: The program 'scriba' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadDrawable (invalid Pixmap or Window parameter)'.
  (Details: serial 722 error_code 9 request_code 139 (RENDER) minor_code 4)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the GDK_SYNCHRONIZE environment
   variable to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)
Trace/breakpoint trap (core dumped)
jrs@laptop:~/sb/sb22/sbt$

Title: Re: SBT
Post by: Charles Pegge on May 15, 2015, 08:46:17 PM
Hi John,

As I recall, only one procedure can be active in one sb program per thread at any one time. If you want concurrency then you must use additional sb programs, using SBT to manage them, and coordinate data.

Title: Re: SBT
Post by: JRS on May 15, 2015, 09:25:32 PM
Even if I don't define any callbacks in my thread IUP test scripts, it still complains. It seems you can't have more than one IUP event loop running in a process. (which the threads are part of)

I don't see IupMainLoop() anywhere in DLLC code. Does this mean DLLC has it's own (ASM) IUP event loop?
Title: Re: SBT
Post by: Charles Pegge on May 15, 2015, 11:58:18 PM
IupMainLoop() is called from the SB script, as a regular DLLC call. Thereafter, the program (running in its own thread), is dedicated to processing IUP callbacks and nothing else.

But the boot program continues to run, and is able to monitor the data captured from incoming IUP callback params.

The data is stored in a shared table (accessible by both programs).

Title: Re: SBT
Post by: JRS on May 16, 2015, 08:58:22 AM
I can't seem to get more than one IupMainLoop() going. (Gtk / X-Windows errors) I didn't define any callbacks in the thread scripts. What is IUP specifically not willing to share?

It would be great to see an O2 only threaded IUP example which would not only help me but I'm sure Arnold would find it interesting and maybe expand on its use.

Title: Re: SBT
Post by: Charles Pegge on May 16, 2015, 01:17:47 PM
In general, the OS expects to see only one GUI per process, ie: one message loop and one main window.
Title: Re: SBT
Post by: JRS on May 16, 2015, 01:24:34 PM
Seems to be the case. I took a look at the DLLC code to see how you solved the mystery and the ASM magic ended the show for me.

Quote from: IUP Mailing List (http://sourceforge.net/p/iup/mailman/iup-users/thread/492D1718.5050700@ipe.airpost.net/)
Note that in GTK this is really easy to achieve:  GLib is thread-safe,
and so you can simply call g_idle_add from your worker thread to
register an idle callback.  The idle callback will then be called from
the GTK main loop in the IUP thread (since IUP simply uses the GTK main
loop, all this should work seamlessly without any modification to IUP).



.
Title: Re: SBT
Post by: JRS on May 17, 2015, 05:55:50 PM
Hi Charles,

I was able to get the thread execution object handle and pass it back to the host. I can now access thread variables and call functions just like the embedded interface I started off with.

Check out All BASIC for more.

John
Title: Re: SBT
Post by: JRS on May 18, 2015, 08:32:42 AM
Charles,

Now that I'm able to call SB thread script functions from the host (boot) SB script, can I follow your path and wrap the IUP GUI generation and event code into a SB main::main function? How would you suggest I precede from here to test if your DLLC method and see if I can get it working under Linux?

Update

I got both IUP test window to show. I needed to give the thread scripts time to load before calling their main functions. The problem is the event buffers fill up quickly and puts the windows in distress. All I need to figure out now is to how to drain and process the events.

Here is what gets returned from the thread main function.

Code: Script BASIC
  1. Iup::Show(Form)
  2. main=Iup::MainLoop()
  3. END FUNCTION
  4.  

Code: Script BASIC
  1. IMPORT sbt.inc
  2.  
  3. sb1 = SB_ThreadStart("test.sbq",undef,"/etc/scriba/basic.conf")
  4. sb2 = SB_ThreadStart("test2.sbq",undef,"/etc/scriba/basic.conf")
  5. sb1_rtn = SB_CallSubArgs(sb1, "main::main", sb1, 0)
  6. sb2_rtn = SB_CallSubArgs(sb2, "main::main", sb2, 0)
  7.  
  8. PRINT "Thread 1 = ", sb1_rtn,"\n"
  9. PRINT "Thread 2 = ", sb2_rtn,"\n"
  10.  
  11. LINE INPUT wait
  12.  


jrs@laptop:~/sb/sb22/sbt$ scriba iupmain.sb
Thread 1 = undef
Thread 2 = undef
Killed
jrs@laptop:~/sb/sb22/sbt$


I think I'm close to getting this working. I'm just confused how to process events. Can you offer any advice?
Title: Re: SBT
Post by: JRS on May 19, 2015, 04:49:48 PM
Hi Charles,

Code: OxygenBasic
  1.   sbcb[v]={pProg,fa,np, @t.scriba_CallArgEx,RetVal,idat} 'PROGRAM,FUNCTION
  2.  r=CallBackTable[v] 'callback handle to pass on to the DLL
  3.  *pReturnValue=ReturnLong pst,r
  4.  

If I can work my way through this O2 functionality, I think I can finish up from there. I'm trying to imagine this in C.  :o
Title: Re: SBT
Post by: Charles Pegge on May 19, 2015, 05:34:41 PM
Hi John,

I would suggest processing IUP low level callback messages, like mouse-move, without attempting to pass the callback to SB. Mouse coordinates and key states can be stored in SB global variables, or a shared table,  leaving more occasional events, like button-clicks to be relayed onto SB callbacks.

As you can see, threading and callbacks is really messy stuff, and way beyond the scope of a casual SB user. So the more the extension module can do internally, the better.

DLLC was too generic to be useable for this task.

Title: Re: SBT
Post by: JRS on May 19, 2015, 05:56:21 PM
Quote from: Charles
I would suggest processing IUP low level callback messages, like mouse-move, without attempting to pass the callback to SB.

I do that now with the SB IUP extension module. It's a bit of a hack as I'm returning to SB with every filtered callback and SB does the ICALL. This goes back to my inability to get MyICall (trial ext. mod.) to work. I'm by that now.

I still need to get square in my head how thread functions really don't return but remain as a life of their own in the same parent process. It's like playing who's on first. It still boggles my mind that a complete working scriba is created as a threaded function. I've decided to use MT as the thread cop. Trying to use sleep while things get loaded is too variable. (program size, ext. mod, ...) MT will smooth that out.

I have to move the event routines out of the IUP extension module and into the SBT ext. mod. My hope is this direction of fooling the threads as to who is in control is a slick trick or the method I posted from the IUP mailing list is the proper method to chose. I want something cross platform and you have already paved the way.  8)

One of the next SB tasks on my list is to be able from one called SB extension module function call another without returning to SB.
Title: Re: SBT
Post by: JRS on May 19, 2015, 11:08:00 PM
Charles,

Before I start on a considerable effort to reproduce what you have done in DLLC, I'm going to try the call g_idle_add from the worker thread to
register an idle callback method first before trying to implant into SBT the IUP event responsibilities.

This single thread example works using my existing IUP custom Iup::MainLoop (single step callback return to SB) It looks like the only issue is treating the Gtk MainLoop() with a does of g_idle_add to play nicely with threads.

Notice MT's first day on the job as a thread cop.  Works great and responsive.

Code: Script BASIC
  1. IMPORT mt.bas
  2. IMPORT sbt.inc
  3.  
  4. sb1 = SB_ThreadStart("test.sbq",undef,"/etc/scriba/basic.conf")
  5.  
  6. WHILE mt::GetVariable("STATUS") <> "OK"
  7. WEND
  8.  
  9. sb1_rtn = SB_CallSubArgs(sb1, "main::main", sb1, 0)
  10.  
  11. SB_Destroy(sb1)
  12.  

test.sbq
Code: Script BASIC
  1. ' Script BASIC Rapid-Q form conversion
  2.  
  3. IMPORT mt.bas
  4. IMPORT sbt.inc
  5. IMPORT iup.bas
  6.  
  7. ' CALLBACKS FUNCTIONS
  8.  
  9. SUB Button_Quit
  10.   Iup::ExitLoop = TRUE
  11. END SUB  
  12.  
  13. SUB Win_exit
  14.   Iup::ExitLoop = TRUE
  15. END SUB
  16.  
  17. FUNCTION main(pProg,idat)
  18.  
  19.   ' SBIUP-Q INIT
  20.  
  21.   Iup::Open()
  22.   Iup::SetGlobal("DEFAULTFONT", "Sans, 7.5")
  23.  
  24.   ' CREATE FORM
  25.  
  26.   Form = Iup::Create("dialog")
  27.          Iup::SetAttributes(Form, "RASTERSIZE=320x240, TITLE=\"Form1\"")
  28.  
  29.        Label1  = Iup::Create("label")
  30.                  Iup::SetAttributes(Label1, "TITLE=\"Customer\", RASTERSIZE=55x13, FLOATING=YES, POSITION=\"19,19\"")
  31.  
  32.        Edit1   = Iup::Create("text")
  33.                  Iup::SetAttributes(Edit1, "RASTERSIZE=121x21, FLOATING=YES, POSITION=\"72,16\"")
  34.  
  35.        Button1 = Iup::Create("button")
  36.                  Iup::SetAttributes(Button1, "TITLE=\"&Quit\", RASTERSIZE=75x25, FLOATING=YES, POSITION=\"107,164\"")
  37.  
  38.   vbx = Iup::Vbox(Label1, Edit1, Button1)
  39.   Iup::Append(Form, vbx)
  40.                  
  41.   ' SET CALLBACKS
  42.  
  43.   Iup::SetCallback(Form, "CLOSE_CB", ADDRESS(Win_exit()))
  44.   Iup::SetCallback(Button1, "ACTION", ADDRESS(Button_Quit()))
  45.  
  46.   ' MAIN
  47.  
  48.   ' Iup::Show(Iup::LayoutDialog(Form))
  49.  Iup::Show(Form)
  50.   Iup::MainLoop
  51.   Iup::Close
  52.   main = "STARTED"
  53. END FUNCTION
  54. mt::SetVariable("STATUS","OK")
  55.  

.
Title: Re: SBT
Post by: JRS on May 20, 2015, 09:30:34 AM
Charles,

I think I'm on my way to to actually getting this working. I added a new SB_msSleep() function to give me better control of my waiting for thread status MT messages.

Code: Script BASIC
  1. WHILE mt::GetVariable("STATUS") <> "OK"
  2.   SB_msSleep(20)
  3. WEND
  4.  

This does a 20 microseconds Linux usleep call.

The plan going forward is to create a common IUP callback that handles all of the various callback function argument lists as a variadic call. I will do a g_idle_add call as part of the callback definition function. I think with MT making sure everything plays by the defined rules, threading shouldn't be that scary.

Title: Re: SBT
Post by: JRS on May 20, 2015, 06:30:16 PM
Charles,

I have been spending a lot of time lately learning from your DLLC code. You spent an amazing amount of time with Script BASIC and making it O2 accessible. If anyone needed an embeddable scripting engine for O2, you have it covered with SB.  :)

Thanks for all the time, help and wealth of knowledge you have shared with me over the years.

John
Title: Re: SBT
Post by: JRS on May 20, 2015, 09:17:14 PM
Charles,

I noticed the HexToBin.sb program in the O2 zip and it dawned on me, was this the beginnings of an assembler?  :-X

Code: Script BASIC
  1.  
  2.  ' TEST HEXADECIMAL TO BINARY CONVERSION
  3. ' =====================================
  4.  
  5.  
  6.   s="B8 04 03 02  01    c3"
  7.  
  8.   'CONVERT TO BINARY
  9.  '-----------------
  10.  
  11.   le=len(s)
  12.   i=1
  13.   j=1
  14.   a=0
  15.   n=0
  16.   while i<=le
  17.     w=asc(mid(s,i,1))
  18.     if w>96 then
  19.       w-=39
  20.     elseif w>64 then
  21.       w-=7
  22.     end if
  23.     w-=48
  24.     if w>=0 then
  25.       a=a*16+w
  26.       n=1
  27.     else
  28.       if n=1 then
  29.         v[j]=chr(a)
  30.         a=0
  31.         j+=1
  32.         n=0
  33.       end if
  34.     end if
  35.     i+=1
  36.   wend
  37.   if n=1 then
  38.     v[j]=chr(a)
  39.   end if
  40.  
  41.  
  42.   'SAVE BINARY
  43.  '-----------
  44.  
  45.   op=join("",v)
  46.  
  47.   open "t.bin" for output as 1
  48.   print #1,op
  49.   close 1
  50.  
  51.   'LOAD BINARY
  52.  '-----------
  53.  
  54.   n=filelen("t.bin")
  55.   open "t.bin" for binary as 1
  56.   op=input(n,1)
  57.   close 1
  58.   '
  59.  'DISPLAY
  60.  '-------
  61.  
  62.   print "lENGTH=" & len(op) & "  BYTE CONTENT="
  63.  
  64.   for i=1 to len(op)
  65.     s="0" & hex(asc(mid(op,i,1)))
  66.     print right(s,2) & " "
  67.   next
  68.  
  69.  
  70.   line input z
  71.  
Title: Re: SBT
Post by: JRS on May 20, 2015, 10:58:46 PM
Charles,

I think this is going to be easier than I thought. I'm able to run my IupLoopStepWait based callback from the host SB script and capture events in the thread. Once I get the TID (Thread ID) attached to the callback definition I'll give more than one thread a try. I'm thinking this is going to work for the first pass. It will make a good tutorial on threaded GUI.  ;D
Title: Re: SBT
Post by: JRS on May 21, 2015, 08:01:06 PM
For the most part I have this working. Before I move on to dealing with multiple threads I still need a sane callback map created by the threads but used by the host doing the calling. Most of this will be transparent in the second round when callbacks and calling SB scripts are handled internally in the extension module interface. Since my existing pre-thread IUP interface is close to working, it's worth extending the original hack to deal with threading. 

So far it's been more fun than frustrating. Unlike the local variable type display issues I was having with the debugger.
Title: Re: SBT
Post by: Charles Pegge on May 21, 2015, 11:31:48 PM
Quote
I noticed the HexToBin.sb program in the O2 zip and it dawned on me, was this the beginnings of an assembler?  :-X

It is a seed program for something, but to become an X86 assembler, it needs another 150K!

Such is the dilemma of building demo programs - turning them into useful products is many times more complex.
Title: Re: SBT
Post by: JRS on May 22, 2015, 07:54:24 PM
I have made some progress with IUP running in a threaded mode. I still need to add the g_idle_add() to add thread support at the Gtk level.

iup.bas (callback functions)
Code: Script BASIC
  1. FUNCTION MainLoop
  2. LOCAL this_event, aname
  3.   LoopStep()
  4.   this_event = GetEvent()
  5.   IF this_event <> undef THEN
  6.     aname = GetActionName()
  7.     IF this_event = event{this_event}[0] THEN
  8.       ICALL(event{this_event}[1])
  9.     END IF
  10.   END IF
  11.   MainLoop = aname
  12. END FUNCTION
  13.  
  14. FUNCTION SetCallback(ih, aname, fname, pprog )
  15.   event{ih}[0] = ih
  16.   event{ih}[1] = fname
  17.   event{ih}[2] = pprog
  18.   SetCallback = __SetCallback(ih, aname)
  19. END FUNCTION
  20.  

iupmain.sb
Code: Script BASIC
  1. IMPORT mt.bas
  2. IMPORT sbt.inc
  3. IMPORT iup.bas
  4.  
  5. SUB SB_Wait(mtvar)
  6.   WHILE mt::GetVariable(mtvar) <> "OK"
  7.     SB_msSleep(20)
  8.   WEND
  9. END SUB
  10.  
  11. sb1 = SB_ThreadStart("rqdemo1.sb",undef,"/etc/scriba/basic.conf")
  12. SB_Wait("sb1_loaded")
  13.  
  14. sb1_rtn = SB_CallSubArgs(sb1, "main::main", sb1)
  15. SB_Wait("sb1_main")
  16.  
  17. sb2 = SB_ThreadStart("rqdemo2.sb",undef,"/etc/scriba/basic.conf")
  18. SB_Wait("sb2_loaded")
  19.  
  20. sb2_rtn = SB_CallSubArgs(sb2, "main::main", sb2)
  21. SB_Wait("sb2_main")
  22.  
  23. threads = 2
  24. sb1_active = TRUE
  25. sb2_active = TRUE
  26.  
  27. WHILE threads
  28.   IF sb1_active THEN
  29.     sb1_event_class = SB_CallSubArgs(sb1, "iup::mainloop")
  30.     IF sb1_event_class = "CLOSE_CB" THEN
  31.       sb1_active = FALSE
  32.       threads -= 1
  33.     END IF
  34.   END IF
  35.   IF sb2_active THEN
  36.     sb2_event_class = SB_CallSubArgs(sb2, "iup::mainloop")
  37.     IF sb2_event_class = "CLOSE_CB" THEN
  38.       sb2_active = FALSE
  39.       threads -= 1
  40.     END IF
  41.   END IF
  42. WEND
  43.  
  44. Iup::Close()
  45.  

rqdemo1.sb (rqdemo2.sb is identical other than references as being the second thread)
Code: Script BASIC
  1. ' Script BASIC Rapid-Q form conversion
  2.  
  3. IMPORT mt.bas
  4. IMPORT sbt.inc
  5. IMPORT iup.bas
  6.  
  7. ' CALLBACKS FUNCTIONS
  8.  
  9. SUB button_quit
  10.   PRINT "Thread 1 Quit Button Pressed\n"
  11. END SUB  
  12.  
  13. SUB win_exit
  14.   ' Good-Bye  
  15. END SUB
  16.  
  17. FUNCTION main(pProg)
  18.  
  19.   ' SBIUP-Q INIT
  20.  
  21.   Iup::Open()
  22.   Iup::SetGlobal("DEFAULTFONT", "Sans, 7.5")
  23.  
  24.   ' CREATE FORM
  25.  
  26.   Form = Iup::Create("dialog")
  27.          Iup::SetAttributes(Form, "RASTERSIZE=320x240, TITLE=\"Thread 1\"")
  28.  
  29.        Label1  = Iup::Create("label")
  30.                  Iup::SetAttributes(Label1, "TITLE=\"Customer\", RASTERSIZE=55x13, FLOATING=YES, POSITION=\"19,19\"")
  31.  
  32.        Edit1   = Iup::Create("text")
  33.                  Iup::SetAttributes(Edit1, "RASTERSIZE=121x21, FLOATING=YES, POSITION=\"72,16\"")
  34.  
  35.        Button1 = Iup::Create("button")
  36.                  Iup::SetAttributes(Button1, "TITLE=\"&Quit\", RASTERSIZE=75x25, FLOATING=YES, POSITION=\"107,164\"")
  37.  
  38.   vbx = Iup::Vbox(Label1, Edit1, Button1)
  39.   Iup::Append(Form, vbx)
  40.                  
  41.   ' SET CALLBACKS
  42.  
  43.   Iup::SetCallback(Form, "CLOSE_CB", ADDRESS(win_exit()))
  44.   Iup::SetCallback(Button1, "ACTION", ADDRESS(button_quit()))
  45.   Iup::Show(Form)
  46.   mt::SetVariable("sb1_main","OK")
  47. END FUNCTION
  48. mt::SetVariable("sb1_loaded","OK")
  49.  


.
Title: Re: SBT
Post by: JRS on May 23, 2015, 12:52:58 AM
I can't seem to exit the main::main function when the IupMainLoop ends. I may have hit the wall with this method I'm using.
Title: Re: SBT
Post by: Charles Pegge on May 23, 2015, 01:17:05 AM
Hi John,

Do you have something like this in your extension code:

Code: [Select]
WaitForMultipleObjects ThreadsRunning, hThread, 1, INFINITE

for i=1 to ThreadsRunning
  CloseHandle hThread[ i ]
next
Title: Re: SBT
Post by: JRS on May 23, 2015, 02:02:45 AM
I have a single thread working. I had to remove the main = Iup::MainLoop return and called it from the parent (host/boot) script. I'll let you know how enabling the second thread goes.

Title: Re: SBT
Post by: JRS on May 23, 2015, 02:49:19 AM
I can get both thread windows to show and close but I'm not able to run two event loops at the same time. Next task to solve.

My approach is going to be calling the IUP event routines from the host script and use IupLoopStep and not have IUP Wait and create my own JRSMainLoop polling both thread event ques.

Good Night!
Title: Re: SBT
Post by: JRS on May 23, 2015, 11:02:54 AM
Charles,

Adding thread support to SBT has been a rewarding stroll down multi-threading ally. The best way to describe what is going on is pretend you you're putting on a puppet show.  ;D
Title: Re: SBT
Post by: JRS on May 23, 2015, 02:47:28 PM
Quote from: IupLoopStep (http://webserver2.tecgraf.puc-rio.br/iup/en/func/iuploopstep.html)
This function is useful for allowing a second message loop to be managed by the application itself. This means that messages can be intercepted and callbacks can be processed inside an application loop.

IupLoopStep returns immediately after processing any messages or if there are no messages to process. IupLoopStepWait put the system in idle until a message is processed (since 3.0).

If IUP_CLOSE is returned the IupMainLoop will not end because the return code was already processed. If you want to end IupMainLoop when IUP_CLOSE is returned by IupLoopStep then call IupExitLoop after IupLoopStep returns.

An example of how to use this function is a counter that can be stopped by the user. For such, the user has to interact with the system, which is possible by calling the function periodically.

This way, this function replaces old mechanisms implemented using the Idle callback.

Note that this function does not replace IupMainLoop.

It looks like this is going to be a minor change to my IupMainLoop for threads. I'm trying to make this so it works generically for both threaded and single script application models.

Quote
IupMainLoopLevel

Returns the current cascade level of IupMainLoop. When no calls were done, return value is 0. You can use this function to check if IupMainLoop was already called and avoid calling it again. A call to IupPopup will increase one level.

Reminds me of checking the keyboard buffer for key presses but not grabbing the key code.
Title: Re: SBT
Post by: JRS on May 23, 2015, 06:28:48 PM
I have made some progress with IUP running in a threaded mode. I still need to add the g_idle_add() to add thread support at the Gtk level. If not obvious in the code example, the SB_CallSubArgs() function is calling both SB script FUNCTIONs/SUBs and extension module shared object (.so) functions in threads transparently for the user.  8)

iup.bas (callback functions)
Code: Script BASIC
  1. FUNCTION MainLoop
  2. LOCAL this_event, aname
  3.   LoopStep()
  4.   this_event = GetEvent()
  5.   IF this_event <> undef THEN
  6.     aname = GetActionName()
  7.     IF this_event = event{this_event}[0] THEN
  8.       ICALL(event{this_event}[1])
  9.     END IF
  10.   END IF
  11.   MainLoop = aname
  12. END FUNCTION
  13.  
  14. FUNCTION SetCallback(ih, aname, fname)
  15.   event{ih}[0] = ih
  16.   event{ih}[1] = fname
  17.   SetCallback = __SetCallback(ih, aname)
  18. END FUNCTION
  19.  

iupmain.sb
Code: Script BASIC
  1. IMPORT mt.bas
  2. IMPORT sbt.inc
  3.  
  4. SUB SB_Wait(mtvar)
  5.   WHILE mt::GetVariable(mtvar) <> "OK"
  6.     SB_msSleep(20)
  7.   WEND
  8. END SUB
  9.  
  10. sb1 = SB_ThreadStart("rqdemo1.sb",undef,"/etc/scriba/basic.conf")
  11. SB_Wait("sb1_loaded")
  12.  
  13. sb1_rtn = SB_CallSub(sb1, "main::main")
  14. SB_Wait("sb1_main")
  15.  
  16. sb2 = SB_ThreadStart("rqdemo2.sb",undef,"/etc/scriba/basic.conf")
  17. SB_Wait("sb2_loaded")
  18.  
  19. sb2_rtn = SB_CallSub(sb2, "main::main")
  20. SB_Wait("sb2_main")
  21.  
  22. threads = 2
  23. sb1_active = TRUE
  24. sb2_active = TRUE
  25.  
  26. WHILE threads
  27.   IF sb1_active THEN
  28.     sb1_event_class = SB_CallSubArgs(sb1, "iup::mainloop")
  29.     IF sb1_event_class = "CLOSE_CB" THEN
  30.       sb1_active = FALSE
  31.       threads -= 1
  32.     END IF
  33.   END IF
  34.   IF sb2_active THEN
  35.     sb2_event_class = SB_CallSubArgs(sb2, "iup::mainloop")
  36.     IF sb2_event_class = "CLOSE_CB" THEN
  37.       sb2_active = FALSE
  38.       threads -= 1
  39.     END IF
  40.   END IF
  41. WEND
  42.  
  43. SB_CallSub(sb2, "iup::exitloop")
  44. SB_CallSub(sb1, "iup::exitloop")
  45.  

rqdemo1.sb (rqdemo2.sb is identical other than references as being the second thread)
Code: Script BASIC
  1. ' Script BASIC Rapid-Q form conversion
  2.  
  3. IMPORT mt.bas
  4. IMPORT iup.bas
  5.  
  6. ' CALLBACKS FUNCTIONS
  7.  
  8. SUB button_quit
  9.   PRINT "Thread 1 Quit Button Pressed\n"
  10. END SUB  
  11.  
  12. SUB win_exit
  13.   ' Good-Bye  
  14. END SUB
  15.  
  16. SUB main
  17.  
  18.   ' SBIUP-Q INIT
  19.  
  20.   Iup::Open()
  21.   Iup::SetGlobal("DEFAULTFONT", "Sans, 7.5")
  22.  
  23.   ' CREATE FORM
  24.  
  25.   Form = Iup::Create("dialog")
  26.          Iup::SetAttributes(Form, "RASTERSIZE=320x240, TITLE=\"Thread 1\"")
  27.  
  28.        Label1  = Iup::Create("label")
  29.                  Iup::SetAttributes(Label1, "TITLE=\"Customer\", RASTERSIZE=55x13, FLOATING=YES, POSITION=\"19,19\"")
  30.  
  31.        Edit1   = Iup::Create("text")
  32.                  Iup::SetAttributes(Edit1, "RASTERSIZE=121x21, FLOATING=YES, POSITION=\"72,16\"")
  33.  
  34.        Button1 = Iup::Create("button")
  35.                  Iup::SetAttributes(Button1, "TITLE=\"&Quit\", RASTERSIZE=75x25, FLOATING=YES, POSITION=\"107,164\"")
  36.  
  37.   vbx = Iup::Vbox(Label1, Edit1, Button1)
  38.   Iup::Append(Form, vbx)
  39.                  
  40.   ' SET CALLBACKS
  41.  
  42.   Iup::SetCallback(Form, "CLOSE_CB", ADDRESS(win_exit()))
  43.   Iup::SetCallback(Button1, "ACTION", ADDRESS(button_quit()))
  44.   Iup::Show(Form)
  45.   mt::SetVariable("sb1_main","OK")
  46. END SUB
  47. mt::SetVariable("sb1_loaded","OK")
  48.  


.
Title: Re: SBT
Post by: JRS on May 23, 2015, 11:46:44 PM
Charles,

I forgot that I didn't have the filter code in the Iup::LoopStep() and only the Iup::LoopStepWait() function. I'm no longer getting segment faults for no reason and clicks are much more responsive. I guess filtering out mouse movement events does help.

Code: C
  1. /*
  2. int IupLoopStep(void);  <iup.h>
  3.  
  4. Runs one iteration of the message loop.
  5. */
  6. besFUNCTION(PuiLoopStep)
  7.  
  8. retry_event:
  9.   IupLoopStep();
  10.   if (evtHandle == NULL) goto retry_event;
  11.   besRETURN_LONG(IUP_DEFAULT);
  12. besEND
  13.  
Title: Re: SBT
Post by: JRS on May 24, 2015, 01:11:39 PM
Charles,

Even though my IupLoopStep() wrapper function was filtering out everything except defined callback events, one to the core CPU was running at 100%. I added a usleep and that brought it down to about 7-12%. At this point even without the Gtk tweaks for threading, it's responsive and seems stable. The second thread window sometimes starts max size and/or without a min/max system menu button. Other than that, I think I can try more complex IUP examples (On-line Dictionary) and see how it goes.

Code: C
  1. /*
  2. int IupLoopStep(void);  <iup.h>
  3.  
  4. Runs one iteration of the message loop.
  5. */
  6. besFUNCTION(PuiLoopStep)
  7.  
  8. retry_event:
  9.   IupLoopStep();
  10.   if (evtHandle == NULL){
  11.     usleep(50);
  12.     goto retry_event;
  13.   }      
  14.   besRETURN_LONG(IUP_DEFAULT);
  15. besEND
  16.  

BTW: I ran into a case where SB AUTO VARS (arrays) is expanding the array with referencing undef elements.  :-\ I'm going to see if the forced variable initializing SB option will resolve the issue.

Here is my latest callback wrappers.

Code: Script BASIC
  1. FUNCTION MainLoop
  2. LOCAL this_event, aname
  3.   LoopStep()
  4.   this_event = GetEvent()
  5.   ICALL(event{BB_HTA(this_event)}[1])
  6.   MainLoop = GetActionName()
  7. END FUNCTION
  8.  
  9. FUNCTION SetCallback(ih, aname, fname)
  10.   event{BB_HTA(ih)}[0] = BB_HTA(ih)
  11.   event{BB_HTA(ih)}[1] = fname
  12.   SetCallback = __SetCallback(ih, aname)
  13. END FUNCTION
  14.  
Title: Re: SBT
Post by: JRS on May 24, 2015, 04:16:02 PM
Charles,

As it turns out the Iup::MainLoop wrapper function has to be run in the host script. It seems to run flawlessly in this mode and responsive to every click even if I rapidly click on the button. Everything closes as it should without the OS bitching on exit. I'll post a fresh set of code on All BASIC / Script BASIC forums when I can get a better method of passing the callback map back to the host.

I'm getting happier with this with each post. I hope it continues that way.  ;)

Done!

I finally got this worked out and didn't have to inform Gtk or IUP that they're being threaded.  8)

I can click on either thread window's button as fast as I can and it responds with the being pressed message. The only minor issue I still have is the second thread window will open in max size and sometimes without a max/restore window button. At this point I'm happy.

iup.bas - callback handling functions
Code: Script BASIC
  1. FUNCTION MainLoop
  2. LOCAL hex_event
  3.   LoopStep()
  4.   this_event = GetEvent()
  5.   hex_event = BB_HTA(this_event)
  6.   IF hex_event = event{hex_event}[0] THEN
  7.     IF event{hex_event}[2] = 1 THEN
  8.       SB_CallSub(main::sb1, event{hex_event}[1])
  9.     ELSEIF event{hex_event}[2] = 2 THEN
  10.       SB_CallSub(main::sb2, event{hex_event}[1])
  11.     END IF
  12.     MainLoop = GetActionName()
  13.   END IF  
  14. END FUNCTION
  15.  
  16. FUNCTION SetThreadCallback(ih, aname, fname, tnum)
  17.   t_event = mt::GetVariable("Callback_Map")
  18.   IF t_event = undef THEN t_event = ""
  19.   t_event = t_event & BB_HTA(ih) & "|" & fname & "|" & tnum & "\n"
  20.   mt::SetVariable("Callback_Map", t_event)
  21.   SetThreadCallback = __SetCallback(ih, aname)
  22. END FUNCTION
  23.  
  24. SUB GetThreadCallback
  25.   LOCAL t_event, e_list, e_array, x
  26.   t_event = mt::GetVariable("Callback_Map")
  27.   SPLITA t_event BY "\n" TO e_list
  28.   FOR x = 0 TO UBOUND(e_list)
  29.     SPLITA e_list[x] BY "|" TO e_array
  30.     event{e_array[0]}[0] = e_array[0]
  31.     event{e_array[0]}[1] = e_array[1]
  32.     event{e_array[0]}[2] = e_array[2]
  33.   NEXT
  34. END SUB
  35.  
  36. FUNCTION BB_HTA(AsciiStr)
  37.   LOCAL AsciiLen,ScanPos,HexStr
  38.   AsciiLen = LEN(AsciiStr)
  39.   HexStr = ""
  40.   IF AsciiLen THEN
  41.     FOR ScanPos = 1 TO AsciiLen
  42.       HexStr &= RIGHT("0" & HEX(ASC(MID(AsciiStr, ScanPos, 1))),2)
  43.     NEXT ScanPos
  44.   ELSE
  45.     HexStr = ""
  46.   END IF
  47.   BB_HTA = HexStr
  48. END FUNCTION
  49.  

iupmain.sb - host SB script (puppet master)
Code: Script BASIC
  1. IMPORT mt.bas
  2. IMPORT sbt.inc
  3. IMPORT iup.bas
  4.  
  5. Iup::Open()
  6.  
  7. SUB SB_Wait(mtvar)
  8.   WHILE mt::GetVariable(mtvar) <> "OK"
  9.     SB_msSleep(5000)
  10.   WEND
  11. END SUB
  12.  
  13. sb1 = SB_ThreadStart("rqdemo1.sb",undef,"/etc/scriba/basic.conf")
  14. SB_Wait("sb1_loaded")
  15. sb1_rtn = SB_CallSubArgs(sb1, "main::main", sb1)
  16.  
  17. sb2 = SB_ThreadStart("rqdemo2.sb",undef,"/etc/scriba/basic.conf")
  18. SB_Wait("sb2_loaded")
  19. sb2_rtn = SB_CallSubArgs(sb2, "main::main", sb2)
  20.  
  21. threads = 2
  22.  
  23. Iup::GetThreadCallback()
  24.  
  25. WHILE threads
  26.   event_class = Iup::MainLoop()
  27.   IF event_class = "CLOSE_CB" THEN
  28.     threads -= 1
  29.     IF Iup::event{Iup::BB_HTA(Iup::this_event)}[2] = 1 THEN
  30.       SB_CallSub(sb1, "iup::exitloop")
  31.     ELSEIF Iup::event{Iup::BB_HTA(Iup::this_event)}[2] = 2 THEN
  32.       SB_CallSub(sb2, "iup::exitloop")
  33.     END IF
  34.   END IF  
  35.   SB_msSleep(250)
  36. WEND
  37.  
  38. Iup::Close()
  39. SB_Destroy(sb2)
  40. SB_Destroy(sb1)
  41.  

rqdemo1.sb - rqdemo2.sb is identical other than the references to it been the second thread.
Code: Script BASIC
  1. ' Script BASIC Rapid-Q form conversion
  2.  
  3. IMPORT mt.bas
  4. IMPORT iup.bas
  5.  
  6. ' CALLBACKS FUNCTIONS
  7.  
  8. SUB button_quit
  9.   PRINT "Thread 1 Quit Button Pressed\n"
  10. END SUB  
  11.  
  12. SUB win_exit
  13.   ' Good-Bye  
  14. END SUB
  15.  
  16. SUB main
  17.  
  18.   ' SBIUP-Q INIT
  19.  
  20.   Iup::Open()
  21.   Iup::SetGlobal("DEFAULTFONT", "Sans, 7.5")
  22.  
  23.   ' CREATE FORM
  24.  
  25.   Form = Iup::Create("dialog")
  26.          Iup::SetAttributes(Form, "RASTERSIZE=320x240, TITLE=\"Thread 1\"")
  27.  
  28.        Label1  = Iup::Create("label")
  29.                  Iup::SetAttributes(Label1, "TITLE=\"Customer\", RASTERSIZE=55x13, FLOATING=YES, POSITION=\"19,19\"")
  30.  
  31.        Edit1   = Iup::Create("text")
  32.                  Iup::SetAttributes(Edit1, "RASTERSIZE=121x21, FLOATING=YES, POSITION=\"72,16\"")
  33.  
  34.        Button1 = Iup::Create("button")
  35.                  Iup::SetAttributes(Button1, "TITLE=\"&Quit\", RASTERSIZE=75x25, FLOATING=YES, POSITION=\"107,164\"")
  36.  
  37.   vbx = Iup::Vbox(Label1, Edit1, Button1)
  38.   Iup::Append(Form, vbx)
  39.                  
  40.   ' SET CALLBACKS
  41.  
  42.   Iup::SetThreadCallback(Form, "CLOSE_CB", "main::win_exit", 1)
  43.   Iup::SetThreadCallback(Button1, "ACTION", "main::button_quit", 1)
  44.   Iup::Show(Form)
  45. END SUB
  46. mt::SetVariable("sb1_loaded","OK")
  47.  


.
Title: SBT IUP Threaded Linux 64 bit Working
Post by: JRS on May 25, 2015, 06:16:20 PM
The solution to my unstable IUP start-up issues were resolved with creating an IUP dialog in the parent script before creating threaded children dialogs.  I moved the Iup::MainLoop and Iup::GetThreadCallback routines into the main script from the IUP extension module. At this point everything is working as expected and I couldn't be happier.

Moral: How can you have well behaved children if you don't have a mature parent in charge?  ::)

SBx_Main
Code: Script BASIC
  1. ' SBT IUP Theaded Example
  2.  
  3. IMPORT mt.bas
  4. IMPORT sbt.inc
  5. IMPORT iup.bas
  6. IMPORT "SBx"
  7.  
  8. Iup::Open()
  9.  
  10. SUB SB_Wait(mtvar)
  11.   WHILE mt::GetVariable(mtvar) <> "OK"
  12.     SB_msSleep(5000)
  13.   WEND
  14. END SUB
  15.  
  16. SUB btn1_clicked
  17.   PRINT "Main 0 Button 1 Pressed\n"
  18.   PRINT "Which Mouse Button: ",CHR(Iup::GetBtnPressed()),"\n"
  19.   PRINT "Button Up/Dn State: ",Iup::GetBtnState(),"\n"
  20. END SUB
  21.  
  22. SUB btn2_clicked
  23.   PRINT "Main 0 Button 2 Pressed\n"
  24. END SUB
  25.  
  26. SUB btn3_clicked
  27.   PRINT "Main 0 Button 3 Pressed\n"
  28. END SUB
  29.  
  30. SUB win_exit
  31.   ' Good-Bye
  32. END SUB
  33.  
  34. win = DIALOG()
  35. SETPROPERTIES win, "TITLE=\"SBx Main 0\", SIZE=300x"
  36. horzbox = HBOX()
  37. SETPROPERTIES horzbox, "GAP=5"
  38. btn1 = BUTTON()
  39. SETPROPERTIES btn1, "TITLE=\"Button 1\", EXPAND=HORIZONTAL"
  40. btn2 = BUTTON()
  41. SETPROPERTIES btn2, "TITLE=\"Button 2\", EXPAND=HORIZONTAL"
  42. btn3 = BUTTON()
  43. SETPROPERTIES btn3, "TITLE=\"Button 3\", EXPAND=HORIZONTAL"
  44. APPEND horzbox, btn1
  45. APPEND horzbox, btn2
  46. APPEND horzbox, btn3
  47. APPEND win, horzbox
  48. Iup::SetThreadCallback(win, "CLOSE_CB", ADDRESS(win_exit()), 0)
  49. Iup::SetThreadCallback(btn1, "BUTTON_CB", ADDRESS(btn1_clicked()), 0)
  50. Iup::SetThreadCallback(btn2, "ACTION", ADDRESS(btn2_clicked()), 0)
  51. Iup::SetThreadCallback(btn3, "ACTION", ADDRESS(btn3_clicked()), 0)
  52. SHOW win
  53.  
  54. ' Puppet Show
  55.  
  56. sb1 = SB_ThreadStart("SBx_T1",undef,"/etc/scriba/basic.conf")
  57. SB_Wait("sb1_loaded")
  58. sb1_rtn = SB_CallSubArgs(sb1, "main::main", sb1)
  59.  
  60. sb2 = SB_ThreadStart("SBx_T2",undef,"/etc/scriba/basic.conf")
  61. SB_Wait("sb2_loaded")
  62. sb2_rtn = SB_CallSubArgs(sb2, "main::main", sb2)
  63.  
  64. threads = 3
  65.  
  66. t_event = mt::GetVariable("Callback_Map")
  67. SPLITA t_event BY "\n" TO e_list
  68. FOR x = 0 TO UBOUND(e_list)
  69.   SPLITA e_list[x] BY "|" TO e_array
  70.   event{e_array[0]}[0] = e_array[0]
  71.   event{e_array[0]}[1] = e_array[1]
  72.   event{e_array[0]}[2] = e_array[2]
  73. NEXT
  74.  
  75. WHILE threads
  76.   Iup::LoopStep()
  77.   this_event = Iup::GetEvent()
  78.   hex_event = Iup::BB_HTA(this_event)
  79.   IF hex_event = event{hex_event}[0] THEN
  80.     IF event{hex_event}[2] = 0 THEN
  81.       ICALL event{hex_event}[1]
  82.     ELSE IF event{hex_event}[2] = 1 THEN
  83.       SB_CallSub(sb1, event{hex_event}[1])
  84.     ELSE IF event{hex_event}[2] = 2 THEN
  85.       SB_CallSub(sb2, event{hex_event}[1])
  86.     END IF
  87.     IF Iup::GetActionName() = "CLOSE_CB" THEN threads -= 1
  88.   END IF  
  89.   SB_msSleep(250)
  90. WEND
  91.  
  92. Iup::Close()
  93. SB_Destroy(sb2)
  94. SB_Destroy(sb1)
  95.  

SBx_T1 - T2 is the same
Code: Script BASIC
  1. ' SBx_buttons Example (Thread 1)
  2.  
  3. IMPORT mt.bas
  4. IMPORT iup.bas
  5. IMPORT "SBx"
  6.  
  7. SUB btn1_clicked
  8.   PRINT "Thread 1 Button 1 Pressed\n"
  9.   PRINT "Which Mouse Button: ",CHR(Iup::GetBtnPressed()),"\n"
  10.   PRINT "Button Up/Dn State: ",Iup::GetBtnState(),"\n"
  11. END SUB
  12.  
  13. SUB btn2_clicked
  14.   PRINT "Thread 1 Button 2 Pressed\n"
  15. END SUB
  16.  
  17. SUB btn3_clicked
  18.   PRINT "Thread 1 Button 3 Pressed\n"
  19. END SUB
  20.  
  21. SUB win_exit
  22.   ' Good-Bye
  23. END SUB
  24.  
  25. SUB main
  26.   win = DIALOG()
  27.   SETPROPERTIES win, "TITLE=\"SBx Thread 1\", SIZE=300x"
  28.   horzbox = HBOX()
  29.   SETPROPERTIES horzbox, "GAP=5"
  30.   btn1 = BUTTON()
  31.   SETPROPERTIES btn1, "TITLE=\"Button 1\", EXPAND=HORIZONTAL"
  32.   btn2 = BUTTON()
  33.   SETPROPERTIES btn2, "TITLE=\"Button 2\", EXPAND=HORIZONTAL"
  34.   btn3 = BUTTON()
  35.   SETPROPERTIES btn3, "TITLE=\"Button 3\", EXPAND=HORIZONTAL"
  36.   APPEND horzbox, btn1
  37.   APPEND horzbox, btn2
  38.   APPEND horzbox, btn3
  39.   APPEND win, horzbox
  40.   Iup::SetThreadCallback(win, "CLOSE_CB", "main::win_exit", 1)
  41.   Iup::SetThreadCallback(btn1, "BUTTON_CB", "main::btn1_clicked", 1)
  42.   Iup::SetThreadCallback(btn2, "ACTION", "main::btn2_clicked", 1)
  43.   Iup::SetThreadCallback(btn3, "ACTION", "main::btn3_clicked", 1)
  44.   SHOW win
  45. END SUB
  46. mt::SetVariable("sb1_loaded","OK")
  47.  

.
Title: Re: SBT
Post by: Mike Lobanovsky on May 26, 2015, 06:52:58 AM
Congrats, John, going successfully multithreaded with an interpretative GUI is no easy task at all! :D
Title: Re: SBT
Post by: JRS on May 26, 2015, 08:48:33 AM
Thanks Mike but the credit really needs to go to Peter Verhas (SB & sbhttpd) and Charles Pegge (DLLC) for showing me the way.

I've been after a SBT like extension module for years. It was a now or never kind of thing.  :)
Title: SBx 3 Forms
Post by: JRS on May 27, 2015, 10:20:28 PM
As it turns out, you really don't need threading to achieve multiple window support. As I see it threading of a IUP dialog would be a special use case. It's good to know it can be done.

Code: Script BASIC
  1. ' SBx_buttons Example (3 Form Version)
  2.  
  3. IMPORT iup.bas
  4. IMPORT sbt.inc
  5. IMPORT "SBx"
  6.  
  7. ' Form 1 Callback Routines
  8. SUB frm1_btn1_clicked
  9.   PRINT "Form 1 Button 1 Pressed\n"
  10.   PRINT "Which Mouse Button: ",CHR(Iup::GetBtnPressed()),"\n"
  11.   PRINT "Button Up/Dn State: ",Iup::GetBtnState(),"\n"
  12. END SUB
  13.  
  14. SUB frm1_btn2_clicked
  15.   PRINT "Form 1 Button 2 Pressed\n"
  16. END SUB
  17.  
  18. SUB frm1_btn3_clicked
  19.   PRINT "Form 1 Button 3 Pressed\n"
  20. END SUB
  21.  
  22. ' Form 2  Callback Routines
  23. SUB frm2_btn1_clicked
  24.   PRINT "Form 2 Button 1 Pressed\n"
  25.   PRINT "Which Mouse Button: ",CHR(Iup::GetBtnPressed()),"\n"
  26.   PRINT "Button Up/Dn State: ",Iup::GetBtnState(),"\n"
  27. END SUB
  28.  
  29. SUB frm2_btn2_clicked
  30.   PRINT "Form 2 Button 2 Pressed\n"
  31. END SUB
  32.  
  33. SUB frm2_btn3_clicked
  34.   PRINT "Form 2 Button 3 Pressed\n"
  35. END SUB
  36.  
  37. ' Form 3 Callback Routines
  38. SUB frm3_btn1_clicked
  39.   PRINT "Form 3 Button 1 Pressed\n"
  40.   PRINT "Which Mouse Button: ",CHR(Iup::GetBtnPressed()),"\n"
  41.   PRINT "Button Up/Dn State: ",Iup::GetBtnState(),"\n"
  42. END SUB
  43.  
  44. SUB frm3_btn2_clicked
  45.   PRINT "Form 3 Button 2 Pressed\n"
  46. END SUB
  47.  
  48. SUB frm3_btn3_clicked
  49.   PRINT "Form 3 Button 3 Pressed\n"
  50. END SUB
  51.  
  52. SUB win_exit
  53.   ' Good-Bye
  54. END SUB
  55.  
  56. Iup::Open()
  57.  
  58. ' Form 1 Dialog
  59. win1 = DIALOG()
  60. SETPROPERTIES(win1, "TITLE=\"SBx Form 1\", SIZE=300x")
  61. horzbox1 = HBOX()
  62. SETPROPERTIES horzbox1, "GAP=5"
  63. btn1_1 = BUTTON()
  64. SETPROPERTIES btn1_1, "TITLE=\"Button 1\", EXPAND=HORIZONTAL"
  65. btn1_2 = BUTTON()
  66. SETPROPERTIES btn1_2, "TITLE=\"Button 2\", EXPAND=HORIZONTAL"
  67. btn1_3 = BUTTON()
  68. SETPROPERTIES btn1_3, "TITLE=\"Button 3\", EXPAND=HORIZONTAL"
  69. APPEND horzbox1, btn1_1
  70. APPEND horzbox1, btn1_2
  71. APPEND horzbox1, btn1_3
  72. APPEND win1, horzbox1
  73. Iup::SetCallback win1, "CLOSE_CB", ADDRESS(win_exit())
  74. Iup::SetCallback btn1_1, "BUTTON_CB", ADDRESS(frm1_btn1_clicked())
  75. Iup::SetCallback btn1_2, "ACTION", ADDRESS(frm1_btn2_clicked())
  76. Iup::SetCallback btn1_3, "ACTION", ADDRESS(frm1_btn3_clicked())
  77. Iup::ShowXY(win1,500,200)
  78.  
  79.  
  80.  
  81. ' Form 2 Dialog
  82. win2 = DIALOG()
  83. SETPROPERTIES win2, "TITLE=\"SBx Form 2\", SIZE=300x"
  84. horzbox2 = HBOX()
  85. SETPROPERTIES horzbox2, "GAP=5"
  86. btn2_1 = BUTTON()
  87. SETPROPERTIES btn2_1, "TITLE=\"Button 1\", EXPAND=HORIZONTAL"
  88. btn2_2 = BUTTON()
  89. SETPROPERTIES btn2_2, "TITLE=\"Button 2\", EXPAND=HORIZONTAL"
  90. btn2_3 = BUTTON()
  91. SETPROPERTIES btn2_3, "TITLE=\"Button 3\", EXPAND=HORIZONTAL"
  92. APPEND horzbox2, btn2_1
  93. APPEND horzbox2, btn2_2
  94. APPEND horzbox2, btn2_3
  95. APPEND win2, horzbox2
  96. Iup::SetCallback win2, "CLOSE_CB", ADDRESS(win_exit())
  97. Iup::SetCallback btn2_1, "BUTTON_CB", ADDRESS(frm2_btn1_clicked())
  98. Iup::SetCallback btn2_2, "ACTION", ADDRESS(frm2_btn2_clicked())
  99. Iup::SetCallback btn2_3, "ACTION", ADDRESS(frm2_btn3_clicked())
  100. Iup::ShowXY(win2,500,400)
  101.  
  102. ' Form 3 Dialog
  103. win3 = DIALOG()
  104. SETPROPERTIES win3, "TITLE=\"SBx Form 3\", SIZE=300x"
  105. horzbox3 = HBOX()
  106. SETPROPERTIES horzbox3, "GAP=5"
  107. btn3_1 = BUTTON()
  108. SETPROPERTIES btn3_1, "TITLE=\"Button 1\", EXPAND=HORIZONTAL"
  109. btn3_2 = BUTTON()
  110. SETPROPERTIES btn3_2, "TITLE=\"Button 2\", EXPAND=HORIZONTAL"
  111. btn3_3 = BUTTON()
  112. SETPROPERTIES btn3_3, "TITLE=\"Button 3\", EXPAND=HORIZONTAL"
  113. APPEND horzbox3, btn3_1
  114. APPEND horzbox3, btn3_2
  115. APPEND horzbox3, btn3_3
  116. APPEND win3, horzbox3
  117. Iup::SetCallback win3, "CLOSE_CB", ADDRESS(win_exit())
  118. Iup::SetCallback btn3_1, "BUTTON_CB", ADDRESS(frm3_btn1_clicked())
  119. Iup::SetCallback btn3_2, "ACTION", ADDRESS(frm3_btn2_clicked())
  120. Iup::SetCallback btn3_3, "ACTION", ADDRESS(frm3_btn3_clicked())
  121. Iup::ShowXY(win3,500,600)
  122.  
  123.  
  124.  
  125. ' Event Loop
  126. windows = 3
  127.  
  128. WHILE windows
  129.   Iup::LoopStep()
  130.   this_event = Iup::GetEvent()
  131.   this_event = Iup::BB_HTA(this_event)
  132.   IF this_event = event{this_event}[0] THEN
  133.     ICALL event{this_event}[1]
  134.     IF Iup::GetActionName() = "CLOSE_CB" THEN windows -= 1
  135.   END IF  
  136.   SB_msSleep(250)
  137. WEND
  138.  
  139. Iup::Close
  140.  

iup.bas - I changed the Iup::SetCallback() to create the event array in the main namespace.
Code: Script BASIC
  1. FUNCTION SetCallback(ih, aname, fname)
  2.   main::event{BB_HTA(ih)}[0] = BB_HTA(ih)
  3.   main::event{BB_HTA(ih)}[1] = fname
  4.   SetCallback = __SetCallback(ih, aname)
  5. END FUNCTION
  6.  


.
Title: Re: SBT - thread testing
Post by: JRS on June 09, 2015, 11:49:17 PM
I'm now 100% convinced that Script BASIC is truly running in a threaded mode.

ttmain.sb
Code: Script BASIC
  1. IMPORT sbt.inc
  2.  
  3. SB_ThreadStart("tt1.sb",undef,"/etc/scriba/basic.conf")
  4. SB_ThreadStart("tt2.sb",undef,"/etc/scriba/basic.conf")
  5. SB_ThreadStart("tt3.sb",undef,"/etc/scriba/basic.conf")
  6.  
  7. PRINT "SB Host\n"
  8. SB_msSleep(50000)
  9.  

tt1.sb
Code: Script BASIC
  1. ' Test Thread
  2.  
  3. IMPORT sbt.inc
  4.  
  5. FOR x = 1 TO 10
  6.   PRINT "Thread 1: ",x,"\n"
  7.   SB_msSleep(1000)
  8. NEXT
  9.  
  10. SB_ThreadEnd
  11.  

tt2.sb
Code: Script BASIC
  1. ' Test Thread
  2.  
  3. IMPORT sbt.inc
  4.  
  5. FOR x = 1 TO 10
  6.   PRINT "Thread 2: ",x,"\n"
  7.   SB_msSleep(2000)
  8. NEXT
  9.  
  10. SB_ThreadEnd
  11.  

tt3.sb
Code: Script BASIC
  1. ' Test Thread
  2.  
  3. IMPORT sbt.inc
  4.  
  5. FOR x = 1 TO 10
  6.   PRINT "Thread 3: ",x,"\n"
  7.   SB_msSleep(3000)
  8. NEXT
  9.  
  10. SB_ThreadEnd
  11.  

Output

scriptbasic: ~/sb22/test $ time scriba ttmain.sb
Thread 1: 1
SB Host
Thread 1: 2
Thread 2: 1
Thread 1: 3
Thread 3: 1
Thread 1: 4
Thread 2: 2
Thread 1: 5
Thread 3: 2
Thread 1: 6
Thread 2: 3
Thread 1: 7
Thread 1: 8
Thread 2: 4
Thread 3: 3
Thread 1: 9
Thread 1: 10
Thread 2: 5
Thread 3: 4
Thread 2: 6
Thread 2: 7
Thread 3: 5
Thread 2: 8
Thread 3: 6
Thread 2: 9
Thread 3: 7
Thread 2: 10
Thread 3: 8
Thread 3: 9
Thread 3: 10
 
real    0m0.054s
user    0m0.005s
sys     0m0.002s
scriptbasic: ~/sb22/test $