Page 1 of 1

QuitThread() to safely quit a thread instead of killing it.

Posted: Sun Sep 13, 2009 4:47 pm
by Rescator
I really wish PureBasic had a loop check like Until QuitThread you know kinda like Until ForEver
And a QuitThread(thread) function that is similar to setting QuitThread to #True
But for now, the way I do it here kinda simulate such behaviour.
As to ThreadPriority, Maybe PB could do a ThreadPriority(#PB_Any,priority) for the thread to refer to itself?

There's a silly hack in this source, hence why I didn't post this in Tips'n'Tricks,
I guess I could have used a global list of threads, but this is just a proof of concept really.

Code: Select all

EnableExplicit
;Public Domain, created by Rescator.

Structure ThreadEx
  *thismemory
  ThreadID.i
  QuitThread.i
  Parameter.i
EndStructure

Procedure.i CreateThreadEx(*proc,param.i)
  Protected *ThreadEx.ThreadEx
  *ThreadEx=AllocateMemory(SizeOf(ThreadEx))
  If *ThreadEx
   *ThreadEx\QuitThread=#False
   *ThreadEx\Parameter=param
   *ThreadEx\ThreadID=CreateThread(*proc,*ThreadEx)
   If Not *ThreadEx\ThreadID
    FreeMemory(*ThreadEx)
    *ThreadEx=#Null
   EndIf
  EndIf
  ProcedureReturn *ThreadEx
EndProcedure

Procedure.i QuitThreadEx(*ThreadEx.ThreadEx,TimeOut.l=-1)
  Protected result.i
  If *ThreadEx
   If *ThreadEx=*ThreadEx\thismemory ;Silly "hack" to avoid double freeing and crashes etc.
    If IsThread(*ThreadEx\ThreadID)
     *ThreadEx\QuitThread=#True
     If TimeOut=-1
      result=WaitThread(*ThreadEx\ThreadID)
     Else
      result=WaitThread(*ThreadEx\ThreadID,TimeOut)
     EndIf
    Else
     result=#True
    EndIf
    If result
     *ThreadEx\thismemory=#Null
     FreeMemory(*ThreadEx)
    EndIf
   EndIf
  EndIf
  ProcedureReturn result
EndProcedure

Procedure.i ThreadPriorityEx(*ThreadEx.ThreadEx,Priority.i)
  Protected result.i
  result=ThreadPriority(*ThreadEx\ThreadID,Priority)
  ProcedureReturn result
EndProcedure

Procedure.i IsThreadEx(*ThreadEx.ThreadEx)
  Protected result.i
  ThreadPriorityEx(*ThreadEx,16) ;<-- a neat bonus feature huh?
  If *ThreadEx
    result=IsThread(*ThreadEx\ThreadID)
  EndIf
  ProcedureReturn result
EndProcedure

;Let's do a simple example to show how this works.
Procedure.i test(*ThreadEx.ThreadEx)
  Repeat
    Delay(1000)
    Debug "WooHoo!"
  Until *ThreadEx\QuitThread
EndProcedure
;If the thread itself need to quit use Break (preffered) or *ThreadEx\QuitThread=#True or similar methods.
;Remember to use QuitThread() later though to free the memory we allocated earlier etc.

Define event.i,quit.i,threadex.i
threadex=CreateThreadEx(@test(),#Null)

If OpenWindow(1,100,200,195,260,"PureBasic Window",#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget)
  Repeat
   event=WaitWindowEvent()
   If event=#PB_Event_CloseWindow
    quit=#True
   EndIf
  Until quit=#True
EndIf

QuitThreadEx(threadex)
;Look, no crash, no forced thread termination at program end, a very nicely behaved program here :)