Assign UDT member Sub to a Sub & start it as Thread

General FreeBASIC programming questions.
Tourist Trap
Posts: 2817
Joined: Jun 02, 2015 16:24

Assign UDT member Sub to a Sub & start it as Thread

Postby Tourist Trap » Jun 24, 2015 13:44

[Edit] This issue is solved

See also those links :
viewtopic.php?f=3&t=23590
http://lampiweb.com/help/freebasic/ProP ... nters.html

Part1Assigning a member Sub to external Sub

1_This code below won't work:

Code: Select all

'#########     'Type Definition
Type ExampleUDT
     Declare Sub udtSub()
     As Object _anything
End Type

Sub ExampleUDT.udtSub()
     ''Do something
End Sub

''#########     Main
Dim As ExampleUDT udt1
Dim As Sub  udtSubCopy
''Attempt to assign udt1.udtSub to udtSubCopy
udtSubCopy = udt1.udtSub  ''--> error 17: Syntax error in 'udtSubCopy = udt1.udtSub'
udtSubCopy = * ProcPtr(udt1.udtSub)    ''--> error 8: Undefined symbol, found 'udt1' in 'udtSubCopy = * ProcPtr(udt1.udtSub)


2_Following works well:

Code: Select all

'#########     'Type Definition
Type ExampleUDT
     Declare Static Sub udtStaticSub(ByRef This As ExampleUDT)
     As Integer _anything
End Type

Sub ExampleUDT.udtStaticSub(ByRef This As ExampleUDT)
    ''can NOT access to This directly so passed in argument...
    This._anything += 1
    ? This._anything
End Sub
'##########     Main

Dim As ExampleUDT udt1
Dim As Sub(As ExampleUDT) udtSubCopy

udtSubCopy = @ExampleUDT.udtStaticSub()
udtSubCopy(udt1)

Sleep


Part2Threading

Code: Select all

'#########     'Type Definition
Type ExampleUDT
     Declare Static Sub udtStaticSub(ByRef This As ExampleUDT)
     
     As String _name
     As Integer _anything
End Type

Sub ExampleUDT.udtStaticSub(ByRef This As ExampleUDT)
    ''can NOT access to This directly so passed in argument...
      For _i As Integer = 1 To 10       ''Should be a constant length loop!
          This._anything += 1
           ? This._name; This._anything
           Sleep 10
      Next _i       
End Sub

''#########     Main

Dim As ExampleUDT udt1
    udt1._name = "UDT1  "
Dim As ExampleUDT udt2
    udt2._name = "UDT2  "

Dim As Sub(As ExampleUDT) udtSubCopy
    udtSubCopy = @ExampleUDT.udtStaticSub()

''2 lines below launch 2 threads that would stand for "(ExampleUDT)udt1,2.udtStaticSub"
Dim handle1 As Any Ptr = ThreadCreate(udtSubCopy,@udt1)
Dim handle2 As Any Ptr = ThreadCreate(udtSubCopy,@udt2)

Sleep


Thanks
Last edited by Tourist Trap on Jun 24, 2015 19:35, edited 1 time in total.
RockTheSchock
Posts: 226
Joined: Mar 12, 2006 16:25

Re: Weird behaviour inside Thread created after 'MemberSub C

Postby RockTheSchock » Jun 24, 2015 15:35

Well the only problem i see is you need to wrap your screen output statements with screenlock / screenunlock. Beside that you should never access non static member vars from a static function. This._anything is just nonsense. Please look again at my example code.
viewtopic.php?f=3&t=23590#p207895

I could imagin overwriting "this" symbol with a parameter name could cause trouble. While it seems to work here though.

Code: Select all

Sub ExampleUDT.udtStaticSub(ByRef This As ExampleUDT)
   ''can NOT access to This directly so passed in argument...
   For _i As Integer = 1 To 10       ''Should be a constant length loop!
      This._anything += 1
      ScreenLock
      Print  "i:" & _i,"anything:" & This._anything,This._name
      ScreenUnLock
      Sleep 10
   Next
End Sub
Tourist Trap
Posts: 2817
Joined: Jun 02, 2015 16:24

Re: Weird behaviour inside Thread created after 'MemberSub C

Postby Tourist Trap » Jun 24, 2015 16:06

