Execution timing of a Sub launched as a thread

General FreeBASIC programming questions.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Execution timing of a Sub launched as a thread

Post by fxm »

Code: Select all

Dim As Any Ptr ptid

Sub myThread (Byval p As Any Ptr)
    Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)
    If ptid = 0 Then
        Print "the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made"
    End If
End Sub

Do
    ptid = 0
    ptid = Threadcreate(@myThread, @ptid)
    Threadwait(ptid)
    Sleep 1, 1
Loop Until Inkey <> ""
Referring to the code above and the line:
ptid = Threadcreate(@myThread, @ptid)

- Is the 'myThread()' Sub (launched as a thread) always executed after the 'ptid' assignment (with the created thread ID)?
- In fact, it seems that the first instruction line of 'myThread()' is always executed after the assignment of 'ptid' by 'Threadcreate()', but is this still guaranteed?
Last edited by fxm on Oct 19, 2020 16:40, edited 2 times in total.
Xusinboy Bekchanov
Posts: 783
Joined: Jul 26, 2018 18:28

Re: Execution timing of a Sub launched as a thread

Post by Xusinboy Bekchanov »

Doesn't need to be set to ptid 0:

Code: Select all

Dim As Any Ptr ptid

Sub myThread (ByVal p As Any Ptr)
    Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)
    If ptid = 0 Then
        Print "the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made"
    Else
    	Print "the assignment 'ptid = Threadcreate(@myThread, @ptid)' is made"
    End If
End Sub

Do
    ptid = 0
    ptid = ThreadCreate(@myThread, @ptid)
    'ptid = 0
    ThreadWait(ptid)
    Sleep 1, 1
Loop Until Inkey <> ""
Sleep
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

It was a residue of a testing phase that I corrected now (thanks).
Roland Chastain
Posts: 993
Joined: Nov 24, 2011 19:49
Location: France
Contact:

Re: Execution timing of a Sub launched as a thread

Post by Roland Chastain »

Hello fxm!

I don't really understand what is the purpose of the local ptid variable. If I replace

Code: Select all

If ptid = 0 Then
with

Code: Select all

If p = 0 Then
the print isn't executed.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

In the main code;
- @ptid points to ptid (a pointer itself).
In the thread:
- The passed p parameter is so equal to @ptid.
- So, Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p) allows to get a local copy of the ptid variable of the main code.
Xusinboy Bekchanov
Posts: 783
Joined: Jul 26, 2018 18:28

Re: Execution timing of a Sub launched as a thread

Post by Xusinboy Bekchanov »

fxm wrote: - Is the 'myThread()' Sub (launched as a thread) always executed after the 'ptid' assignment (with the created thread ID)?
- In fact, it seems that the first instruction line of 'myThread()' is always executed after the assignment of 'ptid' by 'Threadcreate()', but is this still guaranteed?
I think that assigning to ptid and executing subsequent commands is faster than starting the thread itself.
Here's an example:

Code: Select all

Dim As Any Ptr ptid

Sub myThread (ByVal p As Any Ptr)
    Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)
    If ptid = 0 Then
        Print "the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made"
    Else
        Print "the assignment 'ptid = Threadcreate(@myThread, @ptid)' is made: " & ptid
    End If
End Sub

Do
    ptid = 0
    ptid = ThreadCreate(@myThread, @ptid)
    ?ptid
    'ThreadWait(ptid)
    'Sleep 1, 1
Loop Until Inkey <> ""
Sleep
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

fxm wrote:

Code: Select all

Dim As Any Ptr ptid

Sub myThread (Byval p As Any Ptr)
    Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)
    If ptid = 0 Then
        Print "the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made"
    End If
End Sub

Do
    ptid = 0
    ptid = Threadcreate(@myThread, @ptid)
    Threadwait(ptid)
    Sleep 1, 1
Loop Until Inkey <> ""
Referring to the code above and the line:
ptid = Threadcreate(@myThread, @ptid)

