CreateThreadEx() and KillThreadEx() safe quitting of threads

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

CreateThreadEx() and KillThreadEx() safe quitting of threads

Post by Rescator »

CreateThreadEx() and KillThreadEx() are two alternatives/extensions to how CreateThread() and KillThread() works.

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? :P
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.
PS! I know it's possible to message a thread using windows messages, but the example above has way less overhead. (look how tight that While loop is)
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.
Last edited by Rescator on Tue Oct 28, 2008 4:47 am, edited 3 times in total.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

This is great. Thanks for sharing.

cheers
User avatar
idle
Always Here
Always Here
Posts: 5917
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

Is a good tip thanks
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Thank you Rescator 8)
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
Post Reply