Question about threads and mutexes

General FreeBASIC programming questions.
Post Reply
Landeel
Posts: 777
Joined: Jan 25, 2007 10:32
Location: Brazil
Contact:

Question about threads and mutexes

Post by Landeel »

Code: Select all

dim shared as boolean finished=false
dim shared as string string1, string2
string1="1" : string2="2"

sub stringChange()
	do
		string2="'"+chr(128+rnd*64)+"'"
		swap string1, string2
	loop until finished
end sub

screen 13

dim shared as any ptr stringChange_ptr=procptr(stringChange)
dim shared as any ptr stringChange_thread : stringChange_thread=threadcreate(stringChange_ptr,0)

do
	locate 1,1 : print string1+"   "
	screensync()
	flip()
loop until multikey(1)

finished=true

threadwait stringChange_thread
This code is a much simpler version of something I'm working on.
I have 2 shared strings.
The secondary thread will modify 'string2' and swap it with 'string1'.
The main thread will only read from 'string1'.
The strings are never erased. If I understand this correctly, both strings will stay in memory while the program is running, and they will only swap addresses.
My question is: does this code need a mutex?
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question about threads and mutexes

Post by fxm »

It is safer to:
- Use a thread sub of signature: 'Sub(Byval As Any Ptr)'.
- Use a mutex to avoid multi-threading conflict between 'swap string1, string2' and 'print string1+" "', even if 'swap' only exchanges the string decriptors, because the descriptor structure corresponds to a Pointer + 2 Integers.

In your special case where the two strings have the same size (and are not reallocated), that may work, except for the starting (string size: 1 => 3).
I would not comment the use of 'screensync()' and 'flip()' !
Landeel
Posts: 777
Joined: Jan 25, 2007 10:32
Location: Brazil
Contact:

Re: Question about threads and mutexes

Post by Landeel »

fxm wrote:It is safer to:
- Use a thread sub of signature: 'Sub(Byval As Any Ptr)'.
- Use a mutex to avoid multi-threading conflict between 'swap string1, string2' and 'print string1+" "', even if 'swap' only exchanges the string decriptors, because the descriptor structure corresponds to a Pointer + 2 Integers.

In your special case where the two strings have the same size (and are not reallocated), that may work, except for the starting (string size: 1 => 3).
I would not comment the use of 'screensync()' and 'flip()' !
Thank you, fxm!

So, I may get into trouble if the strings change in size.

Does this look ok?

Code: Select all

dim shared as boolean finished=false
dim shared as string string1, string2
string1="1" : string2="2"

dim shared as any ptr stringChange_mutex : stringChange_mutex=mutexcreate()

sub stringChange(Byval dummy As Any Ptr=0)
	do
		string2="'"+chr(128+rnd*64)+"'"
		mutexlock stringChange_mutex
			swap string1, string2
		mutexunlock stringChange_mutex
	loop until finished
end sub

screen 13

dim shared as any ptr stringChange_ptr=procptr(stringChange)
dim shared as any ptr stringChange_thread : stringChange_thread=threadcreate(stringChange_ptr,0)

do
	locate 1,1
	mutexlock stringChange_mutex
		print string1+"   "
	mutexunlock stringChange_mutex
	screensync()
	flip()
loop until multikey(1)

finished=true

threadwait stringChange_thread
mutexdestroy(stringChange_mutex)

Anything wrong in using 'screensync' and 'flip', btw?
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question about threads and mutexes

Post by fxm »

dim shared as any ptr stringChange_ptr=procptr(stringChange)
dim shared as any ptr stringChange_thread : stringChange_thread=threadcreate(stringChange_ptr,0)

dim shared as any ptr stringChange_thread : stringChange_thread=threadcreate(@stringChange)
'' or:
dim shared as any ptr stringChange_thread : stringChange_thread=threadcreate(procptr(stringChange))


Or if you absolutely want to define a procedure pointer:
dim shared as sub(byval as any ptr) stringChange_ptr=procptr(stringChange)
dim shared as any ptr stringChange_thread : stringChange_thread=threadcreate(stringChange_ptr,0)
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question about threads and mutexes

