[SOLVED] CreateThread() within Types?

Started by Midimaster, July 15, 2021, 06:12:44

Previous topic - Next topic

Midimaster

For my Ringbuffer I need a Thread which controls the feeding of the audio-device independent of the users main code. So the user needs to add a function WatchLoop() and register it with CreateThread():
Code (BlitzMax) Select

Global RingBuffer:RingBufferClass = New RingBufferClass
Local WatchThread:TThread=CreateThread(WatchLoop, "")
....

Function  WatchLoop:Object(data:Object)
Repeat
Delay 3
RingBuffer.Watch
Forever
End Function


Type RingBufferClass
         .....
Method Watch()
....
End Method
End Type


This runs perfect. The code of the function WatchLoop() is fix and needs not to be changed by the user.


As the code is always the same I would like to make the function WatchLoop() a part of the TYPE RingBufferClass.
But when I try this:
Code (BlitzMax) Select

Global RingBuffer:RingBufferClass = New RingBufferClass
Local WatchThread:TThread=CreateThread(RingBuffer.WatchLoop_B, "")
....

Type RingBufferClass
        .....
Method Watch()
....
End Method

Method WatchLoop_B:Object(data:Object)
Repeat
Delay 3
Watch
Forever
End Method
End Type


...I get this error message:
Compile Error: Missing function argument 'data'.
[C:/BasicNG/FreeAudio/rauschen.bmx;15;0]
Build Error: failed to compile (-1) C:/BasicNG/FreeAudio/rauschen.bmx


What do I forget?

How can I integrate Thread-functions as a Method of a TYPE?

...back from Egypt

Derron

You already identified that you tried to pass a "method" instead of a "function".

You need to pass a function to "CreateThread" but you can also pass "data" (which you pass empty for now - as "").
A Method is a function with the instance passed as "first parameter" so like a "function(self, firstparameter)" instead of "self.method(firstparameter)".

So ... your current approach would be to create a new thread for each ringbuffer ... ok that's then:

Code (Blitzmax) Select

Global RingBuffer:RingBufferClass = New RingBufferClass
Local WatchThread:TThread=CreateThread(RingBufferClass.WatchLoop, RingBuffer)
....

Type RingBufferClass
        .....
        Method Watch()
                ....
        End Method

        Function WatchLoop:Object(data:Object)
                Local buffer:RingBufferClass = RingBufferClass(data)
                If not buffer then Return Null 
                Repeat
                        Delay 3
                        buffer.Watch()
                Forever
        End Function
End Type


So you pass the "to use buffer" as param to the createThread-call. And inside the function you cast that back into your class.


the issue is: you would create a new thread for each buffer you create. You could also have a thread which "watch()" all known buffers (so store all buffers in a "active" collection and iterate over this list/array ...

Btw you could create this thread as soon as you actually want to use you. So instead of precreating the thread you could have some "Init()" method in the buffer class which creates a thread if not done yet.
Or even have some Methods - which check an integer if true or false, and if not initialized yet, create the thread.


bye
Ron

Midimaster

Quote from: Derron on July 15, 2021, 07:13:27...So you pass the "to use buffer" as param to the createThread-call. And inside the function you cast that back into your class...

Ron, you are the best! I completely understand your explanation. Thanks a lot for the detailed description. I never understood, what this "data:Object" is good for.

I immediately added this to my RingBufferClass. Now it looks more professional and it is more comfortable for the user, because he need not to think about registering the thread:

Code (BlitzMax) Select
Global RingBuffer:RingBufferClass = New RingBufferClass
RingBuffer.CreateNew(12000, SF_MONO16LE)
'thats all!
...
Type RingBufferClass
Method CreateNew(Hertz:Int, UserFormat:Int=SF_STEREO16LE , Latency:Int=8)
...
Local WatchThread:TThread = CreateThread(WatchLoop,Self)
End Method

Private

Method Watch()
...
End Method

Function WatchLoop:Object(data:Object)
        Local RingBuffer:RingBufferClass = RingBufferClass(data)
        If Not RingBuffer Then Return Null 
Repeat
Delay 3
RingBuffer.Watch
Forever
End Function
End Type


As you can see I also changed the point, where the thread is created. This was my original objective. With your help I could reach it.

Only three questions remaining:

1.
Can the variable WatchThread:TThread stay local? Or need I store it into a field of the instance?

2.
Does this casting of data has any consequences on performance?

3.
Do I need to unregister those threads. Or does END of the main code stopp also the threads correctly?
...back from Egypt

Henri

Methods and local variables reside in the stack memory that is erased after use, so for this reason you can't use them in any instance where a handling function is required (aka callback function like in Createthread). Functions and globals reside in heap memory that is constant for the whole lifespan of the application.

1. thread will have a life of its own regardless, but if you want to check if the thread is running or terminate it, you need to store the id somewhere.

2. No.

3. You do not need to unregister threads. They will remove themselves after they done or are manually terminated or when the application closes.

-Henri
- Got 01100011 problems, but the bit ain't 00000001