Author Topic: Threading  (Read 4702 times)

0 Members and 1 Guest are viewing this topic.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Threading
« Reply #15 on: May 06, 2021, 11:20:55 pm »
@justsomeguy You got me seriously interested in this topic so I decided to whip up some WinAPI QB64 code based on the example on the MSDN page on threading here. Below is the code that will run three threads in the same executable.

A header you will need, save it as threadwin.h
Code: C++: [Select]
  1. #include<strsafe.h>
  2. int32 FUNC_MYTHREADFUNCTION(ptrszint*_FUNC_MYTHREADFUNCTION_OFFSET_LPPARAM);
  3. extern "C"{
  4.         __declspec(dllexport) int32 MyThreadFunction(ptrszint*lpParam){
  5.                 return FUNC_MYTHREADFUNCTION((lpParam));
  6.         }
  7. }
  8.  
  9. int32 sizeoftchar(){
  10.         return sizeof(TCHAR);
  11. }

Code: QB64: [Select]
  1.  
  2. Type MyData
  3.     As Long val1, val2
  4.  
  5. Const BUF_SIZE = 255
  6. Const MAX_THREADS = 20
  7. Const HEAP_ZERO_MEMORY = &H00000008
  8. Const INFINITE = 4294967295
  9. Const STD_OUTPUT_HANDLE = -11
  10. Const INVALID_HANDLE_VALUE = -1
  11.  
  12. Const MB_OK = 0
  13.  
  14. Const FORMAT_MESSAGE_ALLOCATE_BUFFER = &H00000100
  15. Const FORMAT_MESSAGE_FROM_SYSTEM = &H00001000
  16. Const FORMAT_MESSAGE_IGNORE_INSERTS = &H00000200
  17. Const LANG_NEUTRAL = &H00
  18. Const SUBLANG_DEFAULT = &H01
  19.  
  20. Const LMEM_ZEROINIT = &H0040
  21.  
  22.     Function LoadLibrary%& (lpLibFileName As String)
  23.     Function GetProcAddress%& (ByVal hModule As _Offset, lpProcName As String)
  24.     Function FreeLibrary%% (ByVal hLibModule As _Offset)
  25.     Sub FreeLibrary (ByVal hLibModule As _Offset)
  26.     Function GetLastError& ()
  27.     Function HeapAlloc%& (ByVal hHeap As _Offset, Byval dwFlags As Long, Byval dwBytes As _Offset)
  28.     Function GetProcessHeap%& ()
  29.     Sub ExitProcess (ByVal uExitCode As _Unsigned Long)
  30.     Function CreateThread%& (ByVal lpThreadAttributes As _Offset, Byval dwStackSize As _Offset, Byval lpStartAddress As _Offset, Byval lpParameter As _Offset, Byval dwCreationFlags As Long, Byval lpThreadId As _Offset)
  31.     Function WaitForMultipleObjects& (ByVal nCount As Long, Byval lpHandles As _Offset, Byval bWaitAll As _Byte, Byval dwMilliseconds As Long)
  32.     Sub WaitForMultipleObjects (ByVal nCount As Long, Byval lpHandles As _Offset, Byval bWaitAll As _Byte, Byval dwMilliseconds As Long)
  33.     Function CloseHandle%% (ByVal hObject As _Offset)
  34.     Sub CloseHandle (ByVal hObject As _Offset)
  35.     Function HeapFree%% (ByVal hHeap As _Offset, Byval dwFlags As Long, Byval lpMem As _Offset)
  36.     Sub HeapFree (ByVal hHeap As _Offset, Byval dwFlags As Long, Byval lpMem As _Offset)
  37.     Sub StringCchPrintf Alias "StringCchPrintfA" (ByVal pszDest As _Offset, Byval cchDest As _Offset, byvalpszFormat As String, Byval arg1 As Long, Byval arg2 As Long)
  38.     Sub StringCchPrintf2 Alias "StringCchPrintfA" (ByVal pszDest As _Offset, Byval cchDest As _Offset, pszFormat As String, lpszFunction As String, Byval error As Long, Byval lpMsgBuf As _Offset)
  39.     Sub StringCchLength Alias "StringCchLengthA" (ByVal psz As _Offset, Byval cchMax As _Offset, Byval pcchLength As _Offset)
  40.     Function GetStdHandle%& (ByVal nStdHandle As Long)
  41.     Function CreateMutex%& Alias "CreateMutexA" (ByVal lpMutexAttributes As _Offset, Byval bInitialOwner As Long, Byval lpName As _Offset)
  42.     Sub WriteConsole (ByVal hConsoleOutput As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfCharsToWrite As Long, Byval lpNumberOfCharsWritten As _Offset, Byval lpReserved As _Offset)
  43.     Sub FormatMessage Alias FormatMessageA (ByVal dwFlags As Long, Byval lpSource As Long, Byval dwMessageId As Long, Byval dwLanguageId As Long, Byval lpBuffer As _Offset, Byval nSize As Long, Byval Arguments As _Offset)
  44.     Sub MessageBox Alias "MessageBoxA" (ByVal hWnd As _Offset, Byval lpText As _Offset, lpCaption As String, Byval uType As _Unsigned Long)
  45.     Sub LocalFree (ByVal hMem As _Offset)
  46.     Function LocalAlloc%& (ByVal uFlags As _Unsigned Long, Byval uBytes As _Unsigned _Offset)
  47.     Function lstrlen& Alias "lstrlenA" (ByVal lpString As _Offset)
  48.     Function LocalSize%& (ByVal hMem As _Offset)
  49.     Sub SetLastError (ByVal dwError As Long)
  50.  
  51. Declare Library "threadwin"
  52.     Function sizeoftchar& ()
  53.  
  54.     Function MAKELANGID& (ByVal p As Long, Byval s As Long)
  55.  
  56. Dim As _Offset libload: libload = LoadLibrary(Command$(0))
  57. Dim As _Offset MyThreadFunc: MyThreadFunc = GetProcAddress(libload, "MyThreadFunction")
  58.  
  59. Dim As MyData pDataArray(1 To MAX_THREADS)
  60. Dim As Long dwThreadIdArray(1 To MAX_THREADS)
  61. Dim As _Offset hThreadArray(1 To MAX_THREADS), heap(1 To MAX_THREADS)
  62.  
  63. Dim As _Offset ghMutex: ghMutex = CreateMutex(0, 0, 0)
  64. If ghMutex = 0 Then
  65.     ErrorHandler "CreateMutex"
  66. For i = 1 To MAX_THREADS
  67.     heap(i) = HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, Len(pDataArray(i)))
  68.     Dim As _MEM pdata: pdata = _MemNew(8)
  69.     _MemPut pdata, pdata.OFFSET, heap(i)
  70.     _MemGet pdata, pdata.OFFSET, pDataArray(i)
  71.     If heap(i) = 0 Then
  72.         ExitProcess 2
  73.     End If
  74.     pDataArray(i).val1 = i
  75.     pDataArray(i).val2 = i + 100
  76.     hThreadArray(i) = CreateThread(0, 0, MyThreadFunc, _Offset(pDataArray(i)), 0, _Offset(dwThreadIdArray(i)))
  77.     If hThreadArray(i) = 0 Then
  78.         ErrorHandler "CreateThread"
  79.         ExitProcess 3
  80.     End If
  81. WaitForMultipleObjects MAX_THREADS, _Offset(hThreadArray()), 1, INFINITE
  82. For i = 1 To MAX_THREADS
  83.     CloseHandle hThreadArray(i)
  84.     If heap(i) <> 0 Then
  85.         HeapFree GetProcessHeap, 0, heap(i)
  86.     End If
  87. CloseHandle ghMutex
  88. FreeLibrary libload
  89.  
  90. Function MyThreadFunction& (lpParam As _Offset)
  91.     Dim As String * BUF_SIZE msgBuf
  92.     Dim As _Offset hStdout
  93.     Dim As Long cchStringSize, dwChars
  94.     Dim As MyData MyData
  95.     hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
  96.     If hStdout = INVALID_HANDLE_VALUE Then
  97.         MyThreadFunction = 1
  98.     End If
  99.     Dim As _MEM PMYDATA: PMYDATA = _MemNew(8)
  100.     _MemPut PMYDATA, PMYDATA.OFFSET, lpParam
  101.     _MemGet PMYDATA, PMYDATA.OFFSET, MyData
  102.     StringCchPrintf _Offset(msgBuf), BUF_SIZE, "Parameters = %d, %d" + Chr$(10) + Chr$(0), MyData.val1, MyData.val2
  103.     StringCchLength _Offset(msgBuf), BUF_SIZE, _Offset(cchStringSize)
  104.     WriteConsole hStdout, _Offset(msgBuf), cchStringSize, _Offset(dwChars), 0
  105.     MyThreadFunction = 0
  106.  
  107. Sub ErrorHandler (lpszFunction As String)
  108.     Dim As _Offset lpMsgBuf, lpDisplayBuf
  109.     Dim As Long dw: dw = GetLastError
  110.     FormatMessage FORMAT_MESSAGE_ALLOCATE_BUFFER Or FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, 0, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), _Offset(lpMsgBuf), 0, 0
  111.     lpDisplayBuf = LocalAlloc(LMEM_ZEROINIT, (lstrlen(lpMsgBuf) + lstrlen(_Offset(lpszFunction)) + 40) * sizeoftchar)
  112.     StringCchPrintf2 lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeoftchar, "%s failed with error %d:" + Chr$(10) + " %s" + Chr$(0), lpszFunction + Chr$(0), dw, lpMsgBuf
  113.     MessageBox 0, lpDisplayBuf, "Error" + Chr$(0), MB_OK
  114.     LocalFree lpMsgBuf
  115.     LocalFree lpDisplayBuf