- Is the 'myThread()' Sub (launched as a thread) always executed after the 'ptid' assignment (with the created thread ID)?
- In fact, it seems that the first instruction line of 'myThread()' is always executed after the assignment of 'ptid' by 'Threadcreate()', but is this still guaranteed?
Maybe Jeff or a developer could confirm this.
If so, we could add it to the documentation.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

I asked myself this question because I wish to use in any thread its ID (returned by Threadcreate()), in order to generate and pass a thread number to a procedure common to all threads and repetitively called by each thread.
So, this procedure could locally declare and address a static memory (not located in the stack of the thread) and specific to each thread (like a Thread Local Storage emulation).
SARG
Posts: 1756
Joined: May 27, 2005 7:15
Location: FRANCE

Re: Execution timing of a Sub launched as a thread

Post by SARG »

I would say not guaranteed.
On Windows the thread runs immediately after creation therefore before the return of Threadcreate(). So the retrieving of ptid could be random.

You could add something like that

Code: Select all

while ptid=0
	sleep 100 ''or smaller value
	ptid = *Cast(Any Ptr Ptr, p)
wend
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Execution timing of a Sub launched as a thread

Post by paul doe »

fxm wrote:...
So, this procedure could locally declare and address a static memory (not located in the stack of the thread) and specific to each thread (like a Thread Local Storage emulation).
I've been using a design like this for some time with no issues (shown here overly simplified of course):

Code: Select all

'' Barebones thread class
type Thread extends Object
  public:
    declare virtual destructor()
    
    declare property running() as boolean
    declare sub start()
    
  protected:
    declare abstract sub run()
    
    as any ptr _thread
    as boolean _running
  
  private:
    declare static sub _run( byval as Thread ptr )
end type

destructor Thread()
  if( _running ) then
    _running = false
    threadWait( _thread )
  end if
end destructor

property Thread.running() as boolean
  return( _running )
end property

sub Thread._run( byval instance as Thread ptr )
  do while( instance->_running )
    instance->run()
    
    sleep( 1, 1 )
  loop
end sub

sub Thread.start()
  if( not _running ) then
    _running = true
    _thread = threadCreate( cast( sub( byval as any ptr ), @_run ), @this )
  end if
end sub

type SimpleThread extends Thread
  public:
    declare constructor()
    declare virtual destructor() override
    
    declare sub run() override
  
  private:
    '' Whatever private memory the thread needs
end type

constructor SimpleThread() : end constructor
destructor SimpleThread() : end destructor

sub SimpleThread.run()
  ? "Running..."
end sub

var t = SimpleThread()

t.start()

do
  ? "Whatever"
  
  sleep( 1, 1 )
loop until( len( inkey() ) )
The same idea can be used for stand-alone procedures, too.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

Thank you both.
Currently I'm using something that looks like this:

Code: Select all

Dim As Any Ptr ptid

Sub myThread (Byval p As Any Ptr)
    Dim As Any Ptr Ptr pptid = p
    While *pptid = 0  '' <-- mandatory ?
    Wend              '' <-- mandatory ?
    Dim As Any Ptr ptid = *pptid
    ' .....
End Sub

Do
    ptid = 0  '' <-- initialization required for the [While...Wend] validity loop in myThread()
    ptid = Threadcreate(@myThread, @ptid)
    Threadwait(ptid)
    Sleep 1, 1
Loop Until Inkey <> ""
Another solution would be to use a mutex locking around 'ptid = Threadcreate(@myThread, @ptid)' in the main thread and around 'Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)' in the child thread:

Code: Select all

Dim As Any Ptr ptid
Dim Shared As Any Ptr pm

Sub myThread (Byval p As Any Ptr)
    Mutexlock(pm)
    Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)
    Mutexunlock(pm)
    ' .....
End Sub

