• CreateThread vs. CreateVOThread

    From Robert Pope@21:1/5 to All on Tue Aug 3 03:56:39 2021
    Hi All.

    I'm wondering if anyone knows what the differences between Win32 API CreateThread() vs. VO's CreateVOThread() are? Mostly with relation to the garbage collector. I.e. if I use Win32 API's CreateThread() would I completely avoid the garbage collector
    running on my thread. Is there a reason why I would still be better off using CreateVOThread()?

    I understand that threads and dynamic memory / the garbage collector don't play well together. I have in my ThreadFunc avoided declaring any LOCAL's and avoided calling any VO functions (because they might also have LOCAL's). There are also no anonymous
    variables like a string concatenation. Where I can't avoid a variable I use a structure (ThreadData) in static memory. The thread is the only reader/writer to this structure.

    Assuming I haven't missed anything, my thread is only working in static memory and only calling Windows API functions.

    The thread itself is pretty simple. It calls Win32 API IsHungAppWindow() once per second and logs an event in the windows event log if that function returns true.

    I'm not sure how much I trust VO's threading (or my code not to have missed some dynamic memory allocation somewhere) so I've also included runtime options to completely disable this feature if it plays up.

    Cheers!
    Rob.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jamal@21:1/5 to Robert Pope on Tue Aug 3 12:05:11 2021
    According to the docs the CreateThread() and CreateVOThread() are the same: However, "CreateVOThread() also performs some additional initializations
    in the kernel runtime."

    But it does not say what those initializations! But I guess it has to do
    with the GC suspension during its processing.

    Additionally, the docs say (Just in case you did not see it and for others):

    CreateVOThread() and ExitVOThread()

    "First of all, threads in a Visual Objects application should not be
    created by calling the Windows API function CreateThread() directly, but
    by calling the Visual Objects runtime function CreateVOThread(). This
    function is called exactly like CreateThread() and actually it calls CreateThread() internally. CreateVOThread() also performs some
    additional initializations in the kernel runtime.

    Similar to CreateVOThread() there is an ExitVOThread() function that
    should be called instead of ExitThread(). However, try to avoid exiting
    a thread in a Visual Objects application by calling ExitThread() at all. Instead, exit your threads by returning from the thread entry point
    function."

    And the docs go on further:

    "When multiple threads are running, each dynamic memory pointer becomes
    a shared resource. One thread might load a pointer to a string into a
    register. (The GC can only update memory locations, not registers.)
    Before actually performing an operation with that pointer, execution is transferred to another thread that triggers the GC. Now the string
    referred to by the register in the first thread is moved, and when
    execution returns to the first thread, the pointer in the register is
    not valid anymore."



    "The problem described above will be resolved in a future release of
    Visual Objects, but right now the programmer has to be aware of it."

    (I don't think this was ever resolved, and I guess will never be)


    "There are two ways of avoiding problems with threads and dynamic data.
    The first solution is to disable garbage collection when more than one
    thread is active and enabling it only again after all threads but one
    finish. This can be done through the Visual Objects runtime functions
    DynLock() and DynUnlock(). The disadvantage of this approach is that no
    more memory will be freed or reused while the DynLock() is active.
    Depending on the application logic, memory consumption might increase
    rapidly. Even when the collector runs again, the dynamic memory pool
    will stay at the maximum size for future reuse. You might reduce the
    size of the dynamic memory pool manually by calling DynShrink().

    Disabling the collector globally while a thread is running, is a good
    solution when threads only run for a short while and do not generate a
    lot of dynamic data garbage. Indexing a database produces a lot of
    dynamic data garbage, so that indexing in a thread is not recommended
    for bigger databases.

    To avoid the collector problem, the solution is not to use dynamic data
    at all inside a thread. Another solution is to minimize the use of
    dynamic data to very few places, so that DynLock() and DynUnlock() can
    be used locally. The Web server that will be presented later is a good
    example for this technique."


    On 8/3/2021 6:56 AM, Robert Pope wrote:
    Hi All.

    I'm wondering if anyone knows what the differences between Win32 API CreateThread() vs. VO's CreateVOThread() are? Mostly with relation to the garbage collector. I.e. if I use Win32 API's CreateThread() would I completely avoid the garbage collector
    running on my thread. Is there a reason why I would still be better off using CreateVOThread()?

    I understand that threads and dynamic memory / the garbage collector don't play well together. I have in my ThreadFunc avoided declaring any LOCAL's and avoided calling any VO functions (because they might also have LOCAL's). There are also no
    anonymous variables like a string concatenation. Where I can't avoid a variable I use a structure (ThreadData) in static memory. The thread is the only reader/writer to this structure.

    Assuming I haven't missed anything, my thread is only working in static memory and only calling Windows API functions.

    The thread itself is pretty simple. It calls Win32 API IsHungAppWindow() once per second and logs an event in the windows event log if that function returns true.

    I'm not sure how much I trust VO's threading (or my code not to have missed some dynamic memory allocation somewhere) so I've also included runtime options to completely disable this feature if it plays up.

    Cheers!
    Rob.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Pope@21:1/5 to Jamal on Tue Aug 3 10:07:23 2021
    On Wednesday, August 4, 2021 at 2:05:19 AM UTC+10, Jamal wrote:
    According to the docs the CreateThread() and CreateVOThread() are the same: However, "CreateVOThread() also performs some additional initializations
    in the kernel runtime."

    But it does not say what those initializations! But I guess it has to do with the GC suspension during its processing.

    Additionally, the docs say (Just in case you did not see it and for others):

    CreateVOThread() and ExitVOThread()

    "First of all, threads in a Visual Objects application should not be
    created by calling the Windows API function CreateThread() directly, but
    by calling the Visual Objects runtime function CreateVOThread(). This function is called exactly like CreateThread() and actually it calls CreateThread() internally. CreateVOThread() also performs some
    additional initializations in the kernel runtime.

    Similar to CreateVOThread() there is an ExitVOThread() function that
    should be called instead of ExitThread(). However, try to avoid exiting
    a thread in a Visual Objects application by calling ExitThread() at all. Instead, exit your threads by returning from the thread entry point function."

    And the docs go on further:

    "When multiple threads are running, each dynamic memory pointer becomes
    a shared resource. One thread might load a pointer to a string into a register. (The GC can only update memory locations, not registers.)
    Before actually performing an operation with that pointer, execution is transferred to another thread that triggers the GC. Now the string
    referred to by the register in the first thread is moved, and when
    execution returns to the first thread, the pointer in the register is
    not valid anymore."



    "The problem described above will be resolved in a future release of
    Visual Objects, but right now the programmer has to be aware of it."

    (I don't think this was ever resolved, and I guess will never be)


    "There are two ways of avoiding problems with threads and dynamic data.
    The first solution is to disable garbage collection when more than one thread is active and enabling it only again after all threads but one finish. This can be done through the Visual Objects runtime functions DynLock() and DynUnlock(). The disadvantage of this approach is that no
    more memory will be freed or reused while the DynLock() is active.
    Depending on the application logic, memory consumption might increase rapidly. Even when the collector runs again, the dynamic memory pool
    will stay at the maximum size for future reuse. You might reduce the
    size of the dynamic memory pool manually by calling DynShrink().

    Disabling the collector globally while a thread is running, is a good solution when threads only run for a short while and do not generate a
    lot of dynamic data garbage. Indexing a database produces a lot of
    dynamic data garbage, so that indexing in a thread is not recommended
    for bigger databases.

    To avoid the collector problem, the solution is not to use dynamic data
    at all inside a thread. Another solution is to minimize the use of
    dynamic data to very few places, so that DynLock() and DynUnlock() can
    be used locally. The Web server that will be presented later is a good example for this technique."
    On 8/3/2021 6:56 AM, Robert Pope wrote:
    Hi All.

    I'm wondering if anyone knows what the differences between Win32 API CreateThread() vs. VO's CreateVOThread() are? Mostly with relation to the garbage collector. I.e. if I use Win32 API's CreateThread() would I completely avoid the garbage collector
    running on my thread. Is there a reason why I would still be better off using CreateVOThread()?

    I understand that threads and dynamic memory / the garbage collector don't play well together. I have in my ThreadFunc avoided declaring any LOCAL's and avoided calling any VO functions (because they might also have LOCAL's). There are also no
    anonymous variables like a string concatenation. Where I can't avoid a variable I use a structure (ThreadData) in static memory. The thread is the only reader/writer to this structure.

    Assuming I haven't missed anything, my thread is only working in static memory and only calling Windows API functions.

    The thread itself is pretty simple. It calls Win32 API IsHungAppWindow() once per second and logs an event in the windows event log if that function returns true.

    I'm not sure how much I trust VO's threading (or my code not to have missed some dynamic memory allocation somewhere) so I've also included runtime options to completely disable this feature if it plays up.

    Cheers!
    Rob.


    Thanks for replying.

    I have seen that documentation / white paper.

    In my case locking dynamic memory isn't an option. The thread continues for the lifetime of the application, and this isn't a small application. So yeah, as per my original post, my approach is to avoid using dynamic memory at all in the thread.

    What I really hope is that the garbage collector would only ever be called from the main thread. I don't want the worker thread to start the GC. Hence why I'm hoping that calling the Windows API's CreateThread() over VO's CreateVOThread() might be more
    ideal in that regard. If VO doesn't know my second thread exists, hopefully the garbage collection would never initiate on it. Since the thread is using no dynamic memory it would be unaffected by the GC running on another (the main) thread concurrently.

    I guess I'm hoping that someone with internal knowledge of VO threading might still be lurking around these parts to provide some undocumented knowledge. :)

    Cheers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jamal@21:1/5 to Robert Pope on Tue Aug 3 18:02:08 2021
    You can post to the X# forum and Robert may know.

    On 8/3/2021 1:07 PM, Robert Pope wrote:
    On Wednesday, August 4, 2021 at 2:05:19 AM UTC+10, Jamal wrote:
    According to the docs the CreateThread() and CreateVOThread() are the same: >> However, "CreateVOThread() also performs some additional initializations
    in the kernel runtime."

    But it does not say what those initializations! But I guess it has to do
    with the GC suspension during its processing.

    Additionally, the docs say (Just in case you did not see it and for others): >>
    CreateVOThread() and ExitVOThread()

    "First of all, threads in a Visual Objects application should not be
    created by calling the Windows API function CreateThread() directly, but
    by calling the Visual Objects runtime function CreateVOThread(). This
    function is called exactly like CreateThread() and actually it calls
    CreateThread() internally. CreateVOThread() also performs some
    additional initializations in the kernel runtime.

    Similar to CreateVOThread() there is an ExitVOThread() function that
    should be called instead of ExitThread(). However, try to avoid exiting
    a thread in a Visual Objects application by calling ExitThread() at all.
    Instead, exit your threads by returning from the thread entry point
    function."

    And the docs go on further:

    "When multiple threads are running, each dynamic memory pointer becomes
    a shared resource. One thread might load a pointer to a string into a
    register. (The GC can only update memory locations, not registers.)
    Before actually performing an operation with that pointer, execution is
    transferred to another thread that triggers the GC. Now the string
    referred to by the register in the first thread is moved, and when
    execution returns to the first thread, the pointer in the register is
    not valid anymore."



    "The problem described above will be resolved in a future release of
    Visual Objects, but right now the programmer has to be aware of it."

    (I don't think this was ever resolved, and I guess will never be)


    "There are two ways of avoiding problems with threads and dynamic data.
    The first solution is to disable garbage collection when more than one
    thread is active and enabling it only again after all threads but one
    finish. This can be done through the Visual Objects runtime functions
    DynLock() and DynUnlock(). The disadvantage of this approach is that no
    more memory will be freed or reused while the DynLock() is active.
    Depending on the application logic, memory consumption might increase
    rapidly. Even when the collector runs again, the dynamic memory pool
    will stay at the maximum size for future reuse. You might reduce the
    size of the dynamic memory pool manually by calling DynShrink().

    Disabling the collector globally while a thread is running, is a good
    solution when threads only run for a short while and do not generate a
    lot of dynamic data garbage. Indexing a database produces a lot of
    dynamic data garbage, so that indexing in a thread is not recommended
    for bigger databases.

    To avoid the collector problem, the solution is not to use dynamic data
    at all inside a thread. Another solution is to minimize the use of
    dynamic data to very few places, so that DynLock() and DynUnlock() can
    be used locally. The Web server that will be presented later is a good
    example for this technique."
    On 8/3/2021 6:56 AM, Robert Pope wrote:
    Hi All.

    I'm wondering if anyone knows what the differences between Win32 API CreateThread() vs. VO's CreateVOThread() are? Mostly with relation to the garbage collector. I.e. if I use Win32 API's CreateThread() would I completely avoid the garbage collector
    running on my thread. Is there a reason why I would still be better off using CreateVOThread()?

    I understand that threads and dynamic memory / the garbage collector don't play well together. I have in my ThreadFunc avoided declaring any LOCAL's and avoided calling any VO functions (because they might also have LOCAL's). There are also no
    anonymous variables like a string concatenation. Where I can't avoid a variable I use a structure (ThreadData) in static memory. The thread is the only reader/writer to this structure.

    Assuming I haven't missed anything, my thread is only working in static memory and only calling Windows API functions.

    The thread itself is pretty simple. It calls Win32 API IsHungAppWindow() once per second and logs an event in the windows event log if that function returns true.

    I'm not sure how much I trust VO's threading (or my code not to have missed some dynamic memory allocation somewhere) so I've also included runtime options to completely disable this feature if it plays up.

    Cheers!
    Rob.


    Thanks for replying.

    I have seen that documentation / white paper.

    In my case locking dynamic memory isn't an option. The thread continues for the lifetime of the application, and this isn't a small application. So yeah, as per my original post, my approach is to avoid using dynamic memory at all in the thread.

    What I really hope is that the garbage collector would only ever be called from the main thread. I don't want the worker thread to start the GC. Hence why I'm hoping that calling the Windows API's CreateThread() over VO's CreateVOThread() might be more
    ideal in that regard. If VO doesn't know my second thread exists, hopefully the garbage collection would never initiate on it. Since the thread is using no dynamic memory it would be unaffected by the GC running on another (the main) thread concurrently.

    I guess I'm hoping that someone with internal knowledge of VO threading might still be lurking around these parts to provide some undocumented knowledge. :)

    Cheers.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dlzc@21:1/5 to Robert Pope on Wed Aug 4 07:32:10 2021
    Dear Robert Pope:

    On Tuesday, August 3, 2021 at 10:07:25 AM UTC-7, Robert Pope wrote:
    ...
    If VO doesn't know my second thread exists, hopefully the garbage
    collection would never initiate on it. Since the thread is using no
    dynamic memory it would be unaffected by the GC running on
    another (the main) thread concurrently.

    Did you write the "second" thread in VO? Does it access VO-defined data / structures / open DBFs? Are all of its memory accesses to global variables? Does it call VO or user functions? If any of these answers is "yes", I think you are going to be in
    trouble. Consider that the code could be triggered to run while GC is running in the "foreground".

    David A. Smith (not a VO-er)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Wolfgang Riedmann@21:1/5 to Robert Pope on Wed Aug 4 17:07:10 2021
    Hi Robert,

    please be aware that you cannot use any VO specific datatype in the
    thread like strings, floats or objects because they are collected when
    not used.

    I would strongly recommend to no use threads in a VO application, and
    use rather a second application instead of a second thread linked by
    some collaboration mechanism.

    Or you may have another alternative: use X# instead. In X# not only the
    garbage collector is thread safe, but also the RDDs if you need them.

    Wolfgang



    Robert Pope wrote:

    On Wednesday, August 4, 2021 at 2:05:19 AM UTC+10, Jamal wrote:
    According to the docs the CreateThread() and CreateVOThread() are
    the same: However, "CreateVOThread() also performs some additional initializations in the kernel runtime."

    But it does not say what those initializations! But I guess it has
    to do with the GC suspension during its processing.

    Additionally, the docs say (Just in case you did not see it and for others):

    CreateVOThread() and ExitVOThread()

    "First of all, threads in a Visual Objects application should not
    be created by calling the Windows API function CreateThread()
    directly, but by calling the Visual Objects runtime function CreateVOThread(). This function is called exactly like
    CreateThread() and actually it calls CreateThread() internally. CreateVOThread() also performs some additional initializations in
    the kernel runtime.

    Similar to CreateVOThread() there is an ExitVOThread() function
    that should be called instead of ExitThread(). However, try to
    avoid exiting a thread in a Visual Objects application by calling ExitThread() at all. Instead, exit your threads by returning from
    the thread entry point function."

    And the docs go on further:

    "When multiple threads are running, each dynamic memory pointer
    becomes a shared resource. One thread might load a pointer to a
    string into a register. (The GC can only update memory locations,
    not registers.) Before actually performing an operation with that
    pointer, execution is transferred to another thread that triggers
    the GC. Now the string referred to by the register in the first
    thread is moved, and when execution returns to the first thread,
    the pointer in the register is not valid anymore."



    "The problem described above will be resolved in a future release
    of Visual Objects, but right now the programmer has to be aware of
    it."

    (I don't think this was ever resolved, and I guess will never be)


    "There are two ways of avoiding problems with threads and dynamic
    data. The first solution is to disable garbage collection when
    more than one thread is active and enabling it only again after all
    threads but one finish. This can be done through the Visual Objects
    runtime functions DynLock() and DynUnlock(). The disadvantage of
    this approach is that no more memory will be freed or reused while
    the DynLock() is active. Depending on the application logic,
    memory consumption might increase rapidly. Even when the collector
    runs again, the dynamic memory pool will stay at the maximum size
    for future reuse. You might reduce the size of the dynamic memory
    pool manually by calling DynShrink().

    Disabling the collector globally while a thread is running, is a
    good solution when threads only run for a short while and do not
    generate a lot of dynamic data garbage. Indexing a database
    produces a lot of dynamic data garbage, so that indexing in a
    thread is not recommended for bigger databases.

    To avoid the collector problem, the solution is not to use dynamic
    data at all inside a thread. Another solution is to minimize the
    use of dynamic data to very few places, so that DynLock() and
    DynUnlock() can be used locally. The Web server that will be
    presented later is a good example for this technique."
    On 8/3/2021 6:56 AM, Robert Pope wrote:
    Hi All.

    I'm wondering if anyone knows what the differences between Win32
    API CreateThread() vs. VO's CreateVOThread() are? Mostly with
    relation to the garbage collector. I.e. if I use Win32 API's CreateThread() would I completely avoid the garbage collector
    running on my thread. Is there a reason why I would still be
    better off using CreateVOThread()?

    I understand that threads and dynamic memory / the garbage
    collector don't play well together. I have in my ThreadFunc
    avoided declaring any LOCAL's and avoided calling any VO
    functions (because they might also have LOCAL's). There are also
    no anonymous variables like a string concatenation. Where I can't
    avoid a variable I use a structure (ThreadData) in static memory.
    The thread is the only reader/writer to this structure.

    Assuming I haven't missed anything, my thread is only working in
    static memory and only calling Windows API functions.

    The thread itself is pretty simple. It calls Win32 API
    IsHungAppWindow() once per second and logs an event in the
    windows event log if that function returns true.

    I'm not sure how much I trust VO's threading (or my code not to
    have missed some dynamic memory allocation somewhere) so I've
    also included runtime options to completely disable this feature
    if it plays up.

    Cheers!
    Rob.


    Thanks for replying.

    I have seen that documentation / white paper.

    In my case locking dynamic memory isn't an option. The thread
    continues for the lifetime of the application, and this isn't a small application. So yeah, as per my original post, my approach is to
    avoid using dynamic memory at all in the thread.

    What I really hope is that the garbage collector would only ever be
    called from the main thread. I don't want the worker thread to start
    the GC. Hence why I'm hoping that calling the Windows API's
    CreateThread() over VO's CreateVOThread() might be more ideal in that
    regard. If VO doesn't know my second thread exists, hopefully the
    garbage collection would never initiate on it. Since the thread is
    using no dynamic memory it would be unaffected by the GC running on
    another (the main) thread concurrently.

    I guess I'm hoping that someone with internal knowledge of VO
    threading might still be lurking around these parts to provide some undocumented knowledge. :)

    Cheers.



    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Pope@21:1/5 to Wolfgang Riedmann on Wed Aug 4 10:18:06 2021
    On Thursday, August 5, 2021 at 1:07:12 AM UTC+10, Wolfgang Riedmann wrote:
    Hi Robert,

    please be aware that you cannot use any VO specific datatype in the
    thread like strings, floats or objects because they are collected when
    not used.

    I would strongly recommend to no use threads in a VO application, and
    use rather a second application instead of a second thread linked by
    some collaboration mechanism.

    Or you may have another alternative: use X# instead. In X# not only the garbage collector is thread safe, but also the RDDs if you need them.

    Wolfgang
    Robert Pope wrote:

    On Wednesday, August 4, 2021 at 2:05:19 AM UTC+10, Jamal wrote:
    According to the docs the CreateThread() and CreateVOThread() are
    the same: However, "CreateVOThread() also performs some additional initializations in the kernel runtime."

    But it does not say what those initializations! But I guess it has
    to do with the GC suspension during its processing.

    Additionally, the docs say (Just in case you did not see it and for others):

    CreateVOThread() and ExitVOThread()

    "First of all, threads in a Visual Objects application should not
    be created by calling the Windows API function CreateThread()
    directly, but by calling the Visual Objects runtime function CreateVOThread(). This function is called exactly like
    CreateThread() and actually it calls CreateThread() internally. CreateVOThread() also performs some additional initializations in
    the kernel runtime.

    Similar to CreateVOThread() there is an ExitVOThread() function
    that should be called instead of ExitThread(). However, try to
    avoid exiting a thread in a Visual Objects application by calling ExitThread() at all. Instead, exit your threads by returning from
    the thread entry point function."

    And the docs go on further:

    "When multiple threads are running, each dynamic memory pointer
    becomes a shared resource. One thread might load a pointer to a
    string into a register. (The GC can only update memory locations,
    not registers.) Before actually performing an operation with that pointer, execution is transferred to another thread that triggers
    the GC. Now the string referred to by the register in the first
    thread is moved, and when execution returns to the first thread,
    the pointer in the register is not valid anymore."



    "The problem described above will be resolved in a future release
    of Visual Objects, but right now the programmer has to be aware of
    it."

    (I don't think this was ever resolved, and I guess will never be)


    "There are two ways of avoiding problems with threads and dynamic
    data. The first solution is to disable garbage collection when
    more than one thread is active and enabling it only again after all threads but one finish. This can be done through the Visual Objects runtime functions DynLock() and DynUnlock(). The disadvantage of
    this approach is that no more memory will be freed or reused while
    the DynLock() is active. Depending on the application logic,
    memory consumption might increase rapidly. Even when the collector
    runs again, the dynamic memory pool will stay at the maximum size
    for future reuse. You might reduce the size of the dynamic memory
    pool manually by calling DynShrink().

    Disabling the collector globally while a thread is running, is a
    good solution when threads only run for a short while and do not
    generate a lot of dynamic data garbage. Indexing a database
    produces a lot of dynamic data garbage, so that indexing in a
    thread is not recommended for bigger databases.

    To avoid the collector problem, the solution is not to use dynamic
    data at all inside a thread. Another solution is to minimize the
    use of dynamic data to very few places, so that DynLock() and
    DynUnlock() can be used locally. The Web server that will be
    presented later is a good example for this technique."
    On 8/3/2021 6:56 AM, Robert Pope wrote:
    Hi All.

    I'm wondering if anyone knows what the differences between Win32
    API CreateThread() vs. VO's CreateVOThread() are? Mostly with
    relation to the garbage collector. I.e. if I use Win32 API's CreateThread() would I completely avoid the garbage collector
    running on my thread. Is there a reason why I would still be
    better off using CreateVOThread()?

    I understand that threads and dynamic memory / the garbage
    collector don't play well together. I have in my ThreadFunc
    avoided declaring any LOCAL's and avoided calling any VO
    functions (because they might also have LOCAL's). There are also
    no anonymous variables like a string concatenation. Where I can't
    avoid a variable I use a structure (ThreadData) in static memory.
    The thread is the only reader/writer to this structure.

    Assuming I haven't missed anything, my thread is only working in
    static memory and only calling Windows API functions.

    The thread itself is pretty simple. It calls Win32 API IsHungAppWindow() once per second and logs an event in the
    windows event log if that function returns true.

    I'm not sure how much I trust VO's threading (or my code not to
    have missed some dynamic memory allocation somewhere) so I've
    also included runtime options to completely disable this feature
    if it plays up.

    Cheers!
    Rob.


    Thanks for replying.

    I have seen that documentation / white paper.

    In my case locking dynamic memory isn't an option. The thread
    continues for the lifetime of the application, and this isn't a small application. So yeah, as per my original post, my approach is to
    avoid using dynamic memory at all in the thread.

    What I really hope is that the garbage collector would only ever be
    called from the main thread. I don't want the worker thread to start
    the GC. Hence why I'm hoping that calling the Windows API's
    CreateThread() over VO's CreateVOThread() might be more ideal in that regard. If VO doesn't know my second thread exists, hopefully the
    garbage collection would never initiate on it. Since the thread is
    using no dynamic memory it would be unaffected by the GC running on
    another (the main) thread concurrently.

    I guess I'm hoping that someone with internal knowledge of VO
    threading might still be lurking around these parts to provide some undocumented knowledge. :)

    Cheers.
    --

    I guess I will post the code of my thread func, given the responses I'm getting :)
    And the question I'm trying to have answered: Would the garbage collector ever initiate on this thread if I start it with CreateThread()? I don't want it to.

    STRUCT HeartBeatMonitor_ThreadData

    MEMBER hWnd AS PTR
    MEMBER hEventSource AS PTR
    MEMBER pszEventSource AS PSZ
    MEMBER RootDirectory AS PSZ

    MEMBER sTimeStart AS _winSYSTEMTIME
    MEMBER sTimeEnd AS _winSYSTEMTIME

    FUNCTION HeartbeatMonitor(sHeartBeatMonitor_ThreadData AS HeartBeatMonitor_ThreadData) AS DWORD PASCAL

    // Do not declare any locals here. Garbage collector is not thread safe.
    // Also must not call any functions including VO builtins that might allocate memory.

    DO WHILE TRUE
    IF sHeartBeatMonitor_ThreadData.hWnd != NULL_PTR

    IF IsHungAppWindow(sHeartBeatMonitor_ThreadData.hWnd)
    IF sHeartBeatMonitor_ThreadData.sTimeStart == NULL_PTR

    sHeartBeatMonitor_ThreadData.sTimeStart := MemAlloc(_SizeOf(_winSYSTEMTIME))
    GetLocalTime(sHeartBeatMonitor_ThreadData.sTimeStart)

    sHeartBeatMonitor_ThreadData.hEventSource := RegisterEventSource(NULL_PSZ, sHeartBeatMonitor_ThreadData.pszEventSource)
    ReportEvent(sHeartBeatMonitor_ThreadData.hEventSource, EVENTLOG_WARNING_TYPE, 0, MSG_APPLICATION_HANG, NULL_PTR, 1, 0, @sHeartBeatMonitor_ThreadData.RootDirectory, NULL_PTR)
    DeregisterEventSource(sHeartBeatMonitor_ThreadData.hEventSource)
    sHeartBeatMonitor_ThreadData.hEventSource := NULL_PTR

    END IF
    ELSE
    IF sHeartBeatMonitor_ThreadData.sTimeStart != NULL_PTR

    sHeartBeatMonitor_ThreadData.sTimeEnd := MemAlloc(_SizeOf(_winSYSTEMTIME))
    GetLocalTime(sHeartBeatMonitor_ThreadData.sTimeEnd)

    sHeartBeatMonitor_ThreadData.hEventSource := RegisterEventSource(NULL_PSZ, sHeartBeatMonitor_ThreadData.pszEventSource)
    ReportEvent(sHeartBeatMonitor_ThreadData.hEventSource, EVENTLOG_INFORMATION_TYPE, 0, MSG_APPLICATION_RESUME, NULL_PTR, 1, 0, @sHeartBeatMonitor_ThreadData.RootDirectory, NULL_PTR)
    DeregisterEventSource(sHeartBeatMonitor_ThreadData.hEventSource)
    sHeartBeatMonitor_ThreadData.hEventSource := NULL_PTR

    MemFree(sHeartBeatMonitor_ThreadData.sTimeStart)
    MemFree(sHeartBeatMonitor_ThreadData.sTimeEnd)

    sHeartBeatMonitor_ThreadData.sTimeStart := NULL_PTR
    sHeartBeatMonitor_ThreadData.sTimeEnd := NULL_PTR

    END IF
    END IF

    END IF

    Sleep(1000)

    END DO

    RETURN 0



    This function runs on the main thread to launch the thread:
    FUNCTION HeartbeatMonitorInit(hWnd AS PTR) AS VOID PASCAL

    LOCAL sSECURITY_ATTRIBUTES AS _winSECURITY_ATTRIBUTES
    LOCAL sHeartBeatMonitor_ThreadData AS HeartBeatMonitor_ThreadData

    sSECURITY_ATTRIBUTES := MemAlloc(_SizeOf(_winSECURITY_ATTRIBUTES))
    sSECURITY_ATTRIBUTES.nLength := _SizeOf(_winSECURITY_ATTRIBUTES)
    sSECURITY_ATTRIBUTES.lpSecurityDescriptor := NULL_PTR
    sSECURITY_ATTRIBUTES.bInheritHandle := FALSE

    sHeartBeatMonitor_ThreadData := MemAlloc(_SizeOf(HeartBeatMonitor_ThreadData))
    sHeartBeatMonitor_ThreadData.hWnd := hWnd
    sHeartBeatMonitor_ThreadData.pszEventSource := PszAlloc(String2PSZ("XXXXX"))
    sHeartBeatMonitor_ThreadData.RootDirectory := PszAlloc(String2PSZ(gcRootDirectory))
    sHeartBeatMonitor_ThreadData.sTimeStart := NULL_PTR
    sHeartBeatMonitor_ThreadData.sTimeEnd := NULL_PTR

    CreateThread(sSECURITY_ATTRIBUTES, 0, @HeartbeatMonitor(), sHeartBeatMonitor_ThreadData, 0, NULL_PTR)

    MemFree(sSECURITY_ATTRIBUTES)

    RETURN

    Cheers!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Wolfgang Riedmann@21:1/5 to All on Thu Aug 5 11:47:06 2021
    Hi Robert,

    I guess I will post the code of my thread func, given the responses
    I'm getting :) And the question I'm trying to have answered: Would
    the garbage collector ever initiate on this thread if I start it with CreateThread()? I don't want it to.

    this code should be safe, but nevertheless I would use CreateVOThread.

    But I think that the GC would never kick in here as you have no local
    variables or other things that could make him kick in.

    But as Jamal wrote: the only person you can give a correct answer is
    Robert v.d.Hulst, and you can reach him through the X# forum.
    I would do that as fast as possible as it is holiday time and he may be
    absent the next weeks for holiday.

    Wolfgang

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)