Post by fxm »

Why do you want to protect from possible flickering?
- The window is never cleared.
- Always the same number of characters are written starting from the same location (1, 1).

Otherwise:
- 'screensynch()' is a process of thumb because the plotting/printing must fit inside the small interval between the frames tracing ('screenlock' ... 'screenunlock' is preferable).
- 'flip()' requires the declaration and use of 2 screen pages (a working page and a displayed page).
Landeel
Posts: 777
Joined: Jan 25, 2007 10:32
Location: Brazil
Contact:

Re: Question about threads and mutexes

Post by Landeel »

fxm wrote:Why do you want to protect from possible flickering?
- The window is never cleared.
- Always the same number of characters are written starting from the same location (1, 1).

Otherwise:
- 'screensynch()' is a process of thumb because the plotting/printing must fit inside the small interval between the frames tracing ('screenlock' ... 'screenunlock' is preferable).
- 'flip()' requires the declaration and use of 2 screen pages (a working page and a displayed page).
Heh, it's just something I have put together quickly.
In my actual program I'm using OpenGL, so I always use the FLIP.
And I only use SCREENSYNC when I need to limit the frame rate, but I usually just enable VSYNC using the OpenGL extensions.
adeyblue
Posts: 299
Joined: Nov 07, 2019 20:08

Re: Question about threads and mutexes

Post by adeyblue »

FB stores the length and content separately, so doing pretty much anything with a dynamic string requires at least two reads. And things that require multiple reads can be interrupted, so this can happen

string1 is 20 characters long
string 2 is 5 characters long

Thread 1
--- processes 'Print string1'
--- The FB internals read string1's length (20) and store it in a local variable
Thread 2 interrupts
--- the whole string swap is completed
--- string1 now has the 5 character data, and string 2 20
Thread 1 runs again
--- The FB internals now read string1's data of 5 characters... and then 15 more since it read a length of 20 before it was interrupted
--- Print completes

Now you have Hello@~@}{}:@;#;<>¬¬ on the screen, or a crash, instead of just Hello, or whatever was in string 1 when the Print started.

Even if swap only swaps the strings addresses rather than the full descriptors (I don't know which it does), the same thing applies.
'Print string1' copies the value of the current string1 pointer into Print to work with, since Print arguments are byval
the swap happens
your non-Print thread now happily modifies string2
- but string2 is the same pointer that may be still inside Print being read from, uh-oh

TL;DR
You fixed it
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question about threads and mutexes

Post by fxm »

My code syntax for your example:

Code: Select all

Type myData
    Dim As String string1="1"
    Dim As String string2="2"
    Dim As Boolean finished=False
    Dim As Any Ptr stringChange_thread
    Dim As Any Ptr stringChange_mutex
End Type

Sub stringChange(Byval p As Any Ptr)
    With *Cptr(myData Ptr, p)
        Do
            .string2="'"+Chr(128+Rnd*64)+"'"
            Mutexlock(.stringChange_mutex)
            Swap .string1, .string2
            Mutexunlock(.stringChange_mutex)
        Loop Until .finished
    End With
End Sub

Screen 13
Dim As myData md
With md
    .stringChange_mutex = Mutexcreate()
    .stringChange_thread = Threadcreate(@stringChange, @md)
    Do
        Locate 1,1
        Mutexlock(.stringChange_mutex)
        Print .string1+"   "
        Mutexunlock(.stringChange_mutex)
        Sleep 15, 1
    Loop Until Multikey(1)
    .finished = True
    Threadwait(.stringChange_thread)
    Mutexdestroy(.stringChange_mutex)
End With
Landeel
Posts: 777
Joined: Jan 25, 2007 10:32
Location: Brazil
Contact:

Re: Question about threads and mutexes

Post by Landeel »

Thanks for the explanation adeyblue! I got it.
Your version looks way more elegant, fxm.
Post Reply