Simple dynamic threaded variables

Share your advanced PureBasic knowledge/code with the community.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Simple dynamic threaded variables

Post by srod »

Hi,

just a pretty crude snippet here as I was (not for the first time!) in need of the ability to dynamically create some threaded variables to be embedded within structures and the like and valid on all platforms.

More specifically, an app I am working on can have lots of instances of a certain structure kicking around, each of which can be accessed by multiple threads and I required a couple of fields in each structure variable to be 'threaded'. That is, for a given instance of the structure, whilst most fields remain the same for every thread, a couple of them can hold values unique to whichever thread is accessing them etc.

Loads of workarounds for this, but, well, here's mine! If someone finds it useful then, great. If not, that's ok as well! :)

Allows for integer values only, but easily modified to take any kind of value.

Code: Select all

CompilerIf Defined(INCLUDE_threadStorage, #PB_Constant)=0
#INCLUDE_threadStorage=1

;/////////////////////////////////////////////////////////////////////////////////
;threadStorage.
;==============
;
;   Created with Purebasic 5.60 for Windows.
;
;   Platforms:  ALL.
;/////////////////////////////////////////////////////////////////////////////////

;/////////////////////////////////////////////////////////////////////////////////
;NOTES.
;
;   i)    Dynamic creation of thread local storage.
;   ii)   The creation and deletion functions CreateThreadStorage()/DeleteThreadStorage() should really only be invoked from the main process.
;         If they might be invoked from multiple threads then Mutex protection should be employed.
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;-MACROS.
;========
  ;The following allows us to define dynamic variables of type 'ThreadStorageIndex' for convenience.
    Macro ThreadStorageIndex
      i
    EndMacro
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;-CONSTANTS and STRUCTURES.
;==========================
  ;The following constant determines how many possible indexes can be in use at any given time (this does not limit the number of threads).
  ;Change to a suitable value as appropriate.
    #THREADSTORAGE_MAXNUMUSEDINDEXES = 1000
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;-GLOBAL and THREADED VARIABLES.
;===============================
  Global Dim threadStorage_gUsedIndexes.i(#THREADSTORAGE_MAXNUMUSEDINDEXES)
  Threaded Dim threadStorage_tIndexes.i(#THREADSTORAGE_MAXNUMUSEDINDEXES)
;/////////////////////////////////////////////////////////////////////////////////


;-FUNCTIONS.

;/////////////////////////////////////////////////////////////////////////////////
;The following creates a new thread storage index.
;Returns 0 if an error such as no more indexes available.
Procedure.i CreateThreadStorage()
  Protected index, i
  ;Need to find the first free index.
  For i = 1 To #THREADSTORAGE_MAXNUMUSEDINDEXES
    If threadStorage_gUsedIndexes(i) = 0
      ;Mark it as used.    
        threadStorage_gUsedIndexes(i) = 1
      index = i
      Break
    EndIf
  Next
  ProcedureReturn index
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;The following frees a thread storage index for reuse.
Procedure DeleteThreadStorage(index)
  If index >0 And index <= #THREADSTORAGE_MAXNUMUSEDINDEXES
    threadStorage_tIndexes(index) = 0
    threadStorage_gUsedIndexes(index) = 0
  EndIf
  ProcedureReturn index
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;The following retrieves the value associated with the given 1-based thread storage index for the current thread.
Procedure.i GetThreadStorageValue(index)
  If index >0 And index <= #THREADSTORAGE_MAXNUMUSEDINDEXES
    ;Only proceed if the index is in use.
    If threadStorage_gUsedIndexes(index)
      ProcedureReturn threadStorage_tIndexes(index)
    EndIf
  EndIf
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;The following sets a value against the given 1-based thread storage index for the current thread.
Procedure SetThreadStorageValue(index, value)
  If index >0 And index <= #THREADSTORAGE_MAXNUMUSEDINDEXES
    ;Only proceed if the index is in use.
    If threadStorage_gUsedIndexes(index)
      threadStorage_tIndexes(index) = value
    EndIf
  EndIf
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////

CompilerEndIf
Quick demo.

Code: Select all

;DEMO.
;=====
Global myTS.ThreadStorageIndex 

Procedure thread(param)
  ;Set the value of our thread storage object for this thread.
    SetThreadStorageValue(myTS, param*10)
  For i = 1 To 100
    Debug "Thread " + Str(param) + ", threadStorage value = " + Str(GetThreadStorageValue(myTS))
    Delay(10)  
  Next
EndProcedure


myTS = CreateThreadStorage()
If myTS
  ;Set the value of our thread storage object for the main process.
    SetThreadStorageValue(myTS, 1)
  ;Create a few threads.
    th1 = CreateThread(@thread(), 1)
    th2 = CreateThread(@thread(), 2)
    th3 = CreateThread(@thread(), 3)
    
  Repeat 
    Debug "Main process, threadStorage value = " + Str(GetThreadStorageValue(myTS))
    Delay(10)  
  Until IsThread(th1) = 0 And IsThread(th2) = 0 And IsThread(th3) = 0
  DeleteThreadStorage(myTS)
EndIf
I may look like a mule, but I'm not a complete ass.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5348
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Simple dynamic threaded variables

Post by Kwai chang caine »

Very usefull code, thanks a lot MaSRODster for sharing 8)
ImageThe happiness is a road...
Not a destination
QuimV
Enthusiast
Enthusiast
Posts: 337
Joined: Mon May 29, 2006 11:29 am
Location: BARCELONA - SPAIN

Re: Simple dynamic threaded variables

Post by QuimV »

I have tested it and I have the error:

[09:35:11] Waiting for executable to start...
[09:35:11] Executable type: Windows - x86 (32bit, Unicode)
[09:35:11] Executable started.
[09:35:11] The Program execution has finished.
[09:36:09] Waiting for executable to start...
[09:36:09] Executable type: Windows - x86 (32bit, Unicode)
[09:36:09] Executable started.
[09:36:10] [ERROR] Line: 123
[09:36:10] [ERROR] Invalid memory access. (read error at address 21374186)

:oops:

Then, I have activated the "Create threadsafe executable" switch and it works fine

:D

Thanks @srod for sharing
QuimV
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Simple dynamic threaded variables

Post by srod »

QuimV wrote:Then, I have activated the "Create threadsafe executable" switch and it works fine

:D
Yes the demo requires the threadsafe switch since the thread uses strings. I should have made that clear. :)
I may look like a mule, but I'm not a complete ass.
Post Reply