{{fbdoc item="title" value="Critical Sections FAQ"}}----
The "**Critical Sections**" related questions in multi-threading.
{{anchor name="ProPgMtCriticalSectionsFAQtop"}}
=={{fbdoc item="section" value="'Critical sections' related questions"}}==
	**{{anchor name="ProPgMtCriticalSectionsFAQ1|1. When is it not mandatory to protect by a mutex one shared variable between several threads?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ2|2. What is the chronology of code execution of 2 critical sections (with a mutex locking and a conditional variable signaling) that compete between 2 threads?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ3|3. What happens if calling 'Condsignal()' or 'Condbroadcast()' without mutex locked?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ4|4. Why it is mandatory to put 'Condwait' within a 'While...Wend' loop for checking a Boolean predicate (set by other thread before activate 'Condsignal' or 'Condbroadcast', and reset after the 'Wend')?"}}**
		**{{anchor name="ProPgMtCriticalSectionsFAQ4.1|4.1. Is 'Condwait' (and 'Condsignal' or 'Condbroadcast') still useful when there is already a 'While' loop for checking a Boolean predicate set by other thread?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ5|5. How to implement a user input-line function fully thread-safe?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ6|6. How to use 'Screenlock' with multi-threading?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ7|7. How to use 'video paging (double buffering or page flipping)' with multi-threading?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ8|8. How to use the FB runtime library for multi-threaded applications (gfxlib2) with multi-threading?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ9|9. How to use console statements and keyboard inputs with multi-threading?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ10|10. Is it better to take precautions when using the keyword 'Sleep' in threads?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ11|11. Can all tools to handle multi-threading be encapsulated in a base Class (that the user extends with a derived Type for his own implementing)?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ12|12. Can we emulate a kind of TLS (Thread Local Storage) with FreeBASIC?"}}**
	**{{anchor name="ProPgMtCriticalSectionsFAQ13|13. Can we emulate a kind of thread pooling feature with FreeBASIC?"}}**
	
----



@@**===='Critical sections' related questions====**@@