pm = Mutexcreate()
Do
    Mutexlock(pm)
    ptid = Threadcreate(@myThread, @ptid)
    Mutexunlock(pm)
    Threadwait(ptid)
    Sleep 1, 1
Loop Until Inkey <> ""
Mutexdestroy(pm)
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

I tried to estimate the delay (positive or negative) between the return of Threadcreate() and the start of Thread(), with a memorization of the time as similar as possible between the line following Threadcreate() and the first Thread() line (the delay calculation is executed after the end of the thread):

Code: Select all

Dim As Any Ptr ptid
Dim As Double t0
Dim As Any Ptr p0 = @t0
Dim As Double t1
Dim As Single tmin = 10  '' start value

Sub myThread (Byval p As Any Ptr)
    *Cast(Double Ptr, p) = Timer
End Sub

Do
    ptid = Threadcreate(@myThread, @t1)
    *Cast(Double Ptr, p0) = Timer
    
    Threadwait(ptid)
    
    If t1 - t0 < tmin Then
        tmin = t1 - t0
        Print "Tmin="; tmin * 1000; " ms"
    End If
Loop Until Inkey <> ""
After a while of observation, I found negative values:
.....
.....
Tmin= 0.02979999 ms
Tmin= 0.01940003 ms
Tmin= 0.01150003 ms
Tmin=-0.02859998 ms
Tmin=-0.05039998 ms
Tmin=-0.1064 ms
Tmin=-0.1098 ms
Tmin=-0.1207 ms
  • mean processor use: 40 %
So it looks like Thread() may start before Threadcreate() returns.
SARG
Posts: 1756
Joined: May 27, 2005 7:15
Location: FRANCE

Re: Execution timing of a Sub launched as a thread

Post by SARG »

fxm wrote:So it looks like Thread() may start before Threadcreate() returns.
Sure it was not guaranteed as the 2 threads run simultaneously so it depends of various factors : processor multicore/multithreaded, processor very busy, how all other threads are handled.
However better have a proof of that. Thanks.

On Windows there is a parameter to not let the new thread running immediatly but that requires to execute an extra statement for starting it.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

Confirmation

Similarly, the test code from my first post, but without 'Sleep 1, 1' triggers after a while:

Code: Select all

Dim As Any Ptr ptid

Sub myThread (Byval p As Any Ptr)
    Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)
    If ptid = 0 Then
        Print Time; " the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made"
    End If
End Sub

Do
    ptid = 0
    ptid = Threadcreate(@myThread, @ptid)
    Threadwait(ptid)
Loop Until Inkey <> ""
13:03:11 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:04:10 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:04:32 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:06:16 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:07:01 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:07:05 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:07:21 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:09:05 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
I will add a sentence in the documentation.


[edit]
With the 'Sleep 1, 1' line, the fault occurrence obviously has a much longer period:

Code: Select all

Dim As Any Ptr ptid

Sub myThread (Byval p As Any Ptr)
    Dim As Any Ptr ptid = *Cast(Any Ptr Ptr, p)
    If ptid = 0 Then
        Print Time; " the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made"
    End If
End Sub

Print Time
Do
    ptid = 0
    ptid = Threadcreate(@myThread, @ptid)
    Threadwait(ptid)
    
    Sleep 1, 1
Loop Until Inkey <> ""
13:10:58
13:24:37 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
13:54:34 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
14:17:14 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
14:26:57 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
15:15:45 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
16:34:20 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
17:12:40 the assignment 'ptid = Threadcreate(@myThread, @ptid)' is not yet made
Last edited by fxm on Oct 20, 2020 17:59, edited 5 times in total.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Execution timing of a Sub launched as a thread

Post by fxm »

fxm wrote:I will add a sentence in the documentation.
Done:
- KeyPgThreadCreate → fxm [thread body may start executing before Threadcreate() returns]
- ProPgMtThreads → fxm [thread body may start executing before Threadcreate()/Threadcall() returns]
Post Reply