And a screenshot of the output:
  [ You are not allowed to view this attachment ]

Edit: For a bit of fun, increase the MAX_THREADS constant to a higher number. I've tried 20
« Last Edit: May 08, 2021, 09:09:46 pm by SpriggsySpriggs »
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Threading
« Reply #16 on: May 06, 2021, 11:31:06 pm »
If you aren't already, please join the Discord. I would love to collaborate with you on more similar projects. It's been a long time since I've been this excited about API programming. Excited enough that I pulled out my work laptop and coded this one while laying in a hotel bed. I NEVER touch my work laptop when I'm at a hotel.
Shuwatch!

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: Threading
« Reply #17 on: May 07, 2021, 12:10:03 am »
@SpriggsySpriggs Very impressive. I'm glad that I could get you fired up!

 Have you tried bench-marking it? How's the stability? I'm surprised that the 'print' statement isn't core dumping on you.

Anyway, I am on discord, and I don't mind collaborating, but I think your programming skill is several orders magnitude greater than mine.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Threading
« Reply #18 on: May 07, 2021, 10:42:40 am »
I had to edit my post above with the thread safe functions for printing. I was noticing small issues with launching and running. This should behave a bit better. You were correct about the printing causing an issue. So, this new printing is far safer (from what I can tell)
« Last Edit: May 07, 2021, 10:50:41 am by SpriggsySpriggs »
Shuwatch!