{{anchor name="ProPgMtCriticalSectionsFAQ1"}}
{{fbdoc item="section" value="1. When is it not mandatory to protect by a mutex one shared variable between several threads?"}}
		When accessing to shared variables between several threads, all their accesses must be generally put inside blocks ##//Mutexlock...Mutexunlock//##, in all threads:
			**""-""** When the shared variable is only one simple predefined numeric type of ##//size <= sizeof(integer)//## (only one assembler instruction for access), the mutex use may be not mandatory.
			**""-""** But if this is for example one shared variable ##//""LongInt""//## with a win32 compilation, it is advised here to use a mutex (otherwise the reading phase by a thread may be interlaced with the writing phase of another thread).
			
		That is because to access a variable in memory (for reading or for writing), a processor uses its internal registers.
		A N-bit processor has N-bit registers but none greater:
			**""-""** So one only assembler instruction allows it to access a N-bit variable in memory.
			**""-""** At opposite, to access a 2N-bit variable, it must use 2 assembler instructions.
			**""-""** If between these two assembler instructions (for writing), another thread accesses this same variable (for reading), the got value may be incoherent (N-bit highest and N-bit lowest incoherent together).
			
		This behavior can be checked with a graphic program using two threads and a shared ##//""LongInt""//## (64-bit) without mutex:
			**""-""** by compiling in 32-bit, many read values are incoherent.
			**""-""** by compiling in 64-bit, no read value is incoherent.
			
		Compile the below test program:
			**""-""** in 32-bit => Many erroneous points not on the circle but anywhere in the square containing the circle. If you uncomment the four lines 37/39/58/60 to activate the mutex, then all the got points are now on the circle only.
			**""-""** in 64-bit => All points are valid, on the circle only, even if the mutex is not activated.
			{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq1.bas"}}%%(freebasic)
'   - The "user-defined thread" computes the points coordinates on a circle,
'     and write those in a LongInt (32-bit & 32-bit = 64-bit)
'   - The "main thread" plots the points from the LongInt value.
'
'   Behavior:
'      - The first point must be pre-determined.
'      - Nothing prevents that a same calculated point could be plotted several times
'      (depends on execution times of the loops between main thread and user thread).
'      - Nothing prevents that a calculated point could be not plotted
'      (same remark on the loop times).
'
'   Remark:
'      Voluntarily, there is no Sleep in the loop of each thread (normally strongly discouraged),
'      but this is just in this special case to amplify the behavior effects to observe.


Union Point2D
	Dim As Longint xy
	Type
		Dim As Long y
		Dim As Long x
	End Type
End Union

Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Integer quit

Sub Thread (ByVal param As Any Ptr)
	Const pi As Single = 4 * Atn(1)
	Dim As Point2D Ptr p = param
	Do
		Dim As Point2D P2D0
		Dim As Single teta = 2 * pi * Rnd
		P2D0.x = 320 + 200 * Cos(teta)
		P2D0.y = 240 + 200 * Sin(teta)
'        Mutexlock(mutex)
		p->xy = P2D0.xy
'        Mutexunlock(mutex)
'        Sleep 5, 1
	Loop Until quit = 1
End Sub


Screen 12

Dim As Point2D P2D
P2D.x = 520
P2D.y = 240

mutex = MutexCreate
handle = ThreadCreate(@Thread, @P2D)

Dim As Integer c

Do
	Dim As Point2D P2D0
'    Mutexlock(mutex)
	P2D0.xy = P2D.xy
'    Mutexunlock(mutex)
	PSet (P2D0.x, P2D0.y), c
	c = (c Mod 15) + 1
'    Sleep 5, 1
Loop Until Inkey <> ""
 
quit = 1
ThreadWait(handle)
Mutexdestroy(mutex)
			%%
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ2"}}
{{fbdoc item="section" value="2. What is the chronology of code execution of 2 critical sections (with a mutex locking and a conditional variable signaling) that compete between 2 threads?"}}
		Chronology for one thread signaling which occurs:
			""a) ""while another thread is waiting (within a While loop on predicate),
			""b) ""before another thread is waiting (within a While loop on predicate).
			{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq2.bas"}}%%(freebasic)
#define while_loop_on_predicate

Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond
Dim As Integer sleep0
Dim As Integer sleep1
#ifdef while_loop_on_predicate
Dim Shared As Integer ready
#endif


Sub Thread1 (ByVal param As Any Ptr)
	Sleep *Cast(Integer Ptr, param), 1
	Mutexlock(mutex)
	Color 11 : Print "        Thread#1 locks the mutex"
	Color 11 : Print "        Thread#1 executes code with exclusion"
	#ifdef while_loop_on_predicate
	ready = 1
	#endif
	Color 11 : Print "        Thread#1 is signaling"
	CondSignal(cond)
	Color 11 : Print "        Thread#1 executes post-code with exclusion"
	Color 11 : Print "        Thread#1 unlocks the mutex"
	Mutexunlock(mutex)
End Sub

Sub Thread0 (ByVal param As Any Ptr)
	Sleep *Cast(Integer Ptr, param), 1
	Mutexlock(mutex)
	Color 10 : Print "    Thread#0 locks the mutex"
	Color 10 : Print "    Thread#0 executes pre-code with exclusion"
	#ifdef while_loop_on_predicate
	While ready <> 1
	#endif
		Color 10 : Print "    Thread#0 is waiting"
		CondWait(cond, mutex)
		Color 10 : Print "    Thread#0 is waked"
	#ifdef while_loop_on_predicate
	Wend
	#endif
	Color 10 : Print "    Thread#0 executes code with exclusion"
	#ifdef while_loop_on_predicate
	ready = 0
	#endif
	Color 10 : Print "    Thread#0 unlocks the mutex"
	Mutexunlock(mutex)
End Sub


mutex = MutexCreate
cond = CondCreate

sleep0 = 0
sleep1 = 1000
Color 7 : Print "Chronology for Thread#1 signaling while Thread#0 is waiting:"
handle = ThreadCreate(@Thread1, @sleep1)
Thread0(@sleep0)
ThreadWait(handle)
Color 7 : Print "Thread#1 finished": Print
Sleep 1000, 1

sleep0 = 1000
sleep1 = 0
Color 7 : Print "Chronology for Thread#1 signaling before Thread#0 is waiting:"
handle = ThreadCreate(@Thread1, @sleep1)
Thread0(@sleep0)
ThreadWait(handle)
Color 7 : Print "Thread#1 finished": Print


Mutexdestroy(mutex)
CondDestroy(cond)
Sleep
			%%
			Output part a - Chronology for Thread#1 signaling while Thread#0 is waiting:
				%%
Chronology for Thread#1 signaling while Thread#0 is waiting:
	Thread#0 locks the mutex
	Thread#0 executes pre-code with exclusion
	Thread#0 is waiting
		Thread#1 locks the mutex
		Thread#1 executes code with exclusion
		Thread#1 is signaling
		Thread#1 executes post-code with exclusion
		Thread#1 unlocks the mutex
	Thread#0 is waked
	Thread#0 executes code with exclusion
	Thread#0 unlocks the mutex
Thread#1 finished
				%%
			Output part b - Chronology for Thread#1 signaling before Thread#0 is waiting:
				%%
Chronology for Thread#1 signaling before Thread#0 is waiting:
		Thread#1 locks the mutex
		Thread#1 executes code with exclusion
		Thread#1 is signaling
		Thread#1 executes post-code with exclusion
		Thread#1 unlocks the mutex
	Thread#0 locks the mutex
	Thread#0 executes pre-code with exclusion
	Thread#0 executes code with exclusion
	Thread#0 unlocks the mutex
Thread#1 finished	
				%%
			**Note:** If ##//""CondWait""//## is not within a ##//While//## loop on predicate (by putting in comment the first line of above program), one can check in the second case (thread#1 signaling before thread#0 waiting), that thread#0 remains blocked in its waiting phase (Ctrl-C to quit).
			
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ3"}}
{{fbdoc item="section" value="3. What happens if calling 'Condsignal()' or 'Condbroadcast()' without mutex locked?"}}
		Referring to the example 2 on the ##[[ProPgMtCriticalSections|Critical Sections]]##, one takes this opportunity to recall that:
			**""-""** The mutex must always be also locked while executing ##//""Condsignal()""//## or ##//""Condbroadcast()""//## to wake up a thread (it may be unlocked but only after ##//""Condsignal()""//## or ##//""Condbroadcast()""//##).
			**""-""** If the mutex is not locked (or even if the mutex is unlocked only just before executing ##//""Condsignal()""//## or ##//""Condbroadcast()""//##), the behavior may become unpredictable (it may work or not, depending on the threads configuration and execution real time).
			
		In the example 2 on the ##[[ProPgMtCriticalSections|Critical Sections]]## "Synchronous method example using a condwait then a condbroadcast (and a mutex) for all threads":
			**""-""** If one at least ##//Mutexunlock()//## is moved just before its ##//""Condbroadcast()""//##, the program hangs very quickly.
			**""-""** Although some users certify that the mutex can always be unlocked just before ##//""Condsignal()""//## or ##//""Condbroadcast()""//##, and others more cautious assert that one can do it only for a ##//""Condbroadcast()""//##, experiment shows the opposite!
			
		The general rule is that:
			**""-""** The condition must not be signaled (by ##//""Condsignal()""//## or ##//""Condbroadcast()""//##) between the time a thread locks the mutex and the time it waits on the condition variable (##//""CondWait()""//##), otherwise it seems that it may damage the waiting queue of threads on that condition variable.
			**""-""** Thus to avoid that and follow this rule, it is necessary that the mutex remains locked when the condition is signaled.
			
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ4"}}
{{fbdoc item="section" value="4. Why it is mandatory to put 'Condwait' within a 'While...Wend' loop for checking a Boolean predicate (set by other thread before activate 'Condsignal' or 'Condbroadcast', and reset after the 'Wend')?"}}
		**##//While predicate <> True
			Condwait(conditionalid, mutexid)
		Wend
		predicate = False//##**
		
		In all documentations, it is highly advisable to do so, mainly justified to fight against eventual spurious wake-ups.
		
		This is probably true, but it is also advisable to do so to avoid to loose a ##//""CondSignal""//## (or ##//""CondBroadcast""//##) if it is prematurely activated while the receiving thread is not yet waiting on ##//""CondWait""//## (the signal is lost forever):
			**""-""** In that case, the receiving thread has even not yet locked the mutex before that ##//""CondSignal""//## (or ##//""CondBroadcast""//##) is activated.
			**""-""** So the predicate will already true before the receiving thread reaches the 'While...Wend' loop, inducing that ##//""CondWait""//## is downright skipped, so avoiding a definitive blocking phenomenon.
			
		Let two threads (thread #0 in main program, thread #1 in a user procedure, each that prints its number in a loop), having about the same execution time, and each one synchronizing the other in order to well interlace their numbers (by using one mutex, two condition variables and ##//""CondSignal""//##/##//""CondWait""//##):
			- Without a 'While...Wend' loop on predicate, the program hangs quickly (Ctrl-C to quit):
					%%
'            Thread#0                           XOR + <==>                       Thread#1
'   .....                                                             .....
'   MutexLock(mut) <-------------------------.             .----------> MutexLock(mut)
'   ( atomic_mutex_unlock(mut) ) ------.     |             |            Do_something_with_exclusion
'   CondWait(cond#1, mut) <----------- | --- | ----------- | ---------- CondSignal(cond#1)
'   ( atomic_mutex_re-lock(mut) ) <--- | ----'----.        |     .----- ( atomic_mutex_unlock(mut) )
'   Do_something_with_exclusion        |     .--- | ------ | --- | ---> CondWait(cond#2, mut)
'   CondSignal(cond#2) --------------- | ----'    |    .---'---- | ---> ( atomic_mutex_re-lock(mut) )
'   Do_something_with_exclusion        |     .--- | ---'         |      Do_something_with_exclusion
'   MutexUnlock(mut) ------------------'-----'    '--------------'----- MutexUnlock(mut)
'   .....                                                               .....
'
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
					%%
					%%(freebasic)
Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond1
Dim Shared As Any Ptr cond2
Dim Shared As Integer quit

Sub Thread (ByVal param As Any Ptr)
	Do
		Mutexlock(mutex)
		Print "1";
		CondSignal(cond1)
		CondWait(cond2, mutex)
		If quit = 1 Then
			Mutexunlock(mutex)
			Exit DO
		End If
		Mutexunlock(mutex)
		Sleep 1, 1
	Loop
End Sub


mutex = MutexCreate
cond1 = CondCreate
cond2 = CondCreate
handle = ThreadCreate(@Thread)

Do
	Mutexlock(mutex)
	CondWait(cond1, mutex)
	Print "0";
	CondSignal(cond2)
	If Inkey <> "" Then
		quit = 1
		Mutexunlock(mutex)
		Exit Do
	End If
	Mutexunlock(mutex)
	Sleep 1, 1
Loop
 
ThreadWait(handle)
Mutexdestroy(mutex)
CondDestroy(cond1)
CondDestroy(cond2)
Print

Sleep
					%%
			- With a 'While...Wend' loop on predicate around each ##//""CondWait""//##, no blocking phenomenon:
					%%
'            Thread#0                                 XOR + <==>                            Thread#1
'   .....                                                                          .....
'   MutexLock(mut) <-----------------------------.                          .----> MutexLock(mut)
'   While bool#1 <> true <---------------------- | --------.                |      Do_something_with_exclusion
'       ( atomic_mutex_unlock(mut) ) ------.     |         '--------------- | ---- bool#1 = true
'       CondWait(cond#1, mut) <----------- | --- | ------------------------ | ---- CondSignal(cond#1)
'       ( atomic_mutex_re-lock(mut) ) <--- | ----'----.    .--------------- | ---> While bool#2 <> true
'   Wend                                   |          |    |          .---- | -------- ( atomic_mutex_unlock(mut) )
'   bool#1 = false                .------- | -------- | ---'  .------ | --- |--------> CondWait(cond#2, mut)
'   Do_something_with_exclusion   |   .--- | -------- | ------'  .--- | ----'--------> ( atomic_mutex_re-lock(mut) )
'   bool#2 = true ----------------'   |    |     .--- | ---------'    |            Wend
'   CondSignal(cond#2) ---------------'    |     |    |               |            bool#2 = false
'   Do_something_with_exclusion            |     |    |               |            Do_something_with_exclusion
'   MutexUnlock(mut) ----------------------'-----'    '---------------'----------- MutexUnlock(mut)
'   .....                                                                          .....
'
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
					%%
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq4.bas"}}%%(freebasic)
Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond1
Dim Shared As Any Ptr cond2
Dim Shared As Integer new1
Dim Shared As Integer new2
Dim Shared As Integer quit

Sub Thread (ByVal param As Any Ptr)
	Do
		Mutexlock(mutex)
		Print "1";
		new1 = 1
		CondSignal(cond1)
		While new2 <> 1
			CondWait(cond2, mutex)
		Wend
		new2 = 0
		If quit = 1 Then
			Mutexunlock(mutex)
			Exit DO
		End If
		Mutexunlock(mutex)
		Sleep 1, 1
	Loop
End Sub


mutex = MutexCreate
cond1 = CondCreate
cond2 = CondCreate
handle = ThreadCreate(@Thread)

Do
	Mutexlock(mutex)
	While new1 <> 1
		CondWait(cond1, mutex)
	Wend
	new1 = 0
	Print "0";
	new2 = 1
	CondSignal(cond2)
	If Inkey <> "" Then
		quit = 1
		Mutexunlock(mutex)
		Exit Do
	End If
	Mutexunlock(mutex)
	Sleep 1, 1
Loop
 
ThreadWait(handle)
Mutexdestroy(mutex)
CondDestroy(cond1)
CondDestroy(cond2)
Print

Sleep
					%%{{anchor name="ProPgMtCriticalSectionsFAQ4.1"}}
	{{fbdoc item="section" value="4.1. Is 'Condwait' (and 'Condsignal' or 'Condbroadcast') still useful when there is already a 'While...Wend' loop for checking a Boolean predicate set by other thread?"}}
		##//(another question induced by the previous one)//##
			**""-""** The recommended structure is as follows:
				%%
'  Principle of mutual exclusion + CONDWAIT in a While...Wend loop with predicate check, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      .......
'      While booleanT <> True <--------------------- from booleanT = True
'          ( atomic_mutex_unlock(mutexID) ) -------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
'          CONDWAIT(conditionalID, mutexID) <------- from CONDSIGNAL(conditionalID)
'          ( atomic_mutex_re-lock(mutexID) ) <------ from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
				%%
				%%
'  Principle of mutual exclusion + CONDSIGNAL with predicate check, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      .......
'      booleanOT = True ---------------------------> to While booleanOT <> True
'      CONDSIGNAL(conditionalID) ------------------> to CONDWAIT(conditionalID, mutexID)
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
				%%
			**""-""** If ""'CondWait'"" is not used, it is mandatory to put instead a 'Sleep x, 1' instruction in the 'While...Wend' loop on the Boolean flag, in order to release time-slice when looping (in addition, this 'Sleep x, 1' must be put inside a ['Mutexunlock'...'Mutexlock'] block to release another thread):
				%%
'  Principle of mutual exclusion + SLEEP in a While...Wend loop with predicate check, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from MUTEXUNLOCK(mutexID)
'      .......
'      While booleanT <> True <--------------------- from booleanT = True
'          MUTEXUNLOCK(mutexID) -------------------> to MUTEXLOCK(mutexID)
'          SLEEP(tempo, 1)
'          MUTEXLOCK(mutexID) <--------------------- from MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)
				%%
				%%
'  Principle of mutual exclusion + predicate check only, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from MUTEXUNLOCK(mutexID)
'      .......
'      booleanOT = True ---------------------------> to While booleanOT <> True
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)
				%%
			During ""'CondWait'"", the thread execution is suspended and does not consume any CPU time until the condition variable is signaled.
			But if 'Sleep x, 1' is put instead, the waiting time is predetermined and not self adaptive like that of ""'CondWait'"".
			
			**=>** ""'CondWait'"" is useful to optimize the execution time.
			
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ5"}}
{{fbdoc item="section" value="5. How to implement a user input-line function fully thread-safe?"}}
		The ##//Input//## keyword may be not thread-safe, when another thread must also access to input/output resource:
			**""-""** When executing the ##//Input//## statement, the other running threads must not change the position of the text cursor, which prohibits instructions such as ##//Locate//##, ##//Print//##, ...
			**""-""** Moreover, one cannot enclosed the ##//Input//## keyword inside a mutex locking (as we can do it for the ##//Inkey//## keyword), because while the inputting line would be not completed and validated, the other threads that want to also access to input/output would be fully blocked (waiting for mutex unlocking).
			
		Thread-safe input-line function (versus input/output resource):
			Input position, prompt message, sleeping time, line-blanking command, mutex pointer can be passed to the following ##//threadInput()//## function that simulates a simplified input function, but thread-safe, by using a looping around the ##//Inkey//## keyword (all input/output keywords must be enclosed inside a mutex locking block, and the cursor position must be restored at each mutex locking block ending):
				%%(freebasic)
Function threadInput (ByVal row As Integer, ByVal column As Integer, ByRef prompt As String = "", _
					  ByVal sleeptime As Integer = 15, ByVal blank As Integer = 0, ByVal mutex As Any Ptr = 0 _
					  ) As String
	Dim As String inputchr
	Dim As String inputline
	Dim As Integer cursor
	Dim As Integer cursor0
	Dim As Integer r
	Dim As Integer c

 
	MutexLock(mutex)
	r = CsrLin()
	c = Pos()
	Locate row, column
	Print prompt & " _";
	cursor0 = Pos() - 1
	Locate r, c
	MutexUnlock(mutex)

	Do
		MutexLock(mutex)
		r = CsrLin()
		c = Pos()
		inputchr = Inkey
		If inputchr <> "" Then
			If inputchr >= Chr(32) And inputchr < Chr(255) Then
				inputline = Left(inputline, cursor) & inputchr & Mid(inputline, cursor + 1)
				cursor += 1
			ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp
				cursor -= 1
				inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
			Elseif inputchr = Chr(255) & "S" And Cursor < Len(inputline) Then     'Del
				inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
			Elseif inputchr = Chr(255) + "M" AND Cursor < LEN(inputline) THEN     'Right
				Cursor += 1
			Elseif inputchr = Chr(255) + "K" AND Cursor > 0 THEN                  'Left
				Cursor -= 1
			End If
			If inputchr = Chr(27) Then                                            'Esc
				Locate row, cursor0
				Print Space(Len(inputline) + 1);
				inputline = ""
				cursor = 0
			End If
			Locate row, cursor0
			Print Left(inputline, cursor) & Chr(95) & Mid(inputline, cursor + 1) & " ";
		End If
		Locate r, c
		MutexUnlock(mutex)
		Sleep sleeptime, 1
	Loop Until inputchr = Chr(13)

	If blank <> 0 Then
		MutexLock(mutex)
		r = CsrLin()
		c = Pos()
		Locate row, cursor0
		Print Space(Len(inputline) + 1);
		Locate r, c
		MutexUnlock(mutex)
	End If

	Return inputline
End Function
				%%
			- From the example 1 on the ##[[ProPgMtCriticalSections|Critical Sections]]## page "Asynchronous method example using a mutex for all threads", now the running multi-threading code is waiting for the "quit" command in order to exit the program:
					%%
'   User thread algorithm:
'
'     Do
'     |  Mutexlock
'     |  .....
'     |  Critical section of code
'     |  .....
'     |  Mutexunlock
'     |  Sleep my_tempo, 1
'     Loop Until quit = true
'
'   There is no any advantage or disadvantage between threads for running their critical sections.
					%%
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq5.bas"}}%%(freebasic)
Function threadInput (ByVal row As Integer, ByVal column As Integer, ByRef prompt As String = "", _
					  ByVal sleeptime As Integer = 15, ByVal blank As Integer = 0, ByVal mutex As Any Ptr = 0 _
					  ) As String
	Dim As String inputchr
	Dim As String inputline
	Dim As Integer cursor
	Dim As Integer cursor0
	Dim As Integer r
	Dim As Integer c

 
	MutexLock(mutex)
	r = CsrLin()
	c = Pos()
	Locate row, column
	Print prompt & " _";
	cursor0 = Pos() - 1
	Locate r, c
	MutexUnlock(mutex)

	Do
		MutexLock(mutex)
		r = CsrLin()
		c = Pos()
		inputchr = Inkey
		If inputchr <> "" Then
			If inputchr >= Chr(32) And inputchr < Chr(255) Then
				inputline = Left(inputline, cursor) & inputchr & Mid(inputline, cursor + 1)
				cursor += 1
			ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp
				cursor -= 1
				inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
			Elseif inputchr = Chr(255) & "S" And Cursor < Len(inputline) Then     'Del
				inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
			Elseif inputchr = Chr(255) + "M" AND Cursor < LEN(inputline) THEN     'Right
				Cursor += 1
			Elseif inputchr = Chr(255) + "K" AND Cursor > 0 THEN                  'Left
				Cursor -= 1
			End If
			If inputchr = Chr(27) Then                                            'Esc
				Locate row, cursor0
				Print Space(Len(inputline) + 1);
				inputline = ""
				cursor = 0
			End If
			Locate row, cursor0
			Print Left(inputline, cursor) & Chr(95) & Mid(inputline, cursor + 1) & " ";
		End If
		Locate r, c
		MutexUnlock(mutex)
		Sleep sleeptime, 1
	Loop Until inputchr = Chr(13)

	If blank <> 0 Then
		MutexLock(mutex)
		r = CsrLin()
		c = Pos()
		Locate row, cursor0
		Print Space(Len(inputline) + 1);
		Locate r, c
		MutexUnlock(mutex)
	End If

	Return inputline
End Function

'------------------------------------------------------------------------------

Type UDT
	Dim As Integer number
	Dim As Integer tempo
	Dim As Any Ptr pThread
	Dim As ULongInt count
	Static As Any Ptr pMutex
	Static As Integer numberMax
	Static As Integer quit
End Type
Dim As Any Ptr UDT.pMutex
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Sub Counter (ByVal pt As UDT Ptr)
	With *pt
		Locate .number, .number, 0
		Sleep 5, 1
		.count += 1
		Print .count;
	End With
End Sub

Sub Thread (ByVal p As Any Ptr)
	Dim As Integer myquit
	Dim As UDT Ptr pUDT = p
	With *pUDT
		Do
			MutexLock(.pMutex)
			Counter(pUDT)
			myquit = .quit
			MutexUnlock(.pMutex)
			Sleep .tempo, 1
		Loop Until myquit = 1
	End With
End Sub


Screen 12
UDT.numberMax = 6

Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
	u(I).number = i
	u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I
UDT.pMutex = MutexCreate

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
	u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Do
Loop Until LCase(threadInput(8, 1, """quit"" for exit?", 10, 1, UDT.pMutex)) = "quit"

UDT.quit = 1

For I As Integer = 1 To UDT.numberMax
	ThreadWait(u(I).pThread)
Next I
t = Timer - t

MutexDestroy(UDT.pMutex)
Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
	c += u(I).count
Next I
Locate UDT.numberMax + 4, 1
Print CULngInt(c / t) & " increments per second"

Sleep
					%%
		**Note:**
			Otherwise, by using only graphics keywords (using the only position of the graphic cursor) as ##//Line//##, ##//Draw String//##, ##//Put//## in the thread, induces a thread-safe procedure that is compatible with the ##//Line Input//## keyword in the main code with no mutex:
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq5bis.bas"}}%%(freebasic)
Type UDT
	Dim As Integer number
	Dim As Integer tempo
	Dim As Any Ptr pThread
	Dim As ULongInt count
	Dim As Any Ptr img
	Static As Integer numberMax
	Static As Integer quit
End Type
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Const As String prompt = "Enter ""quit"" for exit"
Dim As String s

Sub Counter (ByVal pt As UDT Ptr)  ' for a graphic character size 8x16
	With *pt
		Line .img, (0, 0)-(20 * 8 - 1, 16 - 1), 0, BF            ' clearing the image buffer
		Sleep 5, 1
		.count += 1
		Draw String .img, (0, 0), Str(.count)                    ' drawing in the image buffer
		Put ((.number - 1) * 8, (.number - 1) * 16), .img, Pset  ' copying the image buffer to screen
	End With
End Sub

Sub Thread (ByVal p As Any Ptr)    ' for a graphic character size 8x16
	Dim As UDT Ptr pUDT = p
	With *pUDT
		.img = ImageCreate(20 * 8, 16)  ' using an image buffer to avoid flickering
		Do
			Counter(pUDT)
			Sleep .tempo, 1
		Loop Until .quit = 1
		ImageDestroy .img  ' destroying the image buffer
	End With
End Sub


Screen 12
UDT.numberMax = 6

Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
	u(I).number = i
	u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
	u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Do
	Locate 8, 1, 0
	Line Input; prompt; s
	Locate , Len(prompt) + 3
	Print Space(Len(s));
Loop Until LCase(s) = "quit"
UDT.quit = 1

For I As Integer = 1 To UDT.numberMax
	ThreadWait(u(I).pThread)
Next I
t = Timer - t

Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
	c += u(I).count
Next I
Locate UDT.numberMax + 4, 1
Print CULngInt(c / t) & " increments per second"

Sleep
				%%
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ6"}}
{{fbdoc item="section" value="6. How to use 'Screenlock' with multi-threading?"}}
		**""-""** ##//Screenlock...Scrennunlock//## blocks are not compatible with multi-threading (otherwise, the program hangs). This is why a mutex block must be used around each such block to ensure the mutual exclusion.
		**""-""** The input keywords (like for keyboard, mouse) cannot be safely run when the screen is locked, therefore a such keyword must be outside of any ##//Screenlock...Screenunlock//## block, so outside any ##//Screenlock...Screenunlock//## block in its own thread, and protected of all ##//Screenlock...Screenunlock//## blocks of other threads by a mutex block. Therefore, ##//Getkey//## and ##//Input//##, the statements that wait for keypress or line input are unusable, but ##//Inkey//## that does not wait can work.
		
		By applying some rules scrupulously, one can use ##//Screenlock//##/##//Screenunlock//## inside the threads.
		Principle of coding for all threads including the main code (main thread):
			%%
Do
	' instructions without display (printing/drawing, ...) neither input (input/inkey/mouse getting, ...)
	MutexLock(m)
	Screenlock
	' instructions with only display (printing/drawing, ...)
	Screenunlock
	' instructions with only input without waiting (inkey/mouse getting, ...)
	MutexUnlock(m)
	Sleep tempo, 1
Loop Until condition
			%%
		- For example, it is mandatory to use one ##//Mutexlock...Mutexunlock//## block around each ##//Screenlock...Screenunlock//## block, and one other around the ##//Inkey//## instruction which itself must always be outside of any ##//Screenlock...Screenunlock//## bloc:
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq6.bas"}}%%(freebasic)
Type ThreadUDT
	Dim handle As Any Ptr
	Static sync As Any Ptr
	Static quit As Byte
End Type
Dim ThreadUDT.sync As Any Ptr
Dim ThreadUDT.quit As Byte

Function ClockTime () As String
	Return Time
End Function

Function Counter () As Integer
	Static C As Integer
	C = (C + 1) MOD 1000000
	Return C
End Function

Sub ProcedureThread (Byval param As Any Ptr)
	With *Cast(ThreadUDT Ptr, param)
		Do
			MutexLock(.sync)
			Screenlock
			Line (544, 0)-(639, 49), 0, BF  'clear the print area
			Sleep 100, 1
			Locate 2, 71
			Print ClockTime();
			Screenunlock
			MutexUnlock(.sync)
			Sleep 100, 1
		Loop Until .quit = 1
	End With
End Sub

Screen 12
Locate 30, 2
Print "<q/Q> : quit";

Dim TTptr As ThreadUDT Ptr = New ThreadUDT
ThreadUDT.sync = MutexCreate
TTptr->handle = ThreadCreate(@ProcedureThread, TTptr)

Dim As String s
Do
	MutexLock(ThreadUDT.sync)
	Screenlock
	Line (296, 208)-(376, 256), 0, BF  'clear the print area
	Sleep 100, 1
	Locate 15,40
	Print Using "######"; Counter();
	Screenunlock
	s = Inkey
	MutexUnlock(ThreadUDT.sync)
	Sleep 100, 1
Loop Until Lcase(s) = "q"
 
ThreadUDT.quit = 1
ThreadWait(TTptr->handle)
MutexDestroy(ThreadUDT.sync)
Delete TTptr
				%%Note: The ##//Sleep x, 1//## keyword just after the 'clear the print area' lines is only here to highlight the flickering if no screen locking is used.
				
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ7"}}
{{fbdoc item="section" value="7. How to use 'video paging (double buffering or page flipping)' with multi-threading?"}}
		Instead of "screen locking" (see the above paragraph), "video paging (double buffering or page flipping)" can more simply be used with multi-threading, but be careful that many states in the gfxlib2 are thread-dependent like ##//Screenset//## (and also ##//View//## settings, graphic cursor position, graphic colors, ...).
		Therefore, the setting for the working page and the visible page must always be controlled in each thread code which want to work with a multi-video page configuration.
		
		- Example for a double buffering method (at each step, each thread needs to update the working page and copy it to the visible page, from within a mutual exclusion mutex code block):
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq7-1.bas"}}%%(freebasic)
Type ThreadUDT
	Dim handle As Any Ptr
	Static sync As Any Ptr
	Static quit As Byte
End Type
Dim ThreadUDT.sync As Any Ptr
Dim ThreadUDT.quit As Byte

Function ClockTime () As String
	Return Time
End Function

Function Counter () As Integer
	Static C As Integer
	C = (C + 1) MOD 1000000
	Return C
End Function

Sub ProcedureThread (Byval param As Any Ptr)
	Screenset 1, 0  '' setting to define in each thread
	With *Cast(ThreadUDT Ptr, param)
		Do
			MutexLock(.sync)
			Line (544, 0)-(639, 49), 0, BF  '' clear the print area
			Sleep 100, 1
			Locate 2, 71
			Print ClockTime();
			Screencopy
			MutexUnlock(.sync)
			Sleep 100, 1
		Loop Until .quit = 1
	End With
End Sub

Screen 12, , 2
Screenset 1, 0  '' setting to define in each thread
Locate 30, 2
Print "<q/Q> : quit";
Screencopy

Dim TTptr As ThreadUDT Ptr = New ThreadUDT
ThreadUDT.sync = MutexCreate
TTptr->handle = ThreadCreate(@ProcedureThread, TTptr)

Dim s As String
Do
	MutexLock(ThreadUDT.sync)
	Line (296, 208)-(376, 256), 0, BF  '' clear the print area
	Sleep 100, 1
	Locate 15,40
	Print Using "######"; Counter();
	Screencopy
	s = Inkey
	MutexUnlock(ThreadUDT.sync)
	Sleep 100, 1
Loop Until Lcase(s) = "q"
 
ThreadUDT.quit = 1
ThreadWait(TTptr->handle)
MutexDestroy(ThreadUDT.sync)
Delete TTptr
				%%Note: The ##//Sleep x, 1//## keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used.
				
		- Example for a two page flipping method (at each step, each thread needs to update and flip, from within the same exclusion mutex code block, the two screen pages):
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq7-2.bas"}}%%(freebasic)
Type ThreadUDT
	Dim handle As Any Ptr
	Static sync As Any Ptr
	Static quit As Byte
End Type
Dim ThreadUDT.sync As Any Ptr
Dim ThreadUDT.quit As Byte

Function ClockTime () As String
	Return Time
End Function

Function Counter () As Integer
	Static C As Integer
	C = (C + 1) MOD 1000000
	Return C
End Function

Sub ProcedureThread (Byval param As Any Ptr)
	Dim p0 As Integer = 0
	Dim p1 As Integer = 1
	Screenset 1, 0  '' setting to define in each thread
	With *Cast(ThreadUDT Ptr, param)
		Do
			MutexLock(.sync)
			Dim s As String = ClockTime()
			For I As Integer = 1 To 2  '' updating the two screen pages
				Line (544, 0)-(639, 49), 0, BF  '' clear the print area
				Sleep 100, 1
				Locate 2, 71
				Print s;
				Screenset p0, p1
				Swap p0, p1
			Next I
			MutexUnlock(.sync)
			Sleep 100, 1
		Loop Until .quit = 1
	End With
End Sub

Screen 12, , 2
Dim p0 As Integer = 0
Dim p1 As Integer = 1
Screenset 1, 0  '' setting to define in each thread
For I As Integer = 1 To 2  '' updating the two screen pages
	Locate 30, 2
	Print "<q/Q> : quit";
	Screenset p0, p1
	Swap p0, p1
Next I

Dim TTptr As ThreadUDT Ptr = New ThreadUDT
ThreadUDT.sync = MutexCreate
TTptr->handle = ThreadCreate(@ProcedureThread, TTptr)

Dim s As String
Do
	MutexLock(ThreadUDT.sync)
	Dim C As Integer = Counter()
	For I As Integer = 1 To 2  '' updating the two screen pages
		Line (296, 208)-(376, 256), 0, BF  '' clear the print area
		Sleep 100, 1
		Locate 15,40
		Print Using "######"; c;
		Screenset p0, p1
		Swap p0, p1
	Next I
	s = Inkey
	MutexUnlock(ThreadUDT.sync)
	Sleep 100, 1
Loop Until Lcase(s) = "q"
 
ThreadUDT.quit = 1
ThreadWait(TTptr->handle)
MutexDestroy(ThreadUDT.sync)
Delete TTptr
				%%Note: The ##//Sleep x, 1//## keyword just after the 'clear the print area' lines is only here to highlight the flickering if no two page flipping is used.
				
		**Note:** In these two examples, a mutual exclusion mutex code block is mandatory in the two threads, not only because of using console statements + ##//Inkey//##, but around also the graphics statements + ##//Screencopy//## only because of using double buffering method (without anti-flickering process, the graphics statements could be outside the exclusion mutex code block).
		
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ8"}}
{{fbdoc item="section" value="8. How to use the FB runtime library for multi-threaded applications (gfxlib2) with multi-threading?"}}
		The source code of gfxlib2 uses TLS (Thread Local Storage) to store many states, so many things are thread-specific.
		Since gfxlib2 is thread-safe, mutual mutex exclusion between threads is not necessary for the graphics statements themselves (including ##//Draw String//##).
		In contrast, console statements such as ##//Locate//##, ##//Print//##, ... are not thread-safe as previously mentioned (for example, text cursor position is common to all threads).
		
		- Simple example showing that graphic states (such as graphic cursor position, graphic colors) are thread-dependent:
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq8-1.bas"}}%%(freebasic)
Screen 12

Sub thread(Byval p As Any Ptr)
	Color 10
	Pset(150, 10)
	For I As Integer = 1 To 40
		Line -Step(10, 10)
		Sleep 150, 1
	Next I
	Draw String Step (-40, 10), "user thread"
End Sub

Dim As Any Ptr p = Threadcreate(@thread)

Color 14
Pset(10, 100)
For I As Integer = 1 To 24
	Line -Step(10, 10)
	Sleep 250, 1
Next I
Draw String Step (-40, 10), "main thread"

Threadwait(p)

Color 15
Locate 4, 2
Print "Any key for exit"

Sleep
				%%
		- Example showing that graphics statements (such as Line and Draw String and Screencopy) in a thread can compete with console statements (such as Inkey) in another thread, without using any exclusion (by mutex):
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq8-2.bas"}}%%(freebasic)
#include "vbcompat.bi"

Screen 12, , 2
Screenset 1, 0   
Color 0, 7
Cls

Dim Shared terminate As Integer = 0

Sub thread (byval param As Any Ptr)   
	Screenset 1, 0
	Do
		Line (16, 432)-Step(96, 32), 11, BF  'clear print area
		Sleep 100, 1
		Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
		Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
		Screencopy
		Sleep 100, 1
	Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""q"" to quit"
Screencopy

Dim p As Any Ptr = ThreadCreate(@thread)

Do
	reply = Inkey
	Sleep 100, 1
Loop Until Lcase(reply) = "q"

Print " Stop the thread"
Screencopy
terminate=1
Threadwait (p)
Print " Thread terminated"
Screencopy

Sleep
				%%Note: The ##//Sleep x, 1//## keyword just after the 'clear the print area' line is only here to highlight the flickering if no double buffering is used.
				
		- From the above example, if the date displaying and the time displaying are now two separate threads, a mutual exclusion mutex code block between these two threads is mandatory, not due to the graphics statements themselves competing, but only due to the double buffering method used (against flickering) that puts competing these two threads:
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq8-3.bas"}}%%(freebasic)
#include "vbcompat.bi"

Screen 12, , 2
Screenset 1, 0   
Color 0, 7
Cls

Dim Shared terminate As Integer = 0
Dim Shared mutex As Any Ptr

Sub thread1 (byval param As Any Ptr)   
	Screenset 1, 0
	Do
		Mutexlock(mutex)
		Line (16, 432)-Step(96, 16), 11, BF  'clear the print area
		Sleep 200, 1
		Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
		Screencopy
		Mutexunlock(mutex)
		Sleep 100, 1
	Loop Until terminate = 1
End Sub

Sub thread2 (byval param As Any Ptr)   
	Screenset 1, 0
	Do
		Mutexlock(mutex)
		Line (16, 448)-Step(96, 16), 11, BF  'clear the print area
		Sleep 100, 1
		Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
		Screencopy
		Mutexunlock(mutex)
		Sleep 100, 1
	Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""q"" to quit"
Screencopy

mutex = Mutexcreate
Dim p1 As Any Ptr = ThreadCreate(@thread1)
Dim p2 As Any Ptr = ThreadCreate(@thread2)

Do
	reply = Inkey
	Sleep 100, 1
Loop Until Lcase(reply) = "q"

Print " Stop the threads"
Screencopy
terminate=1
Threadwait (p1)
Threadwait (p2)
Mutexdestroy(mutex)
Print " Threads terminated"
Screencopy

Sleep
				%%Note: The ##//Sleep x, 1//## keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used, or if no mutex is used.
				
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ9"}}
{{fbdoc item="section" value="9. How to use console statements and keyboard inputs with multi-threading?"}}
		Console statements (such as ##//Locate//##, ##//Print//##, ##//Color//##, ...), as well as ##//Locate//## and ##//Print//## on Graphics window (but not ##//Color//## on Graphics Window), and keyboard inputs (such as ##//Inkey//##, ##//Getkey//##, ##//Input//##, ...) are not thread-safe:
			**""-""** Thus when they are used in competing sections of different threads, mutual exclusion is mandatory by means of mutex locking blocks in which in addition code can restore states (such as text cursor position, console color, ...) at end of the block (after its own usage), as they were before (at begin of the block).
			**""-""** But the ##//Getkey//## or ##//Input//## keyword cannot be enclosed inside a mutex locking block (as it can be do with the ##//Inkey//## keyword), because as long as the keyboard input is not completed, the other threads in compete would be fully blocked (waiting for the mutex unlocking).
			
			- Example showing that the keywords ##//Locate//## and ##//Print//## are not thread-safe both when applied on a console window or when applied on a graphics window (the text cursor states being not thread dependent in the two cases):
					%%(freebasic)
Sub Thread (Byval p As Any Ptr)
	Locate Cast(Integer, p), Cast(Integer, p)
	For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
		Sleep 20 * Cast(Integer, p), 1
		Print Str(Cast(Integer, p));
	Next I
End Sub

Sub test ()
	Dim As Any Ptr p(1 To 9)
	For I As Integer = 1 To 9
		p(I) = Threadcreate(@Thread, Cast(Any Ptr, I))
		Sleep 25, 1
	Next I
	For I As Integer = 1 To 9
		Threadwait(p(I))
	Next I
End Sub

Screen 0
test()
Locate 15, 1
Print "Any key to continue"
Sleep

Screen 12
test()
Locate 15, 1
Print "Any key to quit"
Sleep
					%%Note: One can see that each thread does not write on its own line corresponding to its thread number (id between 1 and 9), on the console window and on the graphics window.
					
			- From the above example, the thread code has been completed in its competing sections by mutex locking blocks and by saving/restoring cursor states before/after its own cursor moving:
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq9-1.bas"}}%%(freebasic)
Dim Shared As Any Ptr mutex

Sub Thread (Byval p As Any Ptr)
	Mutexlock(mutex)
	Dim As Long l0 = Locate()
	Locate Cast(Integer, p), Cast(Integer, p)
	Dim As Long l = Locate()
	Locate Hibyte(Loword(l0)), Lobyte(Loword(l0)), Hiword(l0)
	Mutexunlock(mutex)
	For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
		Sleep 20 * Cast(Integer, p), 1
		Mutexlock(mutex)
		l0 = Locate()
		Locate Hibyte(Loword(l)), Lobyte(Loword(l)), Hiword(l)
		Print Str(Cast(Integer, p));
		l = Locate()
		Locate Hibyte(Loword(l0)), Lobyte(Loword(l0)), Hiword(l0)
		Mutexunlock(mutex)
	Next I
End Sub

Sub test ()
	Dim As Any Ptr p(1 To 9)
	For I As Integer = 1 To 9
		p(I) = Threadcreate(@Thread, Cast(Any Ptr, I))
		Sleep 25, 1
	Next I
	For I As Integer = 1 To 9
		Threadwait(p(I))
	Next I
End Sub

mutex = Mutexcreate

Screen 0
test()
Locate 15, 1
Print "Any key to continue"
Sleep

Screen 12
test()
Locate 15, 1
Print "Any key to quit"
Sleep

Mutexdestroy(mutex)
					%%Note: One can see that each thread writes now on its own line corresponding to its thread number (id between 1 and 9), on the console window and on the graphics window.
					
			- Example showing that the ##//Color//## keyword is not thread-safe when applied on a console window, but is thread-safe when applied on a graphics window (the color states being thread dependent in that case):
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq9-2.bas"}}%%(freebasic)
Sub Thread (Byval p As Any Ptr)
	Color Cast(Integer, p) + 8, Cast(Integer, p)
	For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
		Print " " & Cast(Integer, p) & " ";
		Sleep 20 * Cast(Integer, p), 1
	Next I
End Sub

Sub test ()
	Dim As Any Ptr p(1 To 9)
	Locate 1, 1
	For I As Integer = 1 To 9
		p(I) = Threadcreate(@Thread, Cast(Any Ptr, I))
		Sleep 25, 1
	Next I
	For I As Integer = 1 To 9
		Threadwait(p(I))
	Next I
	Locate 16, 1
End Sub

Screen 0
test()
Print "Any key to continue"
Sleep

Screen 12
test()
Print "Any key to quit"
Sleep
					%%Note: One can see that the foreground/background colors are not specific to the thread number (id between 1 and 9) on the console window, but this works great on the graphics window.
					
			- From the above example, the thread code has been completed in its competing sections by mutex locking blocks and by saving/restoring color states before/after its own color values usage:
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq9-3.bas"}}%%(freebasic)
Dim Shared As Any Ptr mutex

Sub Thread (Byval p As Any Ptr)
	Mutexlock(mutex)
	Dim As Ulong c0 = Color(Cast(Integer, p) + 8, Cast(Integer, p))
	Dim As Ulong c = Color()
	Color(Loword(c0), Hiword(c0))
	Mutexunlock(mutex)
	For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
		Mutexlock(mutex)
		c0 = Color(Loword(c), Hiword(c))
		Print " " & Cast(Integer, p) & " ";
		Color(Loword(c0), Hiword(c0))
		Mutexunlock(mutex)
		Sleep 20 * Cast(Integer, p), 1
	Next I
End Sub

Sub test ()
	Dim As Any Ptr p(1 To 9)
	Locate 1, 1
	For I As Integer = 1 To 9
		p(I) = Threadcreate(@Thread, Cast(Any Ptr, I))
		Sleep 25, 1
	Next I
	For I As Integer = 1 To 9
		Threadwait(p(I))
	Next I
	Locate 16, 1
End Sub

mutex = Mutexcreate

Screen 0
test()
Print "Any key to continue"
Sleep

Screen 12
test()
Print "Any key to quit"
Sleep

Mutexdestroy(mutex)
					%%Note: One can see that the foreground/background colors are now specific to the thread number (id between 1 and 9) on the console window (obviously this always works on the graphics window).
					
		Therefore, for using Getkey or Input in competing sections of threads:
			**""-""** Only a single thread (for example, the main thread) can uses ##//Getkey//## or ##//Input//## in addition to console statements (such as ##//Locate//##, ##//Print//##, ##//Color//##, ...) and also ##//Inkey//##, in its competing sections.
			**""-""** The other threads must not to use in their competing sections any console statement neither any keyboard input keyword, but can use by cons graphics statements (such as ##//Pset//##, ##//Line//##, ##//Circle//##, ##//Draw String//##, graphic ##//Color//##, ...) which are themselves thread-safe (they can interlace graphically with the main thread without any problem).
			**""-""** ##//Input//## and ##//Getkey//## also exclude the screen locking usage in competing sections of threads (double buffering is recommended as anti-flickering method).
			
			- Example showing that graphics statements (such as ##//Line//## and ##//Draw String//## and ##//Screencopy//##) in a thread (user thread here) can compete with console statements (such as ##//Locate//## and ##//Print//## and ##//Input//##) in another thread (main thread here), without using any mutual exclusion (by mutex):
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq9-4.bas"}}%%(freebasic)
#include "vbcompat.bi"

Screen 12, , 2
Screenset 1, 0   
Color 0, 7
Cls

Dim Shared terminate As Integer = 0

Sub thread (byval param As Any Ptr)   
	Screenset 1, 0
	Do
		Line (16, 432)-Step(96, 32), 11, BF  'clear the print area
		Sleep 100, 1
		Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
		Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
		Screencopy
		Sleep 100, 1
	Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""quit"" to quit"
Screencopy

Dim p As Any Ptr = ThreadCreate(@thread)

Do
	Locate 3, 2
	Print Space(Len(reply) + 2);
	Locate 3, 2
	Input reply
Loop Until Lcase(reply) = "quit"

Print " Stop the thread"
Screencopy
terminate=1
Threadwait (p)
Print " Thread terminated"
Screencopy

Sleep
					%%Note: The ##//Sleep x, 1//## keyword just after the 'clear the print area' line is only here to highlight the flickering if no double buffering is used (screen locking being forbidden by Input usage).
					
			- From the above example, if the date displaying and the time displaying are now two separate user threads, a mutual exclusion mutex code block between these two threads only is mandatory, not due to the graphics statements themselves competing, but only due to the double buffering method used (against flickering) that puts competing these two user threads only:
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq9-5.bas"}}%%(freebasic)
#include "vbcompat.bi"

Screen 12, , 2
Screenset 1, 0   
Color 0, 7
Cls

Dim Shared terminate As Integer = 0
Dim Shared mutex As Any Ptr

Sub thread1 (byval param As Any Ptr)   
	Screenset 1, 0
	Do
		Mutexlock(mutex)
		Line (16, 432)-Step(96, 16), 11, BF  'clear the print area
		Sleep 200, 1
		Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
		Screencopy
		Mutexunlock(mutex)
		Sleep 100, 1
	Loop Until terminate = 1
End Sub

Sub thread2 (byval param As Any Ptr)   
	Screenset 1, 0
	Do
		Mutexlock(mutex)
		Line (16, 448)-Step(96, 16), 11, BF  'clear the print area
		Sleep 100, 1
		Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
		Screencopy
		Mutexunlock(mutex)
		Sleep 100, 1
	Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""quit"" to quit"
Screencopy

mutex = Mutexcreate
Dim p1 As Any Ptr = ThreadCreate(@thread1)
Dim p2 As Any Ptr = ThreadCreate(@thread2)

Do
	Locate 3, 2
	Print Space(Len(reply) + 2);
	Locate 3, 2
	Input reply
Loop Until Lcase(reply) = "quit"

Print " Stop the threads"
Screencopy
terminate=1
Threadwait (p1)
Threadwait (p2)
Mutexdestroy(mutex)
Print " Threads terminated"
Screencopy

Sleep
					%%Note: The ##//Sleep x, 1//## keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used (screen locking being forbidden by Input usage).
					
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ10"}}
{{fbdoc item="section" value="10. Is it better to take precautions when using the keyword 'Sleep' in threads?"}}
		There is still some doubt about the perfect behavior of the keyword ##//Sleep//## in a multi-threading context.
		
		It is therefore advisable to take the following precautions for its use:
			**""-""** If it is absolutely necessary in a critical section of a thread, the syntax ##//Sleep x//## or ##//Sleep x, 0//##, because inducing an internal test of a key-press, must for greatest safety be preferably treated in the same way as the ##//Inkey//## keyword to avoid as much as possible any concurrent conflict with other threads.
			**""-""** Otherwise, the syntax ##//Sleep x, 1//## (inducing no internal test of key-press) is rather advised when there is no protection by mutual exclusion, which is very often the case in order to release time-slice for the other threads.
			
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ11"}}
{{fbdoc item="section" value="11. Can all tools to handle multi-threading be encapsulated in a base Class (that the user extends with a derived Type for his own implementing)?"}}
		A simple 'threadUDT' base Class can be defined as follows:
			**""-""** with a private 'Any Ptr' non-static member field for each handle,
			**""-""** with a private 'Any Ptr' static member field for one shared mutex,
			**""-""** with a private 'Any Ptr' static member field for one shared conditional variable,
			**""-""** with its own public member procedures 'Sub()' calling the corresponding built-in procedures for multi-threading (with same procedure names), including also value integrity tests on the 3 above pointers (non-static procedures for the 3 'thread...()' member Subs, and static procedures for the 4 'mutex...()' member Subs and the 5 'cond...()' member Subs),
			**""-""** with an abstract private 'Sub()' thread to be overridden by another 'Sub()' from inside the user derived Type (therefore its static address is available in the virtual table of the object, and the hidden 'This' parameter passed by reference is compatible with the 'Any Ptr' parameter to be passed to the thread).
				%%(freebasic)
#include once "fbthread.bi"
Type threadUDT Extends Object
	Public:
		Declare Sub threadCreate ()
		Declare Sub threadWait ()
		Declare Sub threadDetach ()
		Declare Static Sub mutexCreate ()
		Declare Static Sub mutexLock ()
		Declare Static Sub mutexUnlock ()
		Declare Static Sub mutexDestroy ()
		Declare Static Sub condCreate ()
		Declare Static Sub condWait ()
		Declare Static Sub condSignal ()
		Declare Static Sub condBroadcast ()
		Declare Static Sub condDestroy ()
	Private:
		Declare Abstract Sub thread ()
		Dim As Any Ptr pThread
		Static As Any Ptr pMutex
		Static As Any Ptr pCond
End Type
Dim As Any Ptr threadUDT.pMutex
Dim As Any Ptr threadUDT.pCond

Sub threadUDT.threadCreate ()
	If This.pThread = 0 Then
	  This.pThread = .Threadcreate(Cast(Any Ptr Ptr Ptr, @This)[0][0], @This)
	End If
End Sub

Sub threadUDT.threadWait ()
	If This.pThread > 0 Then
		.Threadwait(This.pThread)
		This.pThread = 0
	End If
End Sub

Sub threadUDT.threadDetach ()
	If This.pThread > 0 Then
		.Threaddetach(This.pThread)
		This.pThread = 0
	End If
End Sub
  
Sub threadUDT.mutexCreate ()
	If threadUDT.pMutex = 0 Then
		threadUDT.pMutex = .Mutexcreate
	End If
End Sub
  
Sub threadUDT.mutexLock ()
	If threadUDT.pMutex > 0 Then
		.Mutexlock(threadUDT.pMutex)
	End If
End Sub

Sub threadUDT.mutexUnlock ()
	If threadUDT.pMutex > 0 Then
		.Mutexunlock(threadUDT.pMutex)
	End If
End Sub

Sub threadUDT.mutexDestroy ()
	If threadUDT.pMutex > 0 Then
		.Mutexdestroy(threadUDT.pMutex)
		threadUDT.pMutex = 0
	End If
End Sub

Sub threadUDT.condCreate ()
	If threadUDT.pCond = 0 Then
		threadUDT.pCond = .Condcreate
	End If
End Sub

Sub threadUDT.condWait ()
	If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
		.Condwait(threadUDT.pCond, threadUDT.pMutex)
	End If
End Sub

Sub threadUDT.condSignal ()
	If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
		.Condsignal(threadUDT.pCond)
	End If
End Sub

Sub threadUDT.condBroadcast ()
	If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
		.Condbroadcast(threadUDT.pCond)
	End If
End Sub

Sub threadUDT.condDestroy ()
	If threadUDT.pCond > 0 Then
		.Conddestroy(threadUDT.pCond)
		threadUDT.pCond = 0
	End If
End Sub
				%%
		- From the example 2 on the ##[[ProPgMtCriticalSections|Critical Sections]]## page "Synchronous method example using a condwait then a condbroadcast (and a mutex) for all threads", now the user implementation is modified to be compatible with the base Class 'threadUDT':
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq10-1.bas"}}%%(freebasic)
#include once "fbthread.bi"
Type threadUDT Extends Object
	Public:
		Declare Sub threadCreate ()
		Declare Sub threadWait ()
		Declare Sub threadDetach ()
		Declare Static Sub mutexCreate ()
		Declare Static Sub mutexLock ()
		Declare Static Sub mutexUnlock ()
		Declare Static Sub mutexDestroy ()
		Declare Static Sub condCreate ()
		Declare Static Sub condWait ()
		Declare Static Sub condSignal ()
		Declare Static Sub condBroadcast ()
		Declare Static Sub condDestroy ()
	Private:
		Declare Abstract Sub thread ()
		Dim As Any Ptr pThread
		Static As Any Ptr pMutex
		Static As Any Ptr pCond
End Type
Dim As Any Ptr threadUDT.pMutex
Dim As Any Ptr threadUDT.pCond

Sub threadUDT.threadCreate ()
	If This.pThread = 0 Then
	  This.pThread = .Threadcreate(Cast(Any Ptr Ptr Ptr, @This)[0][0], @This)
	End If
End Sub

Sub threadUDT.threadWait ()
	If This.pThread > 0 Then
		.Threadwait(This.pThread)
		This.pThread = 0
	End If
End Sub

Sub threadUDT.threadDetach ()
	If This.pThread > 0 Then
		.Threaddetach(This.pThread)
		This.pThread = 0
	End If
End Sub
  
Sub threadUDT.mutexCreate ()
	If threadUDT.pMutex = 0 Then
		threadUDT.pMutex = .Mutexcreate
	End If
End Sub
  
Sub threadUDT.mutexLock ()
	If threadUDT.pMutex > 0 Then
		.Mutexlock(threadUDT.pMutex)
	End If
End Sub

Sub threadUDT.mutexUnlock ()
	If threadUDT.pMutex > 0 Then
		.Mutexunlock(threadUDT.pMutex)
	End If
End Sub

Sub threadUDT.mutexDestroy ()
	If threadUDT.pMutex > 0 Then
		.Mutexdestroy(threadUDT.pMutex)
		threadUDT.pMutex = 0
	End If
End Sub

Sub threadUDT.condCreate ()
	If threadUDT.pCond = 0 Then
		threadUDT.pCond = .Condcreate
	End If
End Sub

Sub threadUDT.condWait ()
	If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
		.Condwait(threadUDT.pCond, threadUDT.pMutex)
	End If
End Sub

Sub threadUDT.condSignal ()
	If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
		.Condsignal(threadUDT.pCond)
	End If
End Sub

Sub threadUDT.condBroadcast ()
	If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
		.Condbroadcast(threadUDT.pCond)
	End If
End Sub

Sub threadUDT.condDestroy ()
	If threadUDT.pCond > 0 Then
		.Conddestroy(threadUDT.pCond)
		threadUDT.pCond = 0
	End If
End Sub

'------------------------------------------------------------------------------

Type UDT Extends threadUDT
	Declare Sub counter ()
	Declare Sub thread ()
	Dim As Integer number
	Dim As Integer tempo
	Dim As ULongInt count
	Static As Integer threadPriorityNumber
	Static As Integer numberMax
	Static As Integer quit
End Type
Dim As Integer UDT.threadPriorityNumber
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Sub UDT.counter ()
	Locate This.number, This.number, 0
	Sleep 5, 1
	This.count += 1
	Print This.count;
	Locate This.number, 30 + This.number, 0
End Sub

Sub UDT.Thread ()
	Dim As Integer myquit
	Do
		This.mutexLock()
		While UDT.threadPriorityNumber <> This.number  '' synchronous condwait for expected condition
			This.condWait()
		Wend
		This.counter()
		myquit = UDT.quit
		UDT.threadPriorityNumber = (UDT.threadPriorityNumber + 1) Mod (UDT.numberMax + 1)
		This.condBroadcast()
		This.mutexUnlock()
		Sleep This.tempo, 1
	Loop Until myquit = 1
End Sub


UDT.numberMax = 6
Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
	u(I).number = i
	u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I
UDT.mutexCreate()
UDT.condCreate()

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
	u(I).threadCreate()
Next I

Dim As String s
Do
	UDT.mutexLock()
	While UDT.threadPriorityNumber <> u(0).number
		UDT.condWait()
	Wend
	s = Inkey
	If s <> "" Then
		UDT.quit = 1
	End If
	UDT.threadPriorityNumber = (UDT.threadPriorityNumber + 1) Mod (UDT.numberMax + 1)
	UDT.condBroadcast()
	UDT.mutexUnlock()
	Sleep u(0).tempo, 1
Loop Until s <> ""

For I As Integer = 1 To UDT.numberMax
	u(I).threadWait()
Next I
t = Timer - t

UDT.mutexDestroy()
UDT.condDestroy()
Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
	c += u(I).count
Next I
Locate UDT.numberMax+2, 1
Print CULngInt(c / t) & " increments per second"

Sleep
				%%
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ12"}}
{{fbdoc item="section" value="12. Can we emulate a kind of TLS (Thread Local Storage) with FreeBASIC?"}}
		Static variables are normally shared across all the threads. If we modify a static variable, it is visible so modified to all the threads.
		Unlike normal static variable, if we create a TLS static variable, every thread must have its own copy of the variable (but with the same access name), i.e. any change to the variable is local to the thread (locally stored).
		This allows to create a thread-safe procedure, because each call to this procedure gets its own copy of the same declared static variables.
		In normal procedure with static variables, the content of that variables can be updated by multiple threads, but with TLS, we can think of these as static data but local to each thread.
		
		TLS data is similar to static data, but the only difference is that TLS data are unique to each thread.
		
		The principle of this TLS emulation for ""FreeBASIC"" is to use a static array for each requested TLS variable, where each thread has its own unique index (hidden) to access the array element.
		This unique index relating to the thread is deduced from the thread handle value:
			**""-""** With fbc version >= 1.08, the thread handle value is simply returned from the ""'Threadself()'"" function calling (new function) from any thread.
			**""-""** With fbc version < 1.08, the code is more twisted:
					""-"" The thread handle value is only accessible from the ""'ThreadCreate()'"" return in the parent (or main) thread when creating it.
					""-"" There is no way to properly emulate the ""'Threadself()'"" function, but only by a twisted method.
					""-"" In the example below (for fbc version < 1.08), a ""'Threadself()'"" function (returning by reference) value is initialized before each use by the thread (with its own thread handle), and all of this (initialization + use) protected by a mutex as for its corresponding ""'ThreadCreate()'"".
					
		In the below example, the TLS static variable is an integer which is used in a single and generic counting procedure ('counter()') with none passed parameter). This counting procedure is called by each thread (thus each thread counts independently of each other but by calling the same single counting procedure).
		A single macro allows to define any TLS variable (except array) of any type.
		
		- Code with preprocessor conditional directives depending on fbc version:
				{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq12.bas"}}%%(freebasic)
#include once "crt/string.bi"

#if __fb_version__ < "1.08"
	' Emulation of the function Threadself() of FreeBASIC
	' Before each use, the thread must refresh this function value with its own thread handle,
	' and all of this (refreshing + use) protected by a mutex.
	Function Threadself () Byref As Any Ptr
		Static As Any Ptr handle
		Return handle
	End Function
#else
	#include once "fbthread.bi"
#endif

#macro CreateTLSdatatypeVariableFunction (variable_function_name, variable_datatype)
' Creation of a "variable_function_name" function to emulate a static datatype variable (not an array),
' with a value depending on the thread using it.
	Function variable_function_name (Byval cd As Boolean = True) ByRef As variable_datatype
	' Function emulating (creation/access/destruction) a static datatype variable with value depending on thread using it:
		' If calling without parameter (or with 'True') parameter, this allows to [create and] access the static datatype variable.
		' If calling with the 'False' parameter, this allows to destroy the static datatype variable.
		Dim As Integer bound = 0
		Static As Any Ptr TLSindex(bound)
		Static As variable_datatype TLSdata(bound)
		Dim As Any Ptr Threadhandle = Threadself()
		Dim As Integer index = 0
		For I As Integer = 1 To UBound(TLSindex)  ' search existing TLS variable (existing array element) for the running thread
			If TLSindex(I) = Threadhandle Then
				index = I
				Exit For
			End If
		Next I
		If index = 0 And cd = True Then  ' create a new TLS variable (new array element) for a new thread
			index = UBound(TLSindex) + 1
			Redim Preserve TLSindex(index)
			TLSindex(index) = Threadhandle
			Redim Preserve TLSdata(index)
		Elseif index > 0 And cd = False Then  ' destroy a TLS variable (array element) and compact the array
			If index < UBound(TLSindex) Then  ' reorder the array elements
				memmove(@TLSindex(index), @TLSindex(index + 1), (UBound(TLSindex) - index) * Sizeof(Any Ptr))
				Dim As variable_datatype Ptr p = Allocate(Sizeof(variable_datatype))  ' for compatibility to object with destructor
				memmove(p, @TLSdata(index), Sizeof(variable_datatype))                ' for compatibility to object with destructor
				memmove(@TLSdata(index), @TLSdata(index + 1), (UBound(TLSdata) - index) * Sizeof(variable_datatype))
				memmove(@TLSdata(UBound(TLSdata)), p, Sizeof(variable_datatype))      ' for compatibility to object with destructor
				Deallocate(p)                                                         ' for compatibility to object with destructor
			End If
			Redim Preserve TLSindex(UBound(TLSindex) - 1)
			Redim Preserve TLSdata(UBound(TLSdata) - 1)
			index = 0
		End If
		Return TLSdata(index)
	End Function
#endmacro

'------------------------------------------------------------------------------

Type threadData
	Dim As Any Ptr handle
	Dim As String prefix
	Dim As String suffix
	Dim As Double tempo
	#if __fb_version__ < "1.08"
		Static As Any Ptr mutex
	#endif
End Type
#if __fb_version__ < "1.08"
	Dim As Any Ptr threadData.mutex
#endif

CreateTLSdatatypeVariableFunction (TLScount, Integer)  ' create a TLS static integer function

Function counter() As Integer  ' definition of a generic counter with counting depending on thread calling it
	TLScount() += 1            ' increment the TLS static integer
	Return TLScount()          ' return the TLS static integer
End Function

Sub Thread(ByVal p As Any Ptr)
	Dim As threadData Ptr ptd = p
	Dim As UInteger c
	Do
		#if __fb_version__ < "1.08"
			MutexLock(threadData.mutex)
			Threadself() = ptd->handle
		#endif
			c = counter()
		#if __fb_version__ < "1.08"
			MutexUnlock(threadData.mutex)
		#endif
		Print ptd->prefix & c & ptd->suffix & " ";  ' single print with concatenated string avoids using a mutex
		Sleep ptd->tempo, 1
	Loop Until c = 12
	#if __fb_version__ < "1.08"
		MutexLock(threadData.mutex)
		Threadself() = ptd->handle
	#endif
	TLScount(False)  ' destroy the TLS static integer
	#if __fb_version__ < "1.08"
		MutexUnlock(threadData.mutex)
	#endif
End Sub

'------------------------------------------------------------------------------

Print "|x| : counting from thread a"
Print "(x) : counting from thread b"
Print "[x] : counting from thread c"
Print

#if __fb_version__ < "1.08"
	threadData.mutex = MutexCreate()
#endif

Dim As threadData mtlsa
mtlsa.prefix = "|"
mtlsa.suffix = "|"
mtlsa.tempo = 100
#if __fb_version__ < "1.08"
	MutexLock(threadData.mutex)
#endif
mtlsa.handle = ThreadCreate(@Thread, @mtlsa)
#if __fb_version__ < "1.08"
	MutexUnlock(threadData.mutex)
#endif

Dim As threadData mtlsb
mtlsb.prefix = "("
mtlsb.suffix = ")"
mtlsb.tempo = 150
#if __fb_version__ < "1.08"
	MutexLock(threadData.mutex)
#endif
mtlsb.handle = ThreadCreate(@Thread, @mtlsb)
#if __fb_version__ < "1.08"
	MutexUnlock(threadData.mutex)
#endif

Dim As threadData mtlsc
mtlsc.prefix = "["
mtlsc.suffix = "]"
mtlsc.tempo = 250
#if __fb_version__ < "1.08"
	MutexLock(threadData.mutex)
#endif
mtlsc.handle = ThreadCreate(@Thread, @mtlsc)
#if __fb_version__ < "1.08"
	MutexUnlock(threadData.mutex)
#endif

ThreadWait(mtlsa.handle)
ThreadWait(mtlsb.handle)
ThreadWait(mtlsc.handle)
#if __fb_version__ < "1.08"
	MutexDestroy(threadData.mutex)
#endif

Print
Print
Print "end of threads"

Sleep
				%%Output example
					%%
|x| : counting from thread a
(x) : counting from thread b
[x] : counting from thread c

|1| (1) [1] |2| (2) |3| [2] (3) |4| |5| (4) [3] |6| (5) |7| [4] (6) |8| |9| (7) [5] |10| (8) |11| |12| (9) [6] (10) [7] (11) (12) [8] [9] [10] [11] [12]

end of threads
					%%
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@
----
{{anchor name="ProPgMtCriticalSectionsFAQ13"}}
{{fbdoc item="section" value="13. Can we emulate a kind of thread pooling feature with FreeBASIC?"}}
		- Preamble:
				A thread pool is a set of threads that can be used to run tasks based on user needs.
				The thread pool is accessible via a Type structure.
				
				Creating a new thread is an expensive act in terms of resources, both from a processor (CPU) point of view and from a memory point of view.
				Also, in the event that a program requires the execution of many tasks, the creation and deletion of a thread for each of them would greatly penalize the performance of the application.
				Therefore, it would be interesting to be able to share the creation of threads so that a thread that has finished executing a task is available for the execution of a future task.
				
				The objective of thread pooling is to pool the threads in order to avoid untimely creation or deletion of threads, and thus allow their reuse.
				So when a task needs to be executed, it will be more resource efficient to check if the thread pool contains an available thread.
				If so, it will be used while the task is running, and then freed when the task is completed.
				If there is no thread available, a new thread can be created, and at the end of the task, the thread would be in turn available in the pool of threads.
				
		- Content:
				Two Type structures are proposed below:
						- ""ThreadInitThenMultiStart"".
						- ""ThreadPooling"".
						
				The two structures make it possible to use one thread per instance created, and to chain on this dedicated thread the execution of user procedures one after the other, but without the thread stopping between each:
					**""-""** The ""'ThreadInitThenMultiStart'"" structure requires a manual start after initialization (and manual wait for completion) for each user procedure to be executed in sequence in the thread.
					**""-""** The ""'ThreadPooling'"" structure allows to register a sequence of user thread procedure submissions in a queue, while at same time the user procedures start to be executed in the thread without waiting (a last registered wait command is enough to test for full sequence completion).
					
				By creating and using several instances, the two structures make it possible to execute sequences of user procedures in several threads, therefore executed in parallel (temporally).
				
		- **""ThreadInitThenMultiStart""** Type:
				- Principle:
						The ""'ThreadInitThenMultiStart'"" Type below operationally provides to user 3 (4 actually) main public methods (plus a constructor and a destructor), and internally uses 9 private data members plus 1 private subroutine (static) member.
						
						The public methods are:
							**""-""** ""ThreadInit"" : Initialize the instance with the parameters of the requested user procedure to be executed in a thread.
							**""-""** ""ThreadStart"" : Start the user procedure execution in the thread (2 overload methods).
							**""-""** ""ThreadWait"" : Wait for the completion of the user procedure in the thread.
							
						By creating several instances each associated with a thread, we can obtain a kind of thread pooling feature.
						The ""'ThreadInitThenMultiStart'"" Type does not manage any pending thread queue.
						It is up to the user to choose an existing instance or to create a new instance with which to run his thread procedure.
						
				- Description:
						Each user procedure (to be executed in a thread) must be available under the following function signature:
							##Function userproc (Byval puserdata As Any Ptr) As String##
						in order to be compatible with the parameters of the ""'ThreadInit'"" method:
							##""Declare Sub ThreadInit (Byval pThread As Function (Byval As Any Ptr) As String, Byval p As Any Ptr = 0)""##
						and perform the instance ('t') initialization by:
							##""t.ThreadInit(@userproc [, puserdata])""##
							
						The other methods are called on the instance ('t'):
							##""t.ThreadStart()""## or ##""t.ThreadStart(puserdata)""##
							##""t.ThreadWait()""##
							
						The different methods must be called respecting the order of the following sequence:
							##""ThreadInit, [user code,] ThreadStart, [user code,] ThreadWait, [user code,] ThreadStart, [user code,] ThreadWait, [user code,] .....""##
							
						After any ""'ThreadStart'...'ThreadWait'"" sequence, a new user thread procedure can be initialized by calling the ""'ThreadInit'"" method again on the same instance.
						On the other hand, ""'ThreadStart'...'ThreadWait'"" sequences can also be chained on different instances already initialized.
						If using several instances (so several threads), the ordered sequences on each instance can be interlaced between instances because calling methods on different instances.
						The overload method ""'ThreadStart(Byval p As Any Ptr)'"" allows to start the user thread procedure by specifying a new parameter value, without having to call ""'ThreadInit'"" first. The overload method ""'ThreadStart()'"" starts the user thread procedure without modifying the parameter value.
						
						The ""'ThreadWait'"" method returns a 'As String' Type (by value), like the user thread function is declared (a string variable return allows to also pass a numeric value).
						
						This user data return from the user function is accessed through the ""'ThreadWait'"" return. It is always safe (because in this case, the user thread function has been always fully executed).
						If the user doesn't want to use the return value of its thread function (to be used like for a subroutine):
							**""-""** He ends his user thread function with ##Return ####"####"## for example.
							**""-""** He calls ""'ThreadWait'"" as a subroutine and not as a function (not accessing the value potentially returned by ""'ThreadWait'"").
							
						**Warning:** The information supplied to the user thread procedure via the passed pointer (by ""'ThreadInit'"" or ""'ThreadStart'"") should not be changed between ""'ThreadStart'"" and ""'ThreadWait'"" due to the time uncertainty on the real call of the user thread procedure in this interval.
						
				- Under the hood:
						In fact, each instance is associated with an internal thread that runs continuously in a loop as soon as a first initialization (""'ThreadInit'"") has taken place. This internal thread runs the private subroutine (static) member.
						It is this private subroutine (static) member that will call (on a ""'ThreadStart'"") the user procedure to be executed, like a classic function call. The value returned by the user function is stored to be subsequently returned to the user through the returned value by ""'ThreadWait'"".
						
						So, for each new ""'ThreadInitThenMultiStart'"" instance, an internal thread is started on the first ""'ThreadInit'"" method (calling the ""'ThreadCreate'"" ""FreeBASIC"" keyword), then the user thread procedure is started on the ""'ThreadStart'"" method request.
						As each initialized instance is associated with a running internal thread, using local scopes or dynamic instances allow to stop internal threads that are no longer used.
						
						In the ""'ThreadInitThenMultiStart'"" Type, an additional property ""'ThreadState'"" is available to returns (in a Ubyte) the current internal state of the process.
						This property allows to sample at any time the state of the internal thread.
						This property can also be used during the debugging phase (allowing in addition to identify the case of blocking in the user thread procedure running).
						
						""ThreadState"":
							0 -> disabled (internal thread stopped, waiting for ""'ThreadInit'"")
							1 -> available (waiting for ""'ThreadStart'"" or another ""'ThreadInit'"")
							2 -> busy (user thread procedure running)
							4 -> completing (user thread procedure completed, but waiting for ""'ThreadWait'"")
							
						Internally, the Type uses 3 mutexes (by self locking and mutual unlocking) to ensure the ordered sequence of methods called as defined above and wait for the end of the user thread function or for a new user thread function to call.
						So, no waiting loop is used in the methods coding but only mutexes locking/unlocking requests, so that the halted thread (on a mutex to be locked) has its execution suspended and does not consume any CPU time until the mutex is unlocked.
						The constructor is responsible for creating and locking the 3 mutexes, while the destructor stops the thread (if it exists) then destroys the 3 mutexes.
						
						**Note:** An advised user can stop the internal thread (linked to instance 't') by using the non-breaking sequence: ##t.Destructor() : t.Constructor()##. Then a ##""t.ThreadInit(...)""## is necessary to start a new internal thread.
						
				- Example:
						Chronology of the user code:
							**""-""** A single ""'ThreadInitThenMultiStart'"" instance is created in order to use a single thread.
							**""-""** The instance is initialized (""'ThreadInit'"") with a first user thread function: ""'UserThreadS'"" (internal thread creation by using the ""'ThreadCreate'"" ""FreeBASIC"" keyword).
							**""-""** A sequence of 9 ""'ThreadStart...ThreadWait'"" is requested for this first user thread function, used like a thread subroutine.
							**""-""** The same instance is reinitialized (""'ThreadInit'"") with a second user thread function: ""'UserThreadF'"" (the previous pending thread will be reused).
							**""-""** A sequence of 9 ""'ThreadStart...ThreadWait'"" is also requested for this second user thread function, used like a thread function.
							
						Full code with the ""'ThreadInitThenMultiStart'"" Type:
							{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq13.bas"}}%%(freebasic)
		Type ThreadInitThenMultiStart
			Public:
				Declare Constructor()
				Declare Sub ThreadInit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
				Declare Sub ThreadStart()
				Declare Sub ThreadStart(Byval p As Any Ptr)
				Declare Function ThreadWait() As String
				Declare Property ThreadState() As Ubyte
				Declare Destructor()
			Private:
				Dim As Function(Byval p As Any Ptr) As String _pThread
				Dim As Any Ptr _p
				Dim As Any Ptr _mutex1
				Dim As Any Ptr _mutex2
				Dim As Any Ptr _mutex3
				Dim As Any Ptr _pt
				Dim As Byte _end
				Dim As String _returnF
				Dim As Ubyte _state
				Declare Static Sub _Thread(Byval p As Any Ptr)
		End Type
		
		Constructor ThreadInitThenMultiStart()
			This._mutex1 = Mutexcreate()
			Mutexlock(This._mutex1)
			This._mutex2 = Mutexcreate()
			Mutexlock(This._mutex2)
			This._mutex3 = Mutexcreate()
			Mutexlock(This._mutex3)
		End Constructor
		
		Sub ThreadInitThenMultiStart.ThreadInit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
			This._pThread = pThread
			This._p = p
			If This._pt = 0 Then
				This._pt= Threadcreate(@ThreadInitThenMultiStart._Thread, @This)
				Mutexunlock(This._mutex3)
				This._state = 1
			End If
		End Sub
		
		Sub ThreadInitThenMultiStart.ThreadStart()
			Mutexlock(This._mutex3)
			Mutexunlock(This._mutex1)
		End Sub
		
		Sub ThreadInitThenMultiStart.ThreadStart(Byval p As Any Ptr)
			Mutexlock(This._mutex3)
			This._p = p
			Mutexunlock(This._mutex1)
		End Sub
		
		Function ThreadInitThenMultiStart.ThreadWait() As String
			Mutexlock(This._mutex2)
			Mutexunlock(This._mutex3)
			This._state = 1
			Return This._returnF
		End Function
		
		Property ThreadInitThenMultiStart.ThreadState() As Ubyte
			Return This._state
		End Property
		
		Sub ThreadInitThenMultiStart._Thread(Byval p As Any Ptr)
			Dim As ThreadInitThenMultiStart Ptr pThis = p
			Do
				Mutexlock(pThis->_mutex1)
				If pThis->_end = 1 Then Exit Sub
				pThis->_state = 2
				pThis->_returnF = pThis->_pThread(pThis->_p)
				pThis->_state = 4
				Mutexunlock(pThis->_mutex2)
			Loop
		End Sub
		
		Destructor ThreadInitThenMultiStart()
			If This._pt > 0 Then
				This._end = 1
				Mutexunlock(This._mutex1)
				.ThreadWait(This._pt)
			End If
			Mutexdestroy(This._mutex1)
			Mutexdestroy(This._mutex2)
			Mutexdestroy(This._mutex3)
		End Destructor
		
		'---------------------------------------------------
		
		Function UserThreadS(Byval p As Any Ptr) As String
			Dim As Uinteger Ptr pui = p
			Print *pui * *pui
			Return ""
		End Function
		
		Function UserThreadF(Byval p As Any Ptr) As String
			Dim As Uinteger Ptr pui = p
			Dim As Uinteger c = (*pui) * (*pui)
			Return Str(c)
		End Function
		
		Dim As ThreadInitThenMultiStart t
		
		Print "First user function executed like a thread subroutine:"
		t.ThreadInit(@UserThreadS)  '' initializes the user thread function (used as subroutine)
		For I As Uinteger = 1 To 9
			Print I & "^2 = ";
			t.ThreadStart(@I)       '' starts the user thread procedure code body
			t.Threadwait()          '' waits for the user thread procedure code end
		Next I
		Print
		
		Print "Second user function executed like a thread function:"
		t.ThreadInit(@UserThreadF)  '' initializes the user thread function (used as function)
		For I As Uinteger = 1 To 9
			Print I & "^2 = ";
			t.ThreadStart(@I)       '' starts the user thread procedure code body
			Print t.Threadwait()    '' waits for the user thread procedure code end and prints result
		Next I
		Print
		
		Sleep
							%%Output:
								%%
First user function executed like a thread subroutine:
1^2 = 1
2^2 = 4
3^2 = 9
4^2 = 16
5^2 = 25
6^2 = 36
7^2 = 49
8^2 = 64
9^2 = 81

Second user function executed like a thread function:
1^2 = 1
2^2 = 4
3^2 = 9
4^2 = 16
5^2 = 25
6^2 = 36
7^2 = 49
8^2 = 64
9^2 = 81
								%%
		- **""ThreadPooling""** Type:
				- Principle:
						The ""'ThreadPooling'"" Type below operationally provides to user 2 (3 actually) main public methods (plus a constructor and a destructor), and internally uses 11 private data members plus 1 private subroutine (static) member.
						
						The public methods are:
							**""-""** ""PoolingSubmit"" : Enter a user thread procedure in the queue.
							**""-""** ""PoolingWait"" : Wait for full emptying of the queue (with last user procedure executed).
							
						By creating several instances each associated with a thread, we can obtain a kind of thread pooling feature.
						The ""'ThreadPooling'"" Type manages a pending thread queue by instance (so, by thread).
						It is up to the user to choose an existing instance or to create a new instance with which to run his thread procedure sequence.
						
						On each ""'ThreadPooling'"" Type instance, the submitted user thread procedures are immediately entered in a queue specific to the instance.
						These buffered user thread procedures are sequentially as soon as possible executed in the thread dedicated to the instance.
						
				- Description:
						Each user procedure (to be executed in a thread) must be available under the following function signature:
						##Function userproc (Byval puserdata As Any Ptr) As String##
						in order to be compatible with the parameters of the ""'PoolingSubmit()'"" method:
						##""Declare Sub PoolingSubmit (Byval pThread As Function (Byval As Any Ptr) As String, Byval p As Any Ptr = 0)""##
						and perform the instance ('t') submission in the queue by:
						##""t.PoolingSubmit(@userproc [, puserdata])""##
						
						The other method is called on the instance ('t'):
						##""t.PoolingWait() or t.PoolingWait(returndata())""##
						
						The different methods must be called respecting the order of the following sequence:
						##""PoolingSubmit, [user code,] [PoolingSubmit, [user code,] [PoolingSubmit, [user code, ...]] PoolingWait, [user code,] ...""##
						
						After any ""'PoolingSubmit'...'PoolingWait'"" sequence, a new user thread procedure sequence can be submitted by calling another ""'PoolingSubmit'...'PoolingWait'"" sequence again on the same instance.
						On the other hand, ""'PoolingSubmit'...'PoolingWait'"" sequences can also be chained on different instances already initialized.
						If using several instances (so several threads), the ordered sequences on each instance can be interlaced between instances because calling methods on different instances.
						
						The ""'PoolingWait(returndata())'"" method fills in a String array with the user thread function returns (a string variable return allows to also pass a numeric value).
						These user data returns from the user functions is accessed through the argument of ""'PoolingWait(returndata())'"" method. It is always safe (because in this case, the user thread functions has been always fully executed).
						If the user doesn't want to use the return values of its thread functions (to be used like for subroutines):
							**""-""** He ends his user thread functions with ##Return ####"####"## for example.
							**""-""** He calls the ""'PoolingWait()'"" method without parameter.
							
						**Warning:** The information supplied to the user thread procedure via the passed pointer (by ""'PoolingSubmit'"") should not be changed between ""'PoolingSubmit'"" and ""'PoolingWait'"" due to the time uncertainty on the real call of the user thread procedure in this interval.
						
				- Under the hood:
						In fact, each instance is associated with an internal thread that runs continuously in a loop as soon as the instance is constructed. This internal thread runs the private subroutine (static) member.
						It is this private subroutine (static) member that will call the user procedures of the sequence to be executed, like classic function calls. The value returned by each user function is stored in an internal string array to be finally returned to the user through the argument of ""'PoolingWait(returndata())'"".
						
						So, for each new ""'ThreadPooling'"" instance, an internal thread is started by the constructor, then each user thread procedure is started on each dequeuing of the registered submissions.
						As each initialized instance is associated with a running internal thread, using local scopes or dynamic instances allow to stop internal threads that are no longer used.
						
						In the ""'ThreadPooling'"" Type, an additional property ""'PoolingState'"" is available to returns (in a Ubyte) the current internal state of the process.
						This property allows to sample at any time the state of the internal thread.
						This property can also be used during the debugging phase (allowing in addition to identify the case of blocking in the user thread procedure running).
						
						""PoolingState"":
							0 -> User thread procedures sequence execution completed (after ""'PoolingWait'"" acknowledge or new instance creation)
							1 -> Beginning of user thread procedure sequence submitted but no still executing (after first ""'PoolingSubmit'"")
							2 -> User thread procedure running
							4 -> User thread procedure sequence execution pending (for ""'PoolingWait'"" acknowledge or new user thread procedure submission)
							
						An overload method ""'PoolingWait(values() As String)'"" is added.
						""'PoolingWait(values()'"" As String) fills out a user-supplied dynamic array with the return value sequence from the latest user thread functions (then internally clear these same supplied return data).
						The other overload method ""'PoolingWait()'"" (without passed parameter) also clears the internal return values.
						
						""'ThreadPooling'"" Type allows to manage kind of "FIFOs" (First In First Out) via dynamic arrays:
							**""-""** Arrays are filled in as user submissions (from the main thread).
							**""-""** Arrays are automatically emptied on the fly by the secondary thread which executes their requests as and when.
							**""-""** So, the inputs and outputs of the "FIFOs" are therefore asynchronous with an optimized throughput on each side.
							
						With ""'ThreadPooling'"" the execution time of a ""'PoolingSubmit'"" method in the main thread, corresponds only to the time spent to register the user procedure submission.
						
						It is necessary to be able to do (for the ""'PoolingSubmit'"", ""'PoolingWait'"" and 'Destructeur' methods, all in competition with '_Thread' subroutine) atomic mutex unlockings, which is not possible with simple mutexlocks / mutexunlocks.
						This therefore requires the use of conditional variables (condwait / condsignal).
						
						The constructor is responsible for creating the 2 conditional variables and the associated mutex, while the destructor stops the thread then destroys the 2 conditional variables and the associated mutex.
						
				- Example:
						Chronology of the user code:
							**""-""** A single ""'ThreadPooling'"" instance is created in order to use a single thread.
							**""-""** A first sequence (a) of 3 ""'PoolingSubmit'"" is requested for the first three user thread functions, ended by a ""'PoolingWait'"" without parameter.
							**""-""** A second sequence (b) of 3 ""'PoolingSubmit'"" is requested for the last three user thread functions, ended by a ""'PoolingWait'"" with a dynamic string array as argument (so, only the returns from the last three user thread functions will fill out in the dynamic string array).
							
						Full code with the ""'ThreadPooling'"" Type:
							{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq13-2.bas"}}%%(freebasic)
#include once "crt/string.bi"
Type ThreadPooling
	Public:
		Declare Constructor()
		Declare Sub PoolingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
		Declare Sub PoolingWait()
		Declare Sub PoolingWait(values() As String)
		Declare Property PoolingState() As Ubyte
		Declare Destructor()
	Private:
		Dim As Function(Byval p As Any Ptr) As String _pThread0
		Dim As Any Ptr _p0
		Dim As Function(Byval p As Any Ptr) As String _pThread(Any)
		Dim As Any Ptr _p(Any)
		Dim As Any Ptr _mutex
		Dim As Any Ptr _cond1
		Dim As Any Ptr _cond2
		Dim As Any Ptr _pt
		Dim As Byte _end
		Dim As String _returnF(Any)
		Dim As Ubyte _state
		Declare Static Sub _Thread(Byval p As Any Ptr)
End Type

Constructor ThreadPooling()
	Redim This._pThread(0)
	Redim This._p(0)
	Redim This._returnF(0)
	This._mutex = Mutexcreate()
	This._cond1 = Condcreate()
	This._cond2 = Condcreate()
	This._pt= Threadcreate(@ThreadPooling._Thread, @This)
End Constructor

Sub ThreadPooling.PoolingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
	Mutexlock(This._mutex)
	Redim Preserve This._pThread(Ubound(This._pThread) + 1)
	This._pThread(Ubound(This._pThread)) = pThread
	Redim Preserve This._p(Ubound(This._p) + 1)
	This._p(Ubound(This._p)) = p
	CondSignal(This._cond2)
	This._state = 1
	Mutexunlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait()
	Mutexlock(This._mutex)
	While This._state <> 4
		Condwait(This._Cond1, This._mutex)
	Wend
	Redim This._returnF(0)
	This._state = 0
	Mutexunlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait(values() As String)
	Mutexlock(This._mutex)
	While This._state <> 4
		Condwait(This._Cond1, This._mutex)
	Wend
	If Ubound(This._returnF) > 0 Then
		Redim values(1 To Ubound(This._returnF))
		For I As Integer = 1 To Ubound(This._returnF)
			values(I) = This._returnF(I)
		Next I
		Redim This._returnF(0)
	Else
		Erase values
	End If
	This._state = 0
	Mutexunlock(This._mutex)
End Sub

Property ThreadPooling.PoolingState() As Ubyte
	Return This._state
End Property

Sub ThreadPooling._Thread(Byval p As Any Ptr)
	Dim As ThreadPooling Ptr pThis = p
	Do
		Mutexlock(pThis->_mutex)
		If Ubound(pThis->_pThread) = 0 Then
			pThis->_state = 4
			CondSignal(pThis->_cond1)
			While Ubound(pThis->_pThread) = 0
				Condwait(pThis->_cond2, pThis->_mutex)
				If pThis->_end = 1 Then Exit Sub
			Wend
		End If
		pThis->_pThread0 = pThis->_pThread(1)
		pThis->_p0 = pThis->_p(1)
		If Ubound(pThis->_pThread) > 1 Then
			memmove(@pThis->_pThread(1), @pThis->_pThread(2), (Ubound(pThis->_pThread) - 1) * Sizeof(pThis->_pThread))
			memmove(@pThis->_p(1), @pThis->_p(2), (Ubound(pThis->_p) - 1) * Sizeof(pThis->_p))
		End If
		Redim Preserve pThis->_pThread(Ubound(pThis->_pThread) - 1)
		Redim Preserve pThis->_p(Ubound(pThis->_p) - 1)
		Mutexunlock(pThis->_mutex)
		Redim Preserve pThis->_ReturnF(Ubound(pThis->_returnF) + 1)
		pThis->_state = 2
		pThis->_returnF(Ubound(pThis->_returnF)) = pThis->_pThread0(pThis->_p0)
	Loop
End Sub

Destructor ThreadPooling()
	Mutexlock(This._mutex)
	This._end = 1
	CondSignal(This._cond2)
	Mutexunlock(This._mutex)
	.ThreadWait(This._pt)
	Mutexdestroy(This._mutex)
	Conddestroy(This._cond1)
	Conddestroy(This._cond2)
End Destructor

'---------------------------------------------------

Sub Prnt (Byref s As String, Byval p As Any Ptr)
	Dim As String Ptr ps = p
	If ps > 0 Then Print *ps;
	For I As Integer = 1 To 10
		Print s;
		Sleep 100, 1
	Next I
End Sub

Function UserCode1 (Byval p As Any Ptr) As String
	Prnt("1", p)
	Return "UserCode #1"
End Function

Function UserCode2 (Byval p As Any Ptr) As String
	Prnt("2", p)
	Return "UserCode #2"
End Function

Function UserCode3 (Byval p As Any Ptr) As String
	Prnt("3", p)
	Return "UserCode #3"
End Function

Function UserCode4 (Byval p As Any Ptr) As String
	Prnt("4", p)
	Return "UserCode #4"
End Function

Function UserCode5 (Byval p As Any Ptr) As String
	Prnt("5", p)
	Return "UserCode #5"
End Function

Function UserCode6 (Byval p As Any Ptr) As String
	Prnt("6", p)
	Return "UserCode #6"
End Function

Dim As String sa = "  Sequence #a: "
Dim As String sb = "  Sequence #b: "
Dim As String s()

Dim As ThreadPooling t

t.PoolingSubmit(@UserCode1, @sa)
t.PoolingSubmit(@UserCode2)
t.PoolingSubmit(@UserCode3)
Print " Sequence #a of 3 user thread functions fully submitted "
t.PoolingWait()
Print
Print " Sequence #a completed"
Print

t.PoolingSubmit(@UserCode4, @sb)
t.PoolingSubmit(@UserCode5)
t.PoolingSubmit(@UserCode6)
Print " Sequence #b of 3 user thread functions fully submitted "
t.PoolingWait(s())
Print
Print " Sequence #b completed"
Print

Print " List of returned values from sequence #b only"
For I As Integer = Lbound(s) To Ubound(s)
	Print "  " & I & ": " & s(I)
Next I
Print

Sleep
							%%Output example
								%%
 Sequence #a of 3 user thread functions fully submitted
  Sequence #a: 111111111122222222223333333333
 Sequence #a completed

 Sequence #b of 3 user thread functions fully submitted
  Sequence #b: 444444444455555555556666666666
 Sequence #b completed

 List of returned values from sequence #b only
  1: UserCode #4
  2: UserCode #5
  3: UserCode #6
								%%**Note:** If the first user thread procedure of each sequence starts very quickly, the acknowledgement message of each sequence of 3 submissions may appear inserted after the beginning of the text printed by the first user procedure of the sequence.
								That is not the case here.
								
		- **""ThreadInitThenMultiStart""** and **""ThreadPooling""** Types - Execution time gain checking with different multi-threading configurations:
				A user task is defined:
					**""-""** Display 64 characters (2*32) on the screen, each separated by an identical time achieved by a ##[For ... Next]## loop (no Sleep keyword so as not to free up CPU resources).
					**""-""** Depending on the number of threads chosen 1/2/4/8/16/32, this same user task is split in 1/2/4/8/16/32 sub-tasks, each being executed on a thread.
					
				Full code with the ""'ThreadInitThenMultiStart'"" and ""'ThreadPooling'"" Types:
					{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq13-3.bas"}}%%(freebasic)
Type ThreadInitThenMultiStart
	Public:
		Declare Constructor()
		Declare Sub ThreadInit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
		Declare Sub ThreadStart()
		Declare Sub ThreadStart(Byval p As Any Ptr)
		Declare Function ThreadWait() As String
		Declare Property ThreadState() As Ubyte
		Declare Destructor()
	Private:
		Dim As Function(Byval p As Any Ptr) As String _pThread
		Dim As Any Ptr _p
		Dim As Any Ptr _mutex1
		Dim As Any Ptr _mutex2
		Dim As Any Ptr _mutex3
		Dim As Any Ptr _pt
		Dim As Byte _end
		Dim As String _returnF
		Dim As Ubyte _state
		Declare Static Sub _Thread(Byval p As Any Ptr)
End Type

Constructor ThreadInitThenMultiStart()
	This._mutex1 = Mutexcreate()
	Mutexlock(This._mutex1)
	This._mutex2 = Mutexcreate()
	Mutexlock(This._mutex2)
	This._mutex3 = Mutexcreate()
	Mutexlock(This._mutex3)
End Constructor

Sub ThreadInitThenMultiStart.ThreadInit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
	This._pThread = pThread
	This._p = p
	If This._pt = 0 Then
		This._pt= Threadcreate(@ThreadInitThenMultiStart._Thread, @This)
		Mutexunlock(This._mutex3)
		This._state = 1
	End If
End Sub

Sub ThreadInitThenMultiStart.ThreadStart()
	Mutexlock(This._mutex3)
	Mutexunlock(This._mutex1)
End Sub

Sub ThreadInitThenMultiStart.ThreadStart(Byval p As Any Ptr)
	Mutexlock(This._mutex3)
	This._p = p
	Mutexunlock(This._mutex1)
End Sub

Function ThreadInitThenMultiStart.ThreadWait() As String
	Mutexlock(This._mutex2)
	Mutexunlock(This._mutex3)
	This._state = 1
	Return This._returnF
End Function

Property ThreadInitThenMultiStart.ThreadState() As Ubyte
	Return This._state
End Property

Sub ThreadInitThenMultiStart._Thread(Byval p As Any Ptr)
	Dim As ThreadInitThenMultiStart Ptr pThis = p
	Do
		Mutexlock(pThis->_mutex1)
		If pThis->_end = 1 Then Exit Sub
		pThis->_state = 2
		pThis->_returnF = pThis->_pThread(pThis->_p)
		pThis->_state = 4
		Mutexunlock(pThis->_mutex2)
	Loop
End Sub

Destructor ThreadInitThenMultiStart()
	If This._pt > 0 Then
		This._end = 1
		Mutexunlock(This._mutex1)
		.ThreadWait(This._pt)
	End If
	Mutexdestroy(This._mutex1)
	Mutexdestroy(This._mutex2)
	Mutexdestroy(This._mutex3)
End Destructor

'---------------------------------------------------

#include once "crt/string.bi"
Type ThreadPooling
	Public:
		Declare Constructor()
		Declare Sub PoolingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
		Declare Sub PoolingWait()
		Declare Sub PoolingWait(values() As String)
		Declare Property PoolingState() As Ubyte
		Declare Destructor()
	Private:
		Dim As Function(Byval p As Any Ptr) As String _pThread0
		Dim As Any Ptr _p0
		Dim As Function(Byval p As Any Ptr) As String _pThread(Any)
		Dim As Any Ptr _p(Any)
		Dim As Any Ptr _mutex
		Dim As Any Ptr _cond1
		Dim As Any Ptr _cond2
		Dim As Any Ptr _pt
		Dim As Byte _end
		Dim As String _returnF(Any)
		Dim As Ubyte _state
		Declare Static Sub _Thread(Byval p As Any Ptr)
End Type

Constructor ThreadPooling()
	Redim This._pThread(0)
	Redim This._p(0)
	Redim This._returnF(0)
	This._mutex = Mutexcreate()
	This._cond1 = Condcreate()
	This._cond2 = Condcreate()
	This._pt= Threadcreate(@ThreadPooling._Thread, @This)
End Constructor

Sub ThreadPooling.PoolingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
	Mutexlock(This._mutex)
	Redim Preserve This._pThread(Ubound(This._pThread) + 1)
	This._pThread(Ubound(This._pThread)) = pThread
	Redim Preserve This._p(Ubound(This._p) + 1)
	This._p(Ubound(This._p)) = p
	CondSignal(This._cond2)
	This._state = 1
	Mutexunlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait()
	Mutexlock(This._mutex)
	While This._state <> 4
		Condwait(This._Cond1, This._mutex)
	Wend
	Redim This._returnF(0)
	This._state = 0
	Mutexunlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait(values() As String)
	Mutexlock(This._mutex)
	While This._state <> 4
		Condwait(This._Cond1, This._mutex)
	Wend
	If Ubound(This._returnF) > 0 Then
		Redim values(1 To Ubound(This._returnF))
		For I As Integer = 1 To Ubound(This._returnF)
			values(I) = This._returnF(I)
		Next I
		Redim This._returnF(0)
	Else
		Erase values
	End If
	This._state = 0
	Mutexunlock(This._mutex)
End Sub

Property ThreadPooling.PoolingState() As Ubyte
	Return This._state
End Property

Sub ThreadPooling._Thread(Byval p As Any Ptr)
	Dim As ThreadPooling Ptr pThis = p
	Do
		Mutexlock(pThis->_mutex)
		If Ubound(pThis->_pThread) = 0 Then
			pThis->_state = 4
			CondSignal(pThis->_cond1)
			While Ubound(pThis->_pThread) = 0
				Condwait(pThis->_cond2, pThis->_mutex)
				If pThis->_end = 1 Then Exit Sub
			Wend
		End If
		pThis->_pThread0 = pThis->_pThread(1)
		pThis->_p0 = pThis->_p(1)
		If Ubound(pThis->_pThread) > 1 Then
			memmove(@pThis->_pThread(1), @pThis->_pThread(2), (Ubound(pThis->_pThread) - 1) * Sizeof(pThis->_pThread))
			memmove(@pThis->_p(1), @pThis->_p(2), (Ubound(pThis->_p) - 1) * Sizeof(pThis->_p))
		End If
		Redim Preserve pThis->_pThread(Ubound(pThis->_pThread) - 1)
		Redim Preserve pThis->_p(Ubound(pThis->_p) - 1)
		Mutexunlock(pThis->_mutex)
		Redim Preserve pThis->_ReturnF(Ubound(pThis->_returnF) + 1)
		pThis->_state = 2
		pThis->_returnF(Ubound(pThis->_returnF)) = pThis->_pThread0(pThis->_p0)
	Loop
End Sub

Destructor ThreadPooling()
	Mutexlock(This._mutex)
	This._end = 1
	CondSignal(This._cond2)
	Mutexunlock(This._mutex)
	.ThreadWait(This._pt)
	Mutexdestroy(This._mutex)
	Conddestroy(This._cond1)
	Conddestroy(This._cond2)
End Destructor

'---------------------------------------------------

Dim Shared As Double array(1 To 800000)  '' only used by the [For...Next] waiting loop in UserCode()

Function UserCode (Byval p As Any Ptr) As String
	Dim As String Ptr ps = p
	For I As Integer = 1 To 2
		Print *ps;
		For J As Integer = 1 To 800000
			array(I) = Tan(I) * Atn(I) * Exp(I) * Log(I)  '' [For...Next] waiting loop not freeing any CPU resource
		Next J
	Next I
	Return ""
End Function

Dim As String s(0 To 31)
For I As Integer = 0 To 15
	s(I) = Str(hex(I))
Next I
For I As Integer = 16 To 31
	s(I) = Chr(55 + I)
Next I

'---------------------------------------------------

Dim As ThreadInitThenMultiStart ts(0 To 31)
Dim As ThreadPooling tp(0 To 31)

'---------------------------------------------------

#macro ThreadInitThenMultiStartSequence(nbThread)
Scope
	Dim As Double t = Timer
	Print "   ";
	For I As Integer = 0 To 32 - nbThread Step nbThread
		For J As Integer = 0 To nbThread - 1
			ts(J).ThreadInit(@UserCode, @s(I + J))
			ts(J).ThreadStart()
		Next J
		For J As Integer = 0 To nbThread - 1
			ts(J).ThreadWait()
		Next J
	Next I
	t = Timer - t
	Print Using " : ####.## s"; t
End Scope
#endmacro

#macro ThreadPoolingSequence(nbThread)
Scope
	Dim As Double t = Timer
	Print "   ";
	For I As Integer = 0 To 32 - nbThread Step nbThread
		For J As Integer = 0 To nbThread - 1
			tp(J).PoolingSubmit(@UserCode, @s(I + J))
		Next J
	Next I
	For I As Integer = 0 To nbThread - 1
		tp(I).PoolingWait()
	Next I
	t = Timer - t
	Print Using " : ####.## s"; t
End Scope
#endmacro

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 1 secondary thread:"
ThreadInitThenMultiStartSequence(1)

Print "'ThreadPooling' with 1 secondary thread:"
ThreadPoolingSequence(1)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 2 secondary threads:"
ThreadInitThenMultiStartSequence(2)

Print "'ThreadPooling' with 2 secondary threads:"
ThreadPoolingSequence(2)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 4 secondary threads:"
ThreadInitThenMultiStartSequence(4)

Print "'ThreadPooling' with 4 secondary threads:"
ThreadPoolingSequence(4)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 8 secondary threads:"
ThreadInitThenMultiStartSequence(8)

Print "'ThreadPooling' with 8 secondary threads:"
ThreadPoolingSequence(8)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 16 secondary threads:"
ThreadInitThenMultiStartSequence(16)

Print "'ThreadPooling' with 16 secondary threads:"
ThreadPoolingSequence(16)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 32 secondary threads:"
ThreadInitThenMultiStartSequence(32)

Print "'ThreadPooling' with 32 secondary threads:"
ThreadPoolingSequence(32)
Print

Sleep
					%%Output example:
						%%
'ThreadInitThenMultiStart' with 1 secondary thread:
   00112233445566778899AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVV :    7.38 s
'ThreadPooling' with 1 secondary thread:
   00112233445566778899AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVV :    7.37 s

'ThreadInitThenMultiStart' with 2 secondary threads:
   01102332455476768998ABABCDDCEFEFHGHGIJJIKLKLMNMNPOPOQRRQSTSTUVUV :    3.88 s
'ThreadPooling' with 2 secondary threads:
   01012332454567678989ABABCDCDEFEFGHHGJIJILKLKNMNMPOPORQRQTSTSUVVU :    3.87 s

'ThreadInitThenMultiStart' with 4 secondary threads:
   012331204657564789ABA8B9CFDEFEDCGHJIHJGIKNLMMNLKOPQRPRQOSTUVUVTS :    2.11 s
'ThreadPooling' with 4 secondary threads:
   012303214567456789AB8A9BCEDFCEDFGIHJGIHJKMNLKMLNOQRPOQPRSUTVSUTV :    2.09 s

'ThreadInitThenMultiStart' with 8 secondary threads:
   01234567473651208A9BCEDFBA98FDECGHIJLKMNIHGJNKMLOPQSRTVUSPOQUVTR :    2.21 s
'ThreadPooling' with 8 secondary threads:
   0123456712306547A8B9FADCE9B8ECDIFMKGJHNIKJLGHMSQRLNPUOTVRQSPUVTO :    2.10 s

'ThreadInitThenMultiStart' with 16 secondary threads:
   0123456789ABCDEF5380AC4679BD12FEGHIKJLMNOPQRSUVTJNLGHIMOKPURVSTQ :    2.16 s
'ThreadPooling' with 16 secondary threads:
   0123465789ABCDEF3102658A97CB4HFJLMIGOEDNRSPKVQHJOIUGTPKRMNSLQUTV :    2.15 s

'ThreadInitThenMultiStart' with 32 secondary threads:
   0123457698ABDCEFGHIKLJNOMPRQSTVU05743218A69BFECDKHIGLJMTRPSVQOUN :    2.12 s
'ThreadPooling' with 32 secondary threads:
   0V23546789ACBDFEIHGJKMLNOPQRSTU1V3024C78D5F6B9AHGMLKJIEORSPQUT1N :    2.09 s
						%%**Note:** From a certain number of threads used, the gain in execution time becomes more or less constant (even slightly decreasing), which corresponds roughly to the number of threads the used CPU really has (4 in the case above).
						
		- **""ThreadInitThenMultiStart""** and **""ThreadPooling""** Types - **Warning** when using dynamic instances of such Types:
				When using dynamic instances of these types, their addresses should not be changed during their lifetimes, due to the associated internal thread that constantly accesses the data members from a pointer passed once to the thread beginning:
					**""-""** Thus, the use of ##Redim Preserve## or ##Reallocate## on such Type instances is prohibited, otherwise the program crashes.
					**""-""** One solution is to use dynamic pointers to such Type instances instead, so that the addresses of these pointers can be changed without their values changing.
					
		- **""ThreadDispatching""** Type, over-structure of **""ThreadPooling""** Type, dispatching user thread procedures over a given max number of secondary threads:
				- Principle:
						The maximum number of secondary threads that can be used is fixed when constructing the ""'ThreadDispatching'"" instance (1 secondary thread by default).
						""'ThreadDispatching'"" manages an internal dynamic array of pointers to ""'ThreadPooling'"" instances.
						
						If a secondary thread is available (already existing instance of ""'ThreadPooling'"" pending), it is used to submit the user thread procedure.
						Otherwise, a new secondary thread is created (new instance of ""'ThreadPooling'"" created) by respecting the number of secondary threads allowed.
						As long as all potential secondary threads are already in use, each new user thread procedure is distributed evenly over them.
						
				- Description:
						Methods:
							**""-""** Constructor : Construct a ""'ThreadDispatching'"" instance and set the number of usable secondary threads (1 by default).
							**""-""** ""DispatchingSubmit"" : Enter a user thread procedure in the queue of the "best" secondary thread among the usable ones.
							**""-""** ""DispatchingWait"" : Wait for the complete emptying of the queues of all secondary threads used (with all last user procedures executed).
							**""-""** ""DispatchingThread"" : Return the number of internal threads really started.
							**""-""** Destructor : Stop and complete the secondary threads used.
							
				- Example:
						Example of use of ""'ThreadDispatching'"" (whatever the allowed number of secondary threads, the submission sequence syntax is always the same):
							{{fbdoc item="filename" value="examples/manual/proguide/multithreading/criticalsectionfaq13-4.bas"}}%%(freebasic)
#include once "crt/string.bi"
Type ThreadPooling
	Public:
		Declare Constructor()
		Declare Sub PoolingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
		Declare Sub PoolingWait()
		Declare Sub PoolingWait(values() As String)
		Declare Property PoolingState() As Ubyte
		Declare Destructor()
	Private:
		Dim As Function(Byval p As Any Ptr) As String _pThread0
		Dim As Any Ptr _p0
		Dim As Function(Byval p As Any Ptr) As String _pThread(Any)
		Dim As Any Ptr _p(Any)
		Dim As Any Ptr _mutex
		Dim As Any Ptr _cond1
		Dim As Any Ptr _cond2
		Dim As Any Ptr _pt
		Dim As Byte _end
		Dim As String _returnF(Any)
		Dim As Ubyte _state
		Declare Static Sub _Thread(Byval p As Any Ptr)
End Type

Constructor ThreadPooling()
	Redim This._pThread(0)
	Redim This._p(0)
	Redim This._returnF(0)
	This._mutex = Mutexcreate()
	This._cond1 = Condcreate()
	This._cond2 = Condcreate()
	This._pt= Threadcreate(@ThreadPooling._Thread, @This)
End Constructor

Sub ThreadPooling.PoolingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
	Mutexlock(This._mutex)
	Redim Preserve This._pThread(Ubound(This._pThread) + 1)
	This._pThread(Ubound(This._pThread)) = pThread
	Redim Preserve This._p(Ubound(This._p) + 1)
	This._p(Ubound(This._p)) = p
	CondSignal(This._cond2)
	This._state = 1
	Mutexunlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait()
	Mutexlock(This._mutex)
	While This._state <> 4
		Condwait(This._Cond1, This._mutex)
	Wend
	Redim This._returnF(0)
	This._state = 0
	Mutexunlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait(values() As String)
	Mutexlock(This._mutex)
	While This._state <> 4
		Condwait(This._Cond1, This._mutex)
	Wend
	If Ubound(This._returnF) > 0 Then
		Redim values(1 To Ubound(This._returnF))
		For I As Integer = 1 To Ubound(This._returnF)
			values(I) = This._returnF(I)
		Next I
		Redim This._returnF(0)
	Else
		Erase values
	End If
	This._state = 0
	Mutexunlock(This._mutex)
End Sub

Property ThreadPooling.PoolingState() As Ubyte
	Return This._state
End Property

Sub ThreadPooling._Thread(Byval p As Any Ptr)
	Dim As ThreadPooling Ptr pThis = p
	Do
		Mutexlock(pThis->_mutex)
		If Ubound(pThis->_pThread) = 0 Then
			pThis->_state = 4
			CondSignal(pThis->_cond1)
			While Ubound(pThis->_pThread) = 0
				Condwait(pThis->_cond2, pThis->_mutex)
				If pThis->_end = 1 Then Exit Sub
			Wend
		End If
		pThis->_pThread0 = pThis->_pThread(1)
		pThis->_p0 = pThis->_p(1)
		If Ubound(pThis->_pThread) > 1 Then
			memmove(@pThis->_pThread(1), @pThis->_pThread(2), (Ubound(pThis->_pThread) - 1) * Sizeof(pThis->_pThread))
			memmove(@pThis->_p(1), @pThis->_p(2), (Ubound(pThis->_p) - 1) * Sizeof(pThis->_p))
		End If
		Redim Preserve pThis->_pThread(Ubound(pThis->_pThread) - 1)
		Redim Preserve pThis->_p(Ubound(pThis->_p) - 1)
		Mutexunlock(pThis->_mutex)
		Redim Preserve pThis->_ReturnF(Ubound(pThis->_returnF) + 1)
		pThis->_state = 2
		pThis->_returnF(Ubound(pThis->_returnF)) = pThis->_pThread0(pThis->_p0)
	Loop
End Sub

Destructor ThreadPooling()
	Mutexlock(This._mutex)
	This._end = 1
	CondSignal(This._cond2)
	Mutexunlock(This._mutex)
	.ThreadWait(This._pt)
	Mutexdestroy(This._mutex)
	Conddestroy(This._cond1)
	Conddestroy(This._cond2)
End Destructor

'---------------------------------------------------

Type ThreadDispatching
	Public:
		Declare Constructor(Byval nbMaxSecondaryThread As Integer = 1)
		Declare Sub DispatchingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
		declare Sub DispatchingWait()
		Declare Sub DispatchingWait(values() As String)
		Declare Property DispatchingThread() As Integer
		Declare destructor()
	Private:
		Dim As Integer _nbmst
		Dim AS Integer _dstnb
		Dim As ThreadPooling Ptr _tp(Any)
End Type

Constructor ThreadDispatching(Byval nbMaxSecondaryThread As Integer = 1)
	This._nbmst = nbMaxSecondaryThread
End Constructor

Sub ThreadDispatching.DispatchingSubmit(Byval pThread As Function(Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
	For I As Integer = 0 To Ubound(This._tp)
		If (This._tp(I)->PoolingState And 3) = 0 Then
			This._tp(I)->PoolingSubmit(pThread, p)
			Exit Sub
		End If
	Next I
	If Ubound(This._tp) < This._nbmst - 1 Then
		Redim Preserve This._tp(Ubound(This._tp) + 1)
		This._tp(Ubound(This._tp)) = New ThreadPooling
		This._tp(Ubound(This._tp))->PoolingSubmit(pThread, p)
	Elseif Ubound(This._tp) >= 0 Then
		This._tp(This._dstnb)->PoolingSubmit(pThread, p)
		This._dstnb = (This._dstnb + 1) Mod This._nbmst
	End If
End Sub

Sub ThreadDispatching.DispatchingWait()
	For I As Integer = 0 To Ubound(This._tp)
		This._tp(I)->PoolingWait()
	Next I
End Sub

Sub ThreadDispatching.DispatchingWait(values() As String)
	Dim As String s()
	For I As Integer = 0 To Ubound(This._tp)
		This._tp(I)->PoolingWait(s())
		If Ubound(s) >= 1 Then
			If Ubound(values) = -1 Then
				Redim Preserve values(1 To Ubound(values) + Ubound(s) + 1)
			Else
				Redim Preserve values(1 To Ubound(values) + Ubound(s))
			End If
			For I As Integer = 1 To Ubound(s)
				values(Ubound(values) - Ubound(s) + I) = s(I)
			Next I
		End If
	Next I
End Sub

Property ThreadDispatching.DispatchingThread() As Integer
	Return Ubound(This._tp) + 1
End Property

Destructor ThreadDispatching()
	For I As Integer = 0 To Ubound(This._tp)
		Delete This._tp(I)
	Next I
End Destructor

'---------------------------------------------------

Sub Prnt (Byref s As String, Byval p As Any Ptr)
	Dim As String Ptr ps = p
	If ps > 0 Then Print *ps;
	For I As Integer = 1 To 10
		Print s;
		Sleep 100, 1
	Next I
End Sub

Function UserCode1 (Byval p As Any Ptr) As String
	Prnt("1", p)
	Return "UserCode #1"
End Function

Function UserCode2 (Byval p As Any Ptr) As String
	Prnt("2", p)
	Return "UserCode #2"
End Function

Function UserCode3 (Byval p As Any Ptr) As String
	Prnt("3", p)
	Return "UserCode #3"
End Function

Function UserCode4 (Byval p As Any Ptr) As String
	Prnt("4", p)
	Return "UserCode #4"
End Function

Function UserCode5 (Byval p As Any Ptr) As String
	Prnt("5", p)
	Return "UserCode #5"
End Function

Function UserCode6 (Byval p As Any Ptr) As String
	Prnt("6", p)
	Return "UserCode #6"
End Function

Sub SubmitSequence(Byref t As ThreadDispatching, Byval ps As String Ptr)
	t.DispatchingSubmit(@UserCode1, ps)
	t.DispatchingSubmit(@UserCode2)
	t.DispatchingSubmit(@UserCode3)
	t.DispatchingSubmit(@UserCode4)
	t.DispatchingSubmit(@UserCode5)
	t.DispatchingSubmit(@UserCode6)
End Sub   

Dim As String sa = "  Sequence #a: "
Dim As String sb = "  Sequence #b: "
Dim As String sc = "  Sequence #c: "
Dim As String sd = "  Sequence #d: "
Dim As String se = "  Sequence #e: "
Dim As String sf = "  Sequence #f: "
Dim As String s()

Dim As ThreadDispatching t1, t2 = 2, t3 = 3, t4 = 4, t5 = 5, t6 = 6

Print " Sequence #a of 6 user thread functions dispatched over 1 secondary thread:"
SubmitSequence(t1, @sa)
t1.DispatchingWait()
Print
Print

Print " Sequence #b of 6 user thread functions dispatched over 2 secondary threads:"
SubmitSequence(t2, @sb)
t2.DispatchingWait()
Print
Print

Print " Sequence #c of 6 user thread functions dispatched over 3 secondary threads:"
SubmitSequence(t3, @sc)
t3.DispatchingWait()
Print
Print

Print " Sequence #d of 6 user thread functions dispatched over 4 secondary threads:"
SubmitSequence(t4, @sd)
t4.DispatchingWait()
Print
Print

Print " Sequence #e of 6 user thread functions dispatched over 5 secondary threads:"
SubmitSequence(t5, @se)
t5.DispatchingWait()
Print
Print

Print " Sequence #f of 6 user thread functions dispatched over 6 secondary threads:"
SubmitSequence(t6, @sf)
t6.DispatchingWait(s())
Print

Print "  List of returned values from sequence #f:"
For I As Integer = Lbound(s) To Ubound(s)
	Print "   " & I & ": " & s(I)
Next I

Sleep
							%%Output:
								%%
 Sequence #a of 6 user thread functions dispatched over 1 secondary thread:
  Sequence #a: 111111111122222222223333333333444444444455555555556666666666

 Sequence #b of 6 user thread functions dispatched over 2 secondary threads:
  Sequence #b: 122112121212122112213434344343344343344356566565565656565665

 Sequence #c of 6 user thread functions dispatched over 3 secondary threads:
  Sequence #c: 123123312321213132321231213321465654546465546546456654654564

 Sequence #d of 6 user thread functions dispatched over 4 secondary threads:
  Sequence #d: 134243211234432114322341413241233124413256655656566556655656

 Sequence #e of 6 user thread functions dispatched over 5 secondary threads:
  Sequence #e: 134255243141235325415143215234342511524343521251346666666666

 Sequence #f of 6 user thread functions dispatched over 6 secondary threads:
  Sequence #f: 534126216354456132241365563142421365316524245613361245365421
  List of returned values from sequence #f:
   1: UserCode #1
   2: UserCode #2
   3: UserCode #3
   4: UserCode #4
   5: UserCode #5
   6: UserCode #6
								%%
@@{{anchor name="ProPgMtCriticalSectionsFAQtop|Back to top"}}@@

{{fbdoc item="section" value="See also"}}
	- [[ProPgMultiThreading|Multi-Threading Overview]]
	- [[ProPgMtThreads|Threads]]
	- [[ProPgMtMutualExclusion|Mutual Exclusion]]
	- [[ProPgMtConditionalVariables|Conditional Variables]]
	- [[ProPgMtCriticalSections|Critical Sections]]

{{fbdoc item="back" value="CatPgProgrammer|Programmer's Guide"}}