Page 1 of 1

Simple dynamic threaded variables

Posted: Sat Nov 25, 2017 3:28 pm
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

Re: Simple dynamic threaded variables

Posted: Mon Nov 27, 2017 3:21 pm
by Kwai chang caine
Very usefull code, thanks a lot MaSRODster for sharing 8)

Re: Simple dynamic threaded variables

Posted: Tue Nov 28, 2017 9:41 am
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

Re: Simple dynamic threaded variables

Posted: Tue Nov 28, 2017 5:49 pm
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. :)