Killing a thread is rather nasty as some may have noticed you might end up with mutexes still locked (will be autofreed at program end though, but mess up stuff while the program is still running), and various race condition issues since the thread unnaturally ended.
These two Extended ones gives a framework for you that allows signaling your threads that they should end, thus letting you handling cleanup and then a natural ending by having the thread reach the end of the procedure instead.
As a small bonus it is now possible with a timeout kill,
and by default a thread will not be forcefully killed, if you want the old behavior you can specify this by the force parameter.
As CreateThreadEx() and KillThreadEx() uses a pointer to a allocated structure instead of the threadid you can not use the other thread functions directly obviously.
But you can still get the threadid and use it with other thread functions
by using *thread\id as mentioned in the example below.
Ideally PureBasic's thread library should be updated to natively support a "quit" signal that you could check with a inline function/macro. Maybe something like ThreadQuit() which if used inside a thread returns #True or #False if the quit signal for the thread has been set.
This is basically what the "While *thread\running" is doing in the example below, but it would be cooler to use "While Not ThreadQuit()" right?

And most importantly a native implementation would not need these Ex variations.
Also note that the thread itself only reads the *thread\running flag,
only the create and kill actually changes it, so there is no possibility of a racing condition there either.
This code is placed in the Public Domain, enjoy!
Code: Select all
EnableExplicit
;---------------------------------------------------------------------------------------------------
Structure _ThreadMultipleInstance_struct_
id.i
*user
running.i
EndStructure
Procedure.i CreateThreadEx(*proc,*user=#Null)
Protected *thread._ThreadMultipleInstance_struct_
*thread=AllocateMemory(SizeOf(_ThreadMultipleInstance_struct_))
If *thread
*thread\id=CreateThread(*proc,*thread)
If *thread\id
*thread\running=#True
*thread\user=*user
Else
FreeMemory(*thread)
*thread=#False
EndIf
EndIf
ProcedureReturn *thread
EndProcedure
;Signal the thread to end (make sure your thread checks *thread\running).
;Optional timeout in milliseconds.
;Returns #True if the thread ends or is no longer running.
;Returns #False if the thread is still running.
;Make sure to use a timeout to avoid making your program wait forever in case of rogue threads that refuse to end.
Procedure.i KillThreadEx(*thread._ThreadMultipleInstance_struct_,timeout.l=#Null,force.i=#False)
Protected result=#False
If *thread
*thread\running=#False ;Signal the thread to end.
If force And timeout=0 ;Gives the thread a chance to end nicely, and after a 100ms timeout kill it if still running.
timeout=100 ;If a timeout is specified then the force/kill will use that value instead of this one.
EndIf
If timeout
result=WaitThread(*thread\id,timeout)
If force
If result=#False
KillThread(*thread\id)
result=#True
EndIf
EndIf
Else
result=WaitThread(*thread\id)
EndIf
If result
FreeMemory(*thread)
EndIf
EndIf
ProcedureReturn result
EndProcedure
;---------------------------------------------------------------------------------------------------
Procedure ThreadMultipleInstance_proc(*thread._ThreadMultipleInstance_struct_)
Debug "starting"
While *thread\running ;This is the magic, if this turns false our loop will end and the thread ends normally.
Debug *thread\user ;This user pointer/var is just a number here, but could also be a pointer to another structure as well.
Delay(1000) ;Wait before we loop again, unless it's a timecritical loop I'd advise at least 1ms delay here to save CPU.
Wend
Debug "ending"
EndProcedure
;---------------------------------------------------------------------------------------------------
;Start the thread:
Define *thread1
*thread1=CreateThreadEx(@ThreadMultipleInstance_proc(),42)
If *thread1
;Let's do some other stuff here.
Delay(1000) ;Hey, I'm too lazy to do anything fancy here.
;By now at least one value should have been printed to the debug window by the thread.
EndIf
;End the thread:
KillThreadEx(*thread1) ;Let's just tell the thread to end, and wait until it does.
;We could have used the optional timeout and the force flag as well,
;but since I know the simple test thread procedure won't ever hang, we won't do that in this example.
;The thread should react to the flag being changed and end,
;and also print to the debug window that it ended.
;To change thread priority or use other thread functions use *thread\id as the parameter for that.
;Do not use KillThread() use KillThreadEx() instead to avoid possible memory leaks during program run.
Also, this example should be fully portable with no need for changes at all.
EDIT 28th Oct 2008: Reworded a bit about mutexes in intro text.