RockTheSchock wrote: Beside that you should never access non static member vars from a static function. This._anything is just nonsense. Please look again at my example code.
viewtopic.php?f=3&t=23590#p207895


Thanks, I've been reading your code previously but there is something that I wasn't able to understand. Maybe even you would admit that it is strange to wrap access to the value v by the mean of a non-static overloaded version of the static function we would finally use... (setvalue comes in static and non-static version, non static version just to grant access to v apparently)

Would you say that accessing the needed non-static field directly only is forbiden, and however possible if wrapped in any function?

Last point, what's happening wrong when accessing non-static member var from a static function? In my example above, the loop upperbound is clearly under influence of some internal phenomen.
fxm
Posts: 9529
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Weird behaviour inside Thread created after 'MemberSub C

Postby fxm » Jun 24, 2015 16:56

I do not see any execution problem the your last code!
It works as it have been coded (including the loop).

If you want suppress the compiler warnings, you must normally pass an 'any ptr' and not a 'reference' to the Sub 'ExampleUDT.udtStaticSub()' to match with 'ThreadCreate()':

Code: Select all

'#########     'Type Definition
Type ExampleUDT
     Declare Static Sub udtStaticSub(ByVal p As Any Ptr)
     
     As String _name
     As Integer _anything
End Type

Sub ExampleUDT.udtStaticSub(ByVal p As Any Ptr)
    ''can NOT access to This directly so passed in argument...
        With *Cast(ExampleUDT Ptr, p)
            For _i As Integer = 1 To 10       ''Should be a constant length loop!
                ._anything += 1
                Screenlock
                ? ._name; ._anything
                Screenunlock
                Sleep 10
            Next _i 
        End With
End Sub

''#########     Main

Dim As ExampleUDT udt1
     udt1._name = "UDT1  "
Dim As ExampleUDT udt2
     udt2._name = "UDT2  "

Dim As Sub(ByVal As Any Ptr) udtSubCopy
     udtSubCopy = @ExampleUDT.udtStaticSub

udtSubCopy(@udt1)         '' Commenting or uncommenting those
Print
udtSubCopy(@udt2)         '' will change the for...next loop length
Print
udtSubCopy(@udt1)         ''
Print

Dim handle1 As Any Ptr = ThreadCreate(udtSubCopy,@udt1)
Dim handle2 As Any Ptr = ThreadCreate(udtSubCopy,@udt2)

Sleep

Code: Select all

UDT1   1
UDT1   2
UDT1   3
UDT1   4
UDT1   5
UDT1   6
UDT1   7
UDT1   8
UDT1   9
UDT1   10

UDT2   1
UDT2   2
UDT2   3
UDT2   4
UDT2   5
UDT2   6
UDT2   7
UDT2   8
UDT2   9
UDT2   10

UDT1   11
UDT1   12
UDT1   13
UDT1   14
UDT1   15
UDT1   16
UDT1   17
UDT1   18
UDT1   19
UDT1   20

UDT1   21
UDT2   11
UDT1   22
UDT2   12
UDT1   23
UDT2   13
UDT2   14
UDT1   24
UDT1   25
UDT2   15
UDT1   26
UDT2   16
UDT2   17
UDT1   27
UDT2   18
UDT1   28
UDT1   29
UDT2   19
UDT1   30
UDT2   20

Variant of code to suppress the compiler warnings, but using always compatibility between passing byref and passing byval ptr:

Code: Select all

'#########     'Type Definition
Type ExampleUDT
     Declare Static Sub udtStaticSub(Byref myThis As ExampleUDT)
     
     As String _name
     As Integer _anything
End Type

Sub ExampleUDT.udtStaticSub(Byref myThis As ExampleUDT)
    ''can NOT access to This directly so passed in argument...
        For _i As Integer = 1 To 10       ''Should be a constant length loop!
            myThis._anything += 1
            Screenlock
            ? myThis._name; myThis._anything
            Screenunlock
            Sleep 10
        Next _i 
End Sub

''#########     Main

Dim As ExampleUDT udt1
     udt1._name = "UDT1  "
Dim As ExampleUDT udt2
     udt2._name = "UDT2  "

Dim As Sub(ByVal As Any Ptr) udtSubCopy
     udtSubCopy = Cast(Sub(ByVal As Any Ptr), @ExampleUDT.udtStaticSub)

