Announcement

Collapse
No announcement yet.

DLL question

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Lance Edmonds
    replied
    The pointer to a block of memory that holds all of your "global" data is the best way to work with a single LONG parameter that you can pass to your thread.

    However, one word of advice that will save you a lot of time debugging very complex and seemingly random GPF's:

    If you are going to modify any GLOBAL variable across multiple threads then you MUST (earn how to) use synchronization objects - the rule is simple: if the variable is global across threads (or the data it points to is global across threads), use a critical section when changing any part of the variable or target memory. Failure to do so will result in GPF's when Windows forces a context-switch when data is only _partially_ updated - in effect the data is "undefined" - until the context switches back to finish the memory update. If another thread accesses this data while it is "undefined", you are asking for trouble.

    This is too long to describe here, but "Win32 Programming" discusses this in-depth - do yourself a favor and learn this stuff - ISBN's are in the FAQ forum.

    Threading is powerful - but it requires careful app design and implementation.

    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Guest's Avatar
    Guest replied
    Before I start confusing you even more, I suggest reading the walkthrough/tutorial about threads that is in the "Tech Notes" section of PowerBasic's website.

    For starters, we create new threads because we do not want to wait around for the function to return. So that means "g_Result" is only the HANDLE to the newly created thread.
    The result wont appear till that thread is done. You have to program as if that thread may return instantly or take 100 years. It's what makes multithreaded programming so much fun.
    There are two ways of finding out when the thread is done. We can sit & stare at THREAD STATUS till it changes from 259. Or we can let the thread post a message to a window we have open or trigger an event object. It's your choice.

    Remember this: Globals live in the heap. Local variables live in the stack.
    Windows only lets you pass a single LONG integer to the newly created function. Until the THREAD CLOSE, the function's return value is waiting for you at THREAD STATUS. Again, this can only be a single long integer. The best way to do this is to use a User Defined Type as dorian suggested, but instead of creating it as a global, we create it locally on the stack. Then before we create the thread, we move the UDT onto the heap and get a pointer aimed at the UDT on the heap. We then create the thread and pass the pointer. The thread creates a local UDT in its stack. Then it copies the UDT from the pointer location to the local UDT. Modifies the local UDT, and copies the local UDT up into the UDT on the heap. This sounds worse than it really is.
    Code:
    #INCLUDE "WIN32API.INC"
    TYPE ThreadInfoType ' also known as a UDT
      ID AS LONG
      X AS LONG
      St AS ASCIIZ * 60
    END TYPE
    FUNCTION One(BYVAL pThreadInfo AS DWORD) AS LONG
      DIM lThreadInfo AS ThreadInfoType
      DIM lpThreadInfo AS ThreadInfoType PTR
      lpThreadInfo = pThreadInfo ' aim at the passed heap location
      lThreadInfo = @lpThreadInfo ' its better to copy into local stack for editing
      lThreadInfo.St = MID$(lThreadInfo.St,lThreadInfo.X,4) ' modify
      @lpThreadInfo = lThreadInfo ' Copy back into heap
      FUNCTION = 1 ' All local variables will be lost. Only the heap & this function return will survive.
    END FUNCTION
    FUNCTION PBMAIN() AS LONG
      LOCAL hThread AS LONG, r AS LONG
      LOCAL TestTI AS ThreadInfoType ' a UDT containing important info for our thread
      LOCAL pTestTI AS ThreadInfoType PTR ' a UDT pointer
      TestTI.ID = 1024
      TestTI.St = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      TestTI.X = 5
      pTestTI = HeapAlloc(GetProcessHeap,0,LEN(TestTI))
      @pTestTI = TestTI ' the @ sign means to use the UDT that the pointer is aiming at
      THREAD CREATE One(pTestTI) TO hThread
      DO
        THREAD STATUS hThread TO r ' r will get the FUNCTION return value
      LOOP UNTIL r = 1 ' this is a bad way to wait for a thread to finish
      THREAD CLOSE hThread TO r ' cleanup
      TestTI = @pTestTI ' the heap version needs to be copied back first
      HeapFree GetProcessHeap, 0, pTestTI  ' dont forget to clean up!
      MSGBOX TestTI.ST
    END FUNCTION
    Caveats:
    1. variable PTRs are DWORDS with a special @ power
    2. CREATE THREAD passes variables by value. either write your thread functions with byval or use varptr() when passing pointers around.
    3. Dynamic strings & arrays are not supported in PB's UDTs. It's probably for the better when you get in situations like this. It requires quite a mess of extra copying in/out of heap and its just best to avoid them till you get the hang of this.


    ------------------

    Leave a comment:


  • Guest's Avatar
    Guest replied
    Hi there,
    I've recently done a multithreaded DLL which needed multiple paramaters. What I did was to export a function which had all the params, assign these params to global variables, and then call the threaded function. The threaded function then reads the global variables, and does its processing.

    I found the best way was to declare a UDT with enough elements to account for all the parameters, populate it, and then let the called thread use it, e.g.:

    TYPE UDT1
    Param1 as string
    Param2 as string
    END TYPE

    GLOBAL uMyUDT AS UDT1

    FUNCTION F1 (Param1 as string, Param2 as string) EXPORT AS LONG

    uMyUDT.Param1 = Param1
    uMyUDT.Param2 = Param2

    THREAD CREATE InitThread(id) to lRet

    END FUNCTION

    FUNCTION InitThread(id as LONG) as LONG

    MSGBOX uMyUDT.Param1
    MSGBOX uMyUDT.Param2

    END FUNCTION

    Note that the exported function will create the thread, and exit immediately, without waiting for the threaded function to finish.

    To wait for it, I use the THREAD STATUS command (there's an example in the help) to wait around until the thread finishes. The thread could put its result into another element of the UDT, and the function F1 then returns this.

    Using arrayed UDT's allows you to sporn multiple threads, and then wait until all have finished, then return the collective results.

    Dorian.


    ------------------

    Leave a comment:


  • Scott Turchin
    started a topic DLL question

    DLL question

    If I have ONE function in my DLL that uses say 5 others, and does a Thread Create InitThread(id) To g_Result for example, I can't pass parameters to it.
    How do I make global variables in the DLL from the one function?

    I've BEEN doing it like this:

    Function One(Param as String) Export As Long
    Thread Create InitThread(id) To g_Result
    Function = g_Result
    End Function


    OK That works great, but what if I need to do this with a parameter between the functions?
    Thread create only allows one long integer parameter...

    Do I need to dupliate my values?

    Ie, in teh DLL
    DLL_St as string

    Function One(St as String) Export as long
    Thread Create InitThread(id) To g_Result
    Function = g_Result
    End Function

    Function InitThread(ByVal x As Long) Export As Long
    St = Mid$(St,1,4) 'or whatever
    End Function


    Is this St the same St that will be returned to the original function?

    I'm slightly confused as to how the variables interact, my ASSUMPTION is that this is a new value of St and I will get a compile error if i do not declare it in the Thread function


    Scott

    -------------
    Scott
    mailto:[email protected][email protected]</A>
Working...
X