udtSubCopy(@udt1)         '' Commenting or uncommenting those
Print
udtSubCopy(@udt2)         '' will change the for...next loop length
Print
udtSubCopy(@udt1)         ''
Print

Dim handle1 As Any Ptr = ThreadCreate(udtSubCopy,@udt1)
Dim handle2 As Any Ptr = ThreadCreate(udtSubCopy,@udt2)

Sleep
Last edited by fxm on Jun 25, 2015 9:22, edited 2 times in total.
Tourist Trap
Posts: 2817
Joined: Jun 02, 2015 16:24

Re: Weird behaviour inside Thread created after 'MemberSub C

Postby Tourist Trap » Jun 24, 2015 17:23

fxm wrote:I do not see any execution problem the your last code!
It works as it have been coded (including the loop).

Sorry, I see now! By firing udtSubCopy() before ThreadCreate I dont know why I believed it wouldn't launch the loop, don't know why I believed this - is quite silly.

So Ok this part is closed. I'm still on the third part now. If I can't solve this next step I'll post feedback. Else I'll post the result anyway but some later since I will have some graphics to add then.

Thanks all again. Freebasic is a very nice tool and people on the forum very helpful, that makes life easier!
fxm
Posts: 9529
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Weird behaviour inside Thread created after 'MemberSub C

Postby fxm » Jun 24, 2015 17:52

Explanation about use of [ScreenLock...ScreenUnlock] and threading

In Win32 and Linux the screen is locked by stopping the thread that processes also the OS' events.

Thus, when the screen is locked, all other threads are stopped up to the screen unlocking.
Only runs the thread which has locked the screen.
This behavior occurs when screen is locked by any thread, including the main thread which executes the main program.
fxm
Posts: 9529
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Weird behaviour inside Thread created after 'MemberSub C

Postby fxm » Jun 24, 2015 18:23

fxm wrote:If you want suppress the compiler warnings, you must pass an 'any ptr' and not a 'reference' to 'ExampleUDT.udtStaticSub()' to match with 'ThreadCreate()':
.....

There is compatibility between passing "Byref As DataType" and passing "Byval As DataType Ptr" to a procedure (in internal, passing byref is coded as passing byval ptr), but the compiler nevertheless outputs a warning:

Code: Select all

Sub Prnt1 (Byref s As String)
  Print s
End Sub

Sub Prnt2 (Byval ps As String Ptr)
  Print *ps
End Sub


Dim As String s = "FreeBASIC"

Dim As Sub (Byval As String Ptr) ps1 = @Prnt1
ps1(@s)

Dim As Sub (Byref As String) ps2 = @Prnt2
ps2(s)

Sleep
Compiler output:
.....\FBIde0.4.6r4_fbc1.03.0\FBIDETEMP.bas(12) warning 4(1): Suspicious pointer assignment
.....\FBIde0.4.6r4_fbc1.03.0\FBIDETEMP.bas(15) warning 4(1): Suspicious pointer assignment

Code: Select all

FreeBASIC
FreeBASIC
Tourist Trap
Posts: 2817
Joined: Jun 02, 2015 16:24

Re:

Postby Tourist Trap » Jun 25, 2015 13:57

fxm wrote:Thus, when the screen is locked, all other threads are stopped up to the screen unlocking.

Isn't that what is supposed to do MutexLock keyword ?


But, I would like to expand more about what I'm trying to do. I'm trying to monitor graphically the lifetime of a given UDT. It doesnt refere to any realistic scenario, it is for learning purpose in order to bring a more appealing approach to UDT lifetime subject, which can prove to be rather arid.

There could be a lot of different approaches but the principle I've choosen is very natural. I want to set up an User Type that holds an "lifedisplay" function. This function is able to perfom a plot that one may compare to its heartbeats.
The idea here is that the heartbeat function reflects an UDT instance from the lifetime point of view. That's why also a static method would not be relevant (even if it is only what I've been able to achieve so far..).

So now I've made a first code that shoud be explicit enough. Of course as everyone starting with threading I'm encountering freezes and I'm not sure about the good practices, so any help would be very welcome.

The second and main problem I have is that I cant use a static method as said above. And theorically the thread should be launched via constructor. Today it's static and launched very externally from the UDT point of view.

Here is my demo code at present day:

Code: Select all

''
'' Program to monitor UDT lifetime depending on different life event
''

#Include "fbgfx.bi"                  ''Absolutly needed here
#Include "fbthread.bi"               ''both for thread management

''****************************
''                      GLOBAL
''****************************

#Define fC 14                         ''Little color utility
#Define _color(_c) (_c * (-(Abs(_c)<16))) -fC * Not((Abs(_c)<16))

''Declare a global variable for time measurement
Dim Shared As Integer G_absoluteTime = 0

''Declare a global variable for thread lock/unlock management
Dim Shared As Any Ptr G_threadSynchronizer

''****************************
''                        TYPE
''****************************

''Try below to define an User Type able to plot its own life pulse
Type UDT
    Declare Constructor()
    Declare Destructor()
   
    Declare Static Sub TrackingLifeSign(ByRef This As UDT)
   
    Static _Order As UInteger
    As Integer _clock = 0
    As Integer _yPos = 0
    As String _uDTName = ""
End Type 'UDT
Dim As UInteger UDT._Order = 9

Constructor UDT()
''Give this UDT instance its name, preferably an unic one
   Dim sread(1 To 9) As String
   Dim index As UInteger
   
   For _i As UInteger = 1 To 3
        index = CUInt(Rnd*8 + 1)
          For _j As UInteger = 1 To index
              Read sread(index)
          Next _j
         Restore label
        This._uDTName &= sread(index)
   Next _i
   
    This._uDTName &= CUInt(Rnd*8 + 1) & CUInt(Rnd*8 + 1)
   
''Initialize internal clock
    This._clock = Timer
   
''Assign to this UDT its order of creation
    This._Order +=1
    This._yPos = This._Order
   
End Constructor 'UDT() default constructor

Destructor UDT()
    ''Nothing more here
End Destructor 'UDT() default destructor

Sub UDT.TrackingLifeSign(ByRef This As UDT)
''The simple fact to get into this Sub should mean that the UDT is alive
       
       Static As Integer param0
       Dim As Integer param1, param2
       Dim As Double dbParam1
       Dim As String _inkey
       Dim As Byte abortSignal
       _inkey = ""
       abortSignal = 0
       param0 = 130 + 3*G_absoluteTime
       Do
            param1 = 130 + 3*G_absoluteTime
            param2 = 20 * This._yPos
            dbParam1 = This._clock/10 - Int(This._clock/10)

           If (abortSignal = 0) Then
               MutexLock G_threadSynchronizer
               ScreenUnLock
                   Line (100, param2 + 10)-(450, param2 + 10), _color(This._yPos),  , &b1100001111
                   Draw String ( 0, param2), This._uDTName , _color(This._yPos)
                    circle (param0, param2+4*Sin(param0)), 2,  _color(0)
                   PSet (param0, param2+4*Sin(param0)),  _color(This._yPos)
                   circle (param1, param2+4*Sin(param1)), 2,  _color(This._yPos)
                   Sleep 80
               'ScreenUnlock
               MutexUnLock G_threadSynchronizer
           EndIf
                    
            _inkey = InKey
            If _inkey = Chr(27) Then
                       abortSignal -= 1
            EndIf
          
            If (abortSignal = -1) Then
                Draw String ( 0, param2), This._uDTName , 7
                  Draw String ( 150, param2), "Viewing sequence aborted by hand" , 11
                Sleep 200
            EndIf
           
            param0 = param1
            This._clock += G_absoluteTime
       Loop Until abortSignal = -2
   
End Sub 'UDT.TrackingLifeSign()

''****************************
''                        MAIN
''****************************

Randomize Timer
Screen 12
G_threadSynchronizer = MutexCreate()

    Dim udt1 As UDT
   
    Dim TrackingLifeSign_ExternalCopy As Sub(As UDT)
   
    TrackingLifeSign_ExternalCopy = @UDT.TrackingLifeSign()
    ''Above : Should be @(udt1).TrackingLifeSign() but wont work!
   
    Dim handle1 As Any Ptr = _
    ThreadCreate(TrackingLifeSign_ExternalCopy, @udt1)
   
   Dim As String eventMessage = ""
   eventMessage = "Awaiting t=80"
   Dim As Integer param1
   Dim As String _inkey
   _inkey = ""
       Do
           '' Can not call udt1.TrackingLifeSign(udt1) due to its loop...
          
           _inkey = InKey
            If _inkey = Chr(27) Then
                       Exit Do
            EndIf
           
           G_absoluteTime = Timer - 100* Int(Timer\100)
           param1 = 130 + 3*G_absoluteTime
          
           MutexLock G_threadSynchronizer
           ScreenLock
           Draw "BM 0,339"
          Draw "C10"
          Draw "R639 D60 L639 U60"
          Draw "BM +1,1"
          Draw "P 1,10"
         
          Draw "BM 0,400"
          Draw "C10"
          Draw "R639 D79 L639 U79"
          Draw "BM +1,1"
          Draw "P 7,10"
                
            Draw "BM " & 100 & ",370"
          Draw "C0"
          Draw "R390 D18 L390 U18"
          Draw "BM +1,1"
          Draw "P 3,0"
           
                Color 10
              Line (0, 370)-(639, 370)
              Draw String (5, 355), "TimeLine"
              Draw String (param1, 370), " '"
             Draw String (param1, 375), Str(G_absoluteTime)
             Color 8
             
            Draw String (5, 405), "Event Triggered"
            Draw String (75, 425), eventMessage
            
          Sleep 80
          Line (370, 5)-(370, 335), 4
         If G_absoluteTime = 80 Then
               eventMessage = "Call for " & udt1._uDTName & ".destructor"
                 udt1.destructor()
                 Draw String (75, 425), eventMessage            
         EndIf
          ScreenUnLock
          MutexUnLock G_threadSynchronizer
         
       Loop

     MutexDestroy G_threadSynchronizer
     ThreadWait(handle1)
     ThreadDetach(handle1)
     ? "MAINEND"
Sleep : End

''****************************
''                        DATA
''****************************

''UDT_Names Subelements
label:
Data "Alp", "Bet", "Gam", "Del", "Eps", "Phi", "Psy", "Mu", "Ome"
fxm
Posts: 9529
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Re:

Postby fxm » Jun 25, 2015 14:17

Tourist Trap wrote:
fxm wrote:Thus, when the screen is locked, all other threads are stopped up to the screen unlocking.

Isn't that what is supposed to do MutexLock keyword ?
Yes, more generally we use Mutex for doing exclusion between threads.

Same example as previously, but with Mutex (instead of ScreenLock):

Code: Select all

'#########     'Type Definition
Type ExampleUDT
     Declare Static Sub udtStaticSub(ByVal p As Any Ptr)
     
     As String _name
     As Integer _anything
     Static As Any Ptr _mutex
End Type

Dim ExampleUDT._mutex As Any ptr

Sub ExampleUDT.udtStaticSub(ByVal p As Any Ptr)
    ''can NOT access to This directly so passed in argument...
        With *Cast(ExampleUDT Ptr, p)
            For _i As Integer = 1 To 10       ''Should be a constant length loop!
                ._anything += 1
                Mutexlock(._mutex)
                ? ._name; ._anything
                Mutexunlock(._mutex)
                Sleep 10
            Next _i 
        End With
End Sub

''#########     Main

Dim As ExampleUDT udt1
     udt1._name = "UDT1  "
Dim As ExampleUDT udt2
     udt2._name = "UDT2  "

Dim As Sub(ByVal As Any Ptr) udtSubCopy
     udtSubCopy = @ExampleUDT.udtStaticSub

udtSubCopy(@udt1)         '' Commenting or uncommenting those
Print
udtSubCopy(@udt2)         '' will change the for...next loop length
Print
udtSubCopy(@udt1)         ''
Print

ExampleUDT._mutex = MutexCreate()
Dim handle1 As Any Ptr = ThreadCreate(udtSubCopy,@udt1)
Dim handle2 As Any Ptr = ThreadCreate(udtSubCopy,@udt2)

ThreadWait(handle1)
ThreadWait(handle2)
MutexDestroy(ExampleUDT._mutex)

Sleep
Tourist Trap
Posts: 2817
Joined: Jun 02, 2015 16:24

Re: Re:

Postby Tourist Trap » Jun 25, 2015 14:41

fxm wrote:Same example as previously, but with Mutex (instead of ScreenLock):

Code: Select all

'#########     'Type Definition
Type ExampleUDT
'(...)
     Static As Any Ptr _mutex
End Type
'(...)

About this point in particular. You have created mutex as static and local. I have done the opposite of course, introducing a global variable for it (shared), and using it in the main loop and the thread function loop both. What is the true gold practice?

About my first concern, I've tried to find the pointer of a subelement of my UDT in order to pass it to ThreadCall directly rather than by name, since I was unable to pass @This._someSub as argument. But I'm really missing something. This keyword, allows perfect access to the UDT member but is still considered as an incomplete type reference? I'm obviously wrong somewhere but cant find where exactly and what is wrong in accessing a non static function since its adress should be somewhere inside the UDT range ?

Code: Select all


Type ExampleUDT
    Declare Constructor
   Declare Sub udtSub()
   
    As Any Ptr _somePtr
End Type 'ExampleUDT

Sub ExampleUDT.udtSub()
    ? "hello"
End Sub

Constructor ExampleUDT
    Static pointerToThis As ExampleUDT ptr
    pointerToThis = @This

? "From inside the constructor"
   ?    "pointerToThis = "; pointerToThis
   ?  "This._somePtr = "; This._somePtr
   ?  "pointerToThis->_somePtr = "; pointerToThis->_somePtr


      Dim As Sub Ptr subPtr1 = @pointerToThis->udtSub ''error 17: Syntax error in 'Dim As Sub Ptr subPtr1 = @pointerToThis->udtSub'
      
      Dim As Sub Ptr subPtr2 = This.udtSub          ''error 17: Syntax error in 'Dim As Sub Ptr subPtr2 = This.udtSub'
      
''Here below what is wanted or anything that do the same
      Dim As Any Ptr tc = ThreadCreate( @(pointerToThis->udtSub) )         '***
   
?   
    This._somePtr = pointerToThis
   
End Constructor 'Defaut ExampleUDT constructor

Dim As ExampleUDT Ptr udt1ptr
Dim As ExampleUDT udt1
udt1ptr = @udt1

? "From outside the constructor"
? "udt1ptr->_somePtr = "; udt1ptr->_somePtr     

? "udt1ptr[0] = "; udt1ptr[0]    ''error 24: Invalid data types in '? "*udt1ptr = "; Str(udt1ptr[0])'

sleep
fxm
Posts: 9529
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Re:

Postby fxm » Jun 25, 2015 16:44

Tourist Trap wrote:
fxm wrote:Same example as previously, but with Mutex (instead of ScreenLock):

Code: Select all

'#########     'Type Definition
Type ExampleUDT
'(...)
     Static As Any Ptr _mutex
End Type
'(...)

About this point in particular. You have created mutex as static and local. I have done the opposite of course, introducing a global variable for it (shared), and using it in the main loop and the thread function loop both. What is the true gold practice?

A (public) static member variable of an UDT is not local. It is just in the namespace UDT. You can access it from anywhere in the program on the UDT name (as for the static member procedure that you call from the main code).
For your case, I also use non-static member variables for the thread pointers:

Code: Select all

'#########     'Type Definition
Type ExampleUDT
     Declare Static Sub udtStaticSub(ByVal p As Any Ptr)
     
     As String _name
     As Integer _anything
     Static As Any Ptr _mutex
     As Any Ptr _handle
End Type

Dim ExampleUDT._mutex As Any ptr

Sub ExampleUDT.udtStaticSub(ByVal p As Any Ptr)
    ''can NOT access to This directly so passed in argument...
        With *Cast(ExampleUDT Ptr, p)
            For _i As Integer = 1 To 10       ''Should be a constant length loop!
                ._anything += 1
                Mutexlock(._mutex)
                ? ._name; ._anything
                Mutexunlock(._mutex)
                Sleep 10
            Next _i 
        End With
End Sub

''#########     Main

Dim As ExampleUDT udt1
     udt1._name = "UDT1  "
Dim As ExampleUDT udt2
     udt2._name = "UDT2  "

Dim As Sub(ByVal As Any Ptr) udtSubCopy
     udtSubCopy = @ExampleUDT.udtStaticSub

udtSubCopy(@udt1)         '' Commenting or uncommenting those
Print
udtSubCopy(@udt2)         '' will change the for...next loop length
Print
udtSubCopy(@udt1)         ''
Print

ExampleUDT._mutex = MutexCreate()
udt1._handle = ThreadCreate(udtSubCopy,@udt1)
udt2._handle = ThreadCreate(udtSubCopy,@udt2)

ThreadWait(udt1._handle)
ThreadWait(udt2._handle)
MutexDestroy(ExampleUDT._mutex)

Sleep
Tourist Trap
Posts: 2817
Joined: Jun 02, 2015 16:24

Re: Re:

Postby Tourist Trap » Jun 25, 2015 17:31

fxm wrote:A (public) static member variable of an UDT is not local. It is just in the namespace UDT. You can access it from anywhere in the program on the UDT name (as for the static member procedure that you call from the main code).
For your case, I also use non-static member variables for the thread pointers:

Code: Select all

 Declare *Static* Sub udtStaticSub(ByVal p As Any Ptr)
     *Static*  As Any Ptr _mutex
     As Any Ptr _handle
End Type

Dim As Sub(ByVal As Any Ptr) udtSubCopy =  @ExampleUDT.udtStaticSub

Ok, I didn't really know about the namespace. I thought the UDT was enclosing a local scope.

Anyway, I see only static stuff here. If you try the code I've posted a few post before, you will see that due to that, the fact that I destroy an instance of the UDT won't destroy the UDT static function, and won't kill the thread. This is however what I would have expected to be able do. But for doing this I must create a thread targeting the Instance Sub, not the static one. And this is what I fail to do...
fxm
Posts: 9529
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Assign UDT member Sub to a Sub & start it as Thread

Postby fxm » Jun 25, 2015 17:57

In FreeBASIC, there is not pointer-to-member-procedure at present time.
A workaround is to use a virtual member procedure, and to use its static pointer in the vtable by passing the reference (This) as parameter.

Always from the same example:

Code: Select all

'#########     'Type Definition
Type ExampleUDT Extends Object
     Declare Virtual Sub udtVirtualSub()
     
     As String _name
     As Integer _anything
     Static As Any Ptr _mutex
     As Any Ptr _handle
End Type

Dim ExampleUDT._mutex As Any ptr

Sub ExampleUDT.udtVirtualSub()
     For _i As Integer = 1 To 10
          This._anything += 1
          Mutexlock(This._mutex)
          ? This._name; This._anything
          Mutexunlock(This._mutex)
          Sleep 10
     Next _i 
End Sub

''#########     Main

Dim As ExampleUDT udt1
     udt1._name = "UDT1  "
Dim As ExampleUDT udt2
     udt2._name = "UDT2  "

Dim As Sub(ByVal As Any Ptr) udtSubCopy1
     udtSubCopy1 = Cast(Any Ptr Ptr Ptr, @udt1)[0][0]

Dim As Sub(ByVal As Any Ptr) udtSubCopy2
     udtSubCopy2 = Cast(Any Ptr Ptr Ptr, @udt2)[0][0]

udtSubCopy1(@udt1)
Print
udtSubCopy2(@udt2)
Print
udtSubCopy1(@udt1)
Print

ExampleUDT._mutex = MutexCreate()
udt1._handle = ThreadCreate(udtSubCopy1,@udt1)
udt2._handle = ThreadCreate(udtSubCopy2,@udt2)

ThreadWait(udt1._handle)
ThreadWait(udt2._handle)
MutexDestroy(ExampleUDT._mutex)

Sleep
Tourist Trap
Posts: 2817
Joined: Jun 02, 2015 16:24

Re: Assign UDT member Sub to a Sub & start it as Thread

Postby Tourist Trap » Jun 25, 2015 18:30

fxm wrote:In FreeBASIC, there is not pointer-to-member-procedure at present time.
A workaround is to use a virtual member procedure, and to use its static pointer in the vtable by passing the reference (This) as parameter.
Code


That's very impressive, those ptr chains, and I really wonder how you managed to know what this is pointing to ;-)
But unfortunately it doesnt seem to work for my purpose. I've refactored (just simplified) your code (with virtual addition) so that you will understand exactly what 's going on :

Code: Select all

    '#########     'Type Definition
    Type ExampleUDT Extends Object
         Declare Virtual Sub udtVirtualSub()
         
         As String _name
         As Integer _anything
         Static As Any Ptr _mutex
         As Any Ptr _handle
    End Type

    Dim ExampleUDT._mutex As Any ptr

    Sub ExampleUDT.udtVirtualSub()
         Dim As String _inkey
         Do
              This._anything += 1
              Mutexlock(This._mutex)
             
                 Select Case This._name
                    Case ""
                       ? "OBJECT DESTROYED ... loop continues"; This._anything
                    Case Else
                       ? This._name & "..." & This._anything
                 End Select
             
              Mutexunlock(This._mutex)
              Sleep 10
              _inkey = InKey
              If _inkey = Chr(27) Then Exit Do
         Loop
    End Sub

''#########     Main

Dim As ExampleUDT udt1
   udt1._name = "UDT1  "

Dim As Sub(ByVal As Any Ptr) udtSubCopy1
   udtSubCopy1 = Cast(Any Ptr Ptr Ptr, @udt1)[0][0]

ExampleUDT._mutex = MutexCreate()

    udt1._handle = ThreadCreate(udtSubCopy1,@udt1)

   Sleep 1000 : udt1.destructor

    ThreadWait(udt1._handle)

MutexDestroy(ExampleUDT._mutex)

Sleep
 

We can see than even after udt1 has been destroyed, the loop wont stop. . .
fxm
Posts: 9529
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Assign UDT member Sub to a Sub & start it as Thread

Postby fxm » Jun 25, 2015 18:38

An example of thread which starts and stops according to the instance construction and instance destruction:

Code: Select all

'#########     'Type Definition
Type ExampleUDT Extends Object
     Declare Virtual Sub udtVirtualSub()
     
     Declare Constructor(Byref S As String)
     Declare destructor()
     
     Declare Static Sub moduleConstructor()
     Declare Static Sub moduleDestructor()
     
     As String _name
     As Integer _anything
     Static As Any Ptr _mutex
     As Any Ptr _handle
     As Integer _stop
End Type

Dim ExampleUDT._mutex As Any ptr

Sub ExampleUDT.udtVirtualSub()
     Mutexlock(This._mutex)
     ? "Thread " & This._name & "starts"
     Mutexunlock(This._mutex)
     While This._stop = 0
          This._anything += 1
          Mutexlock(This._mutex)
          ? This._name; This._anything
          Mutexunlock(This._mutex)
          Sleep 1000
     Wend 
     Mutexlock(This._mutex)
     ? "Thread " & This._name & "stops"
     Mutexunlock(This._mutex)
End Sub

Constructor ExampleUDT(Byref s As String)
     This._name = s
     Dim As Sub(ByVal As Any Ptr) udtSubCopy
     udtSubCopy = Cast(Any Ptr Ptr Ptr, @This)[0][0]
     This._handle = ThreadCreate(udtSubCopy, @This)
End Constructor

Destructor ExampleUDT()
     This._stop = 1
     ThreadWait(This._handle)
End Destructor

Sub ExampleUDT.moduleConstructor() Constructor
  ExampleUDT._mutex = MutexCreate()
End Sub

Sub ExampleUDT.moduleDestructor() Destructor
MutexDestroy(ExampleUDT._mutex)
End Sub


''#########     Main

Scope
  Dim As ExampleUDT udt1 = ExampleUDT("UDT1  ")
  Dim As ExampleUDT udt2 = ExampleUDT("UDT2  ")
  Sleep 10000
End Scope

Sleep

Code: Select all

Thread UDT1  starts
Thread UDT2  starts
UDT1   1
UDT2   1
UDT2   2
UDT1   2
UDT1   3
UDT2   3
UDT2   4
UDT1   4
UDT2   5
UDT1   5
UDT1   6
UDT2   6
UDT2   7
UDT1   7
UDT1   8
UDT2   8
UDT2   9
UDT1   9
UDT1   10
UDT2   10
Thread UDT2  stops
UDT1   11
Thread UDT1  stops

Another example of main part:

Code: Select all

''#########     Main

Dim As ExampleUDT udt1 = ExampleUDT("UDT1  ")
Dim As ExampleUDT udt2 = ExampleUDT("UDT2  ")
Sleep 10000
udt1.destructor()
udt2.destructor()

Sleep


[edit]
Added a static module Constructor/Destructor to Create/Destroy the mutex.
Last edited by fxm on Jun 25, 2015 20:53, edited 2 times in total.

Return to “General”

Who is online

Users browsing this forum: No registered users and 1 guest