Page 1 of 2 12 LastLast
Results 1 to 40 of 54

Thread: ways to provide a synchronous response from an asynchronous task

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Schmidt
    There are dozens of different ways, to ensure "synchronous-behaviour for asynchronous tasks"
    ('Sleep 1 + DoEvents' being only one of those "non-GUI-blocking Wait-Functions")...
    [...]
    - how to wait for the finishing of these threads within a function that behaves synchronously on the outside

    Olaf
    Yes, that's the question.

    The asynchronous component usually notifies that it finished with an event, but there could be also a property that one can check to see whether it finished or not, for example Object.StillExecuting

    So, the DoEvents way for waiting for the asynch task to complete is:

    Code:
    Do While Object.StillExecuting
        DoEvents
        [May be also] Sleep Somevalue 
    Loop
    What are the others?

  2. #2
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    DoEvents leads to other issues if you fail to address them: user can close the form, access menus and other controls.

    What is the goal here? Prevent user from continuing until async action finishes? Provide some sort of status while async is executing? Something else?
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  3. #3
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    DoEvents leads to other issues if you fail to address them: user can close the form, access menus and other controls.
    That can be easily secured by disabling Controls (or group of controls by disabling their Container) -
    or by setting Cancel in Form_QueryUnload etc...

    Quote Originally Posted by LaVolpe View Post
    What is the goal here? Prevent user from continuing until async action finishes? Provide some sort of status while async is executing?
    The question is precise enough and doesn't leave much room for interpretation IMO.

    It's the question, how one can write (or place) e.g. a function(call) like:
    TimeNeeded = CalculateMandelBrotAreaOn(VBPictureBox)

    ... in ones normal program-flow - which will then not return until the task is finished ...
    (although one was e.g. splitting the area into 4 SubAreas - then starting 4 threads on each SubArea
    asynchronously, not knowing in what order the 4 threads wil be finished with their SubArea-calculations,
    but waiting until all 4 of them "are done").

    @Eduardo
    the DoEvents-call should be the last one, before checking "cancel-conditions" though, so it would be better to write:

    Code:
    Do While Object.StillExecuting And Not (boolCancelFlag Or TimeOutReached)
        Sleep SomevalueLargerThanZero '<- this should be mandatory in such loops
        DoEvents 'last call before the Exit-Condition-Check
    Loop
    Olaf
    Last edited by Schmidt; Apr 16th, 2017 at 12:23 PM.

  4. #4
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    Olaf. The reason for asking for clarification is that the OP also stated that he could check the state in some cases? Therefore, why check for the state if a completion event is sent? So, yes there is room for interpretation IMO. Usually a DoEvents is in place because a) no final notification is provided or b) a status wants to be displayed or c) the ability to cancel the async action.

    Another option could be to show a modal form. In that form, the ability to close it is disabled. The code that begins the async exists in that form. If the user can't close the form, only the completion event triggers it closure, then the user is stuck until the form closes.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Schmidt View Post
    @Eduardo
    the DoEvents-call should be the last one, before checking "cancel-conditions"
    Of course. I tried to present it in a minimalistic example.

    Quote Originally Posted by Schmidt View Post
    though, so it would be better to write:

    Code:
    Do While Object.StillExecuting Or boolCancelFlag Or TimeOutReached
        Sleep SomevalueLargerThanZero '<- this should be mandatory in such loops
        DoEvents 'last call before the Exit-Condition-Check
    Loop
    Olaf
    Why do you say that Sleep should be mandatory?
    AFAIK, if there is no Sleep, the processor will show 100% usage in the task manager, even when it's in fact not really used (or something like that), but aside from that there is not other issue (AFAIK).

    On the other hand, Sleep blocks any execution in the current thread for that time, so if the task you are waiting for is done in the same thread (in VB code in the same project), what you are doing with the Sleep call is to freeze the execution for that little while, and so making the task to take a little longer than it would otherwise.
    A small price that perhaps is better to pay so the task manager doesn't show the processor running at 100%.

    But if the task is performed in another thread, I agree that the Sleep should be mandatory. (But even whithout it, I think you won't notice much difference in performance).

    Is there anything else involved?

  6. #6

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    Olaf. The reason for asking for clarification is that the OP also stated that he could check the state in some cases? Therefore, why check for the state if a completion event is sent? So, yes there is room for interpretation IMO. Usually a DoEvents is in place because a) no final notification is provided or b) a status wants to be displayed or c) the ability to cancel the async action.

    Another option could be to show a modal form. In that form, the ability to close it is disabled. The code that begins the async exists in that form. If the user can't close the form, only the completion event triggers it closure, then the user is stuck until the form closes.
    Hello LaVolpe,

    The issue is to return a synchronous result from an asynchronous task.
    Of course you can do it whith an event, like:

    Code:
    Private Withevent SomeObject as cObjectClass
    Private mFinished as Boolean
    Private mLeaving as Boolean
    
    Public Funtion RunAsynchTaskAndReturnResult() as ResultType
        mFinished = False
        SomeObject.RunAsyncTask
        Do While Not mFinished
            Sleep 1
            DoEvents
            iF mLeaving Then Exit Sub
        Loop
        
        RunAsynchTaskAndReturnResult = SomeObject.Result
    End Function
    
    Private Sub SomeObject_Finished()
        mFinished = True
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
        If Cancel = 0 Then
            mLeaving = True
        End If
    End Sub
    But if the object has a property to indicate whether it finished or not, it is the same than doing:

    Code:
    Private mLeaving as Boolean
    
    Public Funtion RunAsynchTaskAndReturnResult() as ResultType
        SomeObject.RunAsyncTask
        Do While SomeObject.Running ' Or .StillExecuting, or whatever
            Sleep 1
            DoEvents
            iF mLeaving Then Exit Sub
        Loop
        
        RunAsynchTaskAndReturnResult = SomeObject.Result
    End Function
    
    Private Sub Form_Unload(Cancel As Integer)
        If Cancel = 0 Then
            mLeaving = True
        End If
    End Sub
    It already has the "mFinished" condition implemented in a property.
    Last edited by Eduardo-; Apr 16th, 2017 at 12:57 PM.

  7. #7
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Eduardo- View Post
    Why do you say that Sleep should be mandatory?
    AFAIK, if there is no Sleep, the processor will show 100% usage in the task manager, even when it's in fact not really used (or something like that), but aside from that there is not other issue (AFAIK).
    Well, you pointed out the issue already - without an additional Sleep-call, the CPU-Core goes to 100% Load
    (and thus will drain your NoteBook-battery unnecessarily, or making your CPU-cooler make unnecessary noise etc.).

    Quote Originally Posted by Eduardo- View Post
    On the other hand, Sleep blocks any execution in the current thread for that time, so if the task you are waiting for is done in the same thread (in VB code in the same project), what you are doing with the Sleep call is to freeze the execution for that little while, and so making the task to take a little longer than it would otherwise.
    Not when you reduce the Sleep-interval by wrapping the loop in e.g.:
    - timeBeginPeriod 1
    - timeEndPeriod 1
    ... or use one of the Waitxxx-APIs instead of Sleep
    ... or use SleepEx, when your async tasks are based on CompletionPorts, etc.

    That's what I meant with "dozens of different ways to wait for async completion" -
    if there's no GUI involved, there's even more APIs one could choose from (avoiding DoEvents completely).

    Though - if there's a GUI involved, you will in most cases have to do it by "opening up a new Message-Pump"
    (and a DoEvents-looping does exactly that - as does LaVolpes suggestion, to show a Modal-Form instead).

    Olaf

  8. #8
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    @Eduardo. Well I will bow out of this thread. In the 1st example, you are using a DoEvents loop which unless you disabled your form/controls, the user is allowed to continue/abort while waiting on the async event to finish. If the user wasn't allowed, then the loop would be unneeded since no action would be allowed until the completed event was sent. That is the only part of your question that confused me. I read the question as ways of making async behave as sync, I interpreted it as meaning: the user waits, period, no interaction is allowed. Clearly I misunderstood, which is why I asked for clarification

    Edited: And for a little clarification on my part
    When I said above the loop wouldn't be needed, the assumption is that you have that code related to a modal window. Otherwise, you have little choice in using a loop, of some sort, while waiting for the async event to finish while trying to restrict some user actions. Whether it is necessary to query the state property/value is a separate item. Modal windows are a common approach -- think of a progress bar. If the user is allowed to cancel the async action, then the progress bar window will have a 'cancel' button else it won't. The modal window approach would negate any need for a loop if not allowed to abort. However, I can't imagine any good application that would force a user to wait for an async action without ability to abort on demand if the action was taking too long or user changed their mind.
    Last edited by LaVolpe; Apr 16th, 2017 at 01:30 PM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  9. #9

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Yes, there are two different scenarios, or may be three:

    1) The user is compelled to wait for the function to return the response and can do nothing else.
    2) This is a variation from 1: the user must wait for the function to return, but the only other thing that he can do is to cancel the task.
    3) The user can do anything, to call other functions (launch other tasks), close the application, cancel the task, call the same function again while the previous response is still in course.

    To disable all the controls/forms can serve for option 1.
    To wrap a form displayed modally can be a good option for option 2.

    But if you don't want to be so limiting, and you want to give freedom to the user while he is executing a long task, as a professional program in my opinion should do, option 3 is the best one.
    Besides, if it is a component that any client program could use, I don't see 1 and 2 as nice options.

    So the issue is to handle reentrancy. But that's another subject and would be off-topic, but it's related.

    The subject here is how to do a synchronous function to return a result from an asynchronous task.
    (Leaving aside how to handle reentrancy)

    Besides the use of DoEvent, another way have been suggested: to wrap a form displayed modally (may be outside of the screen so it's not visible, or may be a 1x1 pixel borderless form, so the user don't see it).
    It has the side effect that option 3 is not viable.

  10. #10
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    Well, option 3 doesn't really meet the definition of synchronous, does it? I think we all agree Option 1 is not very good, unless you are updating some software or the system maybe. And option 2, whether done in the current form's context, or within a progress-like form, is where I think this thread is headed.

    For a separate component, I'd imagine you might be tracking the status of the event and forwarding the event to the user. In that case, your component simply initiates, repeats/forwards events, allows caller to trigger a cancel option, and informs caller that event has finished normally or canceled.

    Reentrancy is not off-topic IMO. It must be addressed if it will be a problem whenever you are using a loop that allows it.

    Tip: I don't think you can move windows off the desktop any longer. And if the user had the ability to cancel the process, it shouldn't be hidden.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  11. #11
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    FWIW, here's a bit of quick&dirty code, which shows an alternative to Sleep in the WaitLoop (with fast reaction time to UserInput),
    and also how one can handle "premature Form-Closing" (when the task is still running).

    Into a Form (a Form_Click will cancel the WaitLoop - and trying to close the Form whilst waiting will also be handled)
    Code:
    Option Explicit
    
    Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
    
    Private bCancel As Boolean
    
    Private Sub Form_Load()
      Show
      
      Caption = "Waiting..."
        PerformAsyncTaskUntilCompletion
      Caption = "Wait finished"
      
      If Tag = "x" Then Unload Me
    End Sub
    
    Private Sub Form_Click()
      bCancel = True
    End Sub
    
    Private Sub PerformAsyncTaskUntilCompletion()
      Do While Not bCancel
        MsgWaitForMultipleObjects 0, ByVal 0&, 0&, 15, &H4FF '<- QS_ALLINPUT
        DoEvents
      Loop
    End Sub
    
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
      If Caption = "Waiting..." Then
        Cancel = 1
        bCancel = MsgBox("Async-Job still running, do you want to cancel it?", vbYesNo) = vbYes
        If bCancel Then Tag = "x"
      End If
    End Sub
    Olaf

  12. #12
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    Similar to what Olaf provided, I typically use a form/class-level variable that contains bitwise flags. Where a value of zero indicates no looping is going on, 1 indicates looping is active, 2 indicates user attempting to cancel but not yet canceled, and 4,8,16, etc are used for other custom purposes. That way, the variable can be checked in any event to allow or prevent reentrancy, i.e., If (m_IsBusy And 1) Then looping & respond accordingly.

    But to the question at hand, if not placing the async event behind a modal form and you have no other way to generate a synchronous event, then a loop may be your only answer. Disabling the entire form/component until the event finishes isn't ideal either as that leads to confusion for the user.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  13. #13
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    Well, option 3 doesn't really meet the definition of synchronous, does it?
    Synchronous calls (which don't return, until async "Sub-Jobs" are done) allow an
    easier formulation of WorkFlows.

    E.g. one "WorkFlow-task" could be, to synchronize local DB content with an online-DB.

    Sub SynchronizeWithOnlineDB()
    ... CheckForDifferences
    ... DownloadDiffAndUpdateLocal
    ... UploadDiffAndUpdateOnline
    ... CheckForDifferences 'again, to make sure they really are zero now on both ends
    End Sub

    When each one of the 4 Sub-Tasks above is written as a routine with synchronous behaviour,
    the aggregating routine (SynchronizeWithOnlineDB) can be written in a "normal way" -
    then in turn perhaps just updating a "SideBar" with the current progress of the WorkFlow
    (whilst the User is allowed to work normally on his local DB).

    Sure, one can trigger the workflow-actions also directly from asynchronously incoming Events,
    but sometimes a lot of different "Event-Throwers" are involved and a lot of Flags- to manage
    when to start other parts of the over-all-workflow...
    For some devs this might not be as "clean" a solution as the alternative...

    Olaf

  14. #14
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    @Olaf. Maybe semantics are at play here. From my posts, I am using the definitions: When you execute something synchronously, you wait for it to finish before moving on to another task. When you execute something asynchronously, you can move on to another task before it finishes. In other words, a synchronous call is similar to: MsgBox "Hello World". Any lines of code that follow will not execute until the msgbox is closed. Eduardo's option 3, IMO, is a great definition of async vs sync.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  15. #15

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    Well, option 3 doesn't really meet the definition of synchronous, does it?
    Yes, synchronous (as I understand it) means that it doesn't return to the caller until it has the result.
    It is not about not being able to do another thing while waiting.

    That's what can be confusing, that it's still inside a function (it didn't return or exit the function) but still some code started to run somewhere else.

    And the code that started to run somewhere else can be also synchronous or asynchronous.

    If it is asynchronous, it start another task, may be in another thread, or some task that runs in the same thread but run in chunks and is called by a timer that is started when you start that task.

    In this way, in the case of an asynch task, the first task (our main task we are talking about here) will run along with this second one.

    But if this second task is also synchronous, it will run and finish first, and our first task won't be reasumed (at least it won't be reasumed our checking whether it finised in the case the task is running in other thread and has already finished) until this second task finish.

    It will behave in a LIFO way (last in first out).

    Because DoEvents allows to go to process events, but it doesn't return until the events are all processed. It's not that it will do a little of one job and and a little of the other.

    Quote Originally Posted by LaVolpe View Post
    I think we all agree Option 1 is not very good, unless you are updating some software or the system maybe. And option 2, whether done in the current form's context, or within a progress-like form
    Agree.

    Quote Originally Posted by LaVolpe View Post
    is where I think this thread is headed.
    In my intention it is headed to option 3.
    But we could discuss any option because I think the subject is interesting enough.

    Quote Originally Posted by LaVolpe View Post
    For a separate component, I'd imagine you might be tracking the status of the event and forwarding the event to the user.
    Exactly.

    Quote Originally Posted by LaVolpe View Post
    In that case, your component simply initiates, repeats/forwards events, allows caller to trigger a cancel option, and informs caller that event has finished normally or canceled.
    It doesn't have any UI element, the cancel order must come from the client program.

    Quote Originally Posted by LaVolpe View Post
    Reentrancy is not off-topic IMO. It must be addressed if it will be a problem whenever you are using a loop that allows it.
    Yes, but reentrancy is tight related to the nature of the functions, what you can actually do and what you can't. I mean, it's related to the logic and nature of the program.

    For example, if you are writing to a file, you cannot allow to write to the same file while the firt task is not completed... or yes in the case it's a database and it's another register.
    If you are calling a web page for the weather information, and there is another call for the weather information while the fist one still didn't return (may be for the same geographic location or for another one), there is no problem with that reentrancy, you should be able to return both results.

    Quote Originally Posted by LaVolpe View Post
    Tip: I don't think you can move windows off the desktop any longer. And if the user had the ability to cancel the process, it shouldn't be hidden.
    If you want to show UI, yes.

  16. #16

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Schmidt View Post
    FWIW, here's a bit of quick&dirty code, which shows an alternative to Sleep in the WaitLoop (with fast reaction time to UserInput),
    and also how one can handle "premature Form-Closing" (when the task is still running).

    Into a Form (a Form_Click will cancel the WaitLoop - and trying to close the Form whilst waiting will also be handled)
    Code:
    Option Explicit
    
    Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
    
    Private bCancel As Boolean
    
    Private Sub Form_Load()
      Show
      
      Caption = "Waiting..."
        PerformAsyncTaskUntilCompletion
      Caption = "Wait finished"
      
      If Tag = "x" Then Unload Me
    End Sub
    
    Private Sub Form_Click()
      bCancel = True
    End Sub
    
    Private Sub PerformAsyncTaskUntilCompletion()
      Do While Not bCancel
        MsgWaitForMultipleObjects 0, ByVal 0&, 0&, 15, &H4FF '<- QS_ALLINPUT
        DoEvents
      Loop
    End Sub
    
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
      If Caption = "Waiting..." Then
        Cancel = 1
        bCancel = MsgBox("Async-Job still running, do you want to cancel it?", vbYesNo) = vbYes
        If bCancel Then Tag = "x"
      End If
    End Sub
    Olaf
    But you are still using DoEvents.

    And second: what is the point os using the MsgWaitForMultipleObjects API when you have already that information in a variable (or a property)?

  17. #17
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    That's what can be confusing, that it's still inside a function (it didn't return or exit the function) but still some code started to run somewhere else.
    That's understandable if DoEvents is used. And of course even without DoEvents, if any code inside that function called another method that could trigger an event, then that event could be executing other code.

    It doesn't have any UI element, the cancel order must come from the client program.
    Yes. Your component will usually have a cancel method or pass a boolean via an event to allow the user to set that boolean, on return, in order to cancel.

    Almost sounds like you may be more interested in an ActiveX EXE?
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  18. #18
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Eduardo- View Post
    But you are still using DoEvents.
    Because there's no easier way to fire-up a (fully vbRuntime-compatible) MessagePump (MessageLoop) in VB,

    Quote Originally Posted by Eduardo- View Post
    And second: what is the point os using the MsgWaitForMultipleObjects API ...
    It's a better replacment for the Sleep-call (to avoid the already mentioned 100% CPU-utilization,
    which is caused because DoEvents deals with the Apps MessageQueue "sidewards" per PeekMessage).

    In the example, despite having specified a TimeOut of 15msec there (similar to a Sleep 15),
    the call to MsgWaitForMultipleObjects will *immediately* return (before the 15msec are up),
    when a new "general-input" WindowsMessage is detected in the Apps MessageQueue.

    A Sleep 1 will not return until 12-15msec are up (unless you introduced the already mentioned timeBeginPeriod/EndPeriod-calls in addition).

    So, what I've posted is an immediately reacting MessagePump, which still will avoid the "100% CPU-load issue"
    (in case one is careless enough to use DoEvents in a "naked way" inside such a MessagePump-loop).

    Olaf

  19. #19

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    That's understandable if DoEvents is used. And of course even without DoEvents, if any code inside that function called another method that could trigger an event, then that event could be executing other code.
    Yes, and if the client calls the same function again from the event procedure it could end up with an out of stack space error.

    Quote Originally Posted by LaVolpe View Post
    Yes. Your component will usually have a cancel method or pass a boolean via an event to allow the user to set that boolean, on return, in order to cancel.

    Almost sounds like you may be more interested in an ActiveX EXE?
    No... it is an OCX.

    And since AX exe's can't be used in SxS installations (that is what I always use now) it's ruled out for me.
    I never made an AX exe BTW.

  20. #20
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    Don't know if this could apply, but maybe have your ocx be a container for another ocx. That inner ocx is created as a control array where each does its own thing and forwards events to your outer ocx. Kinda like a form hosting an array of Winsock controls.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  21. #21
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    Don't know if this could apply, but maybe have your ocx be a container for another ocx. That inner ocx is created as a control array where each does its own thing and forwards events to your outer ocx. Kinda like a form hosting an array of Winsock controls.
    That still doesn't solve the problem which this thread is all about:
    How to write a synchronous function which:
    - triggers several async actions (which will later report back over Events)
    - only returns, after all async actions were finished

    I've described the problem already in post #3...

    So (to get specific) what would be your solution to:
    TimeNeeded = CalculateMandelBrotAreaOn(VBPictureBox)

    TimeNeeded being calculated inside the (synchronously behaving) "outer function" (CalculateMandelBrotAreaOn) -
    the function itself internally triggering 4 asynchronous actions (4 threads) which will calculate something (on 4 Cores in parallel).

    Olaf

  22. #22

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Schmidt View Post
    Because there's no easier way to fire-up a (fully vbRuntime-compatible) MessagePump (MessageLoop) in VB,



    It's a better replacment for the Sleep-call (to avoid the already mentioned 100% CPU-utilization,
    which is caused because DoEvents deals with the Apps MessageQueue "sidewards" per PeekMessage).

    In the example, despite having specified a TimeOut of 15msec there (similar to a Sleep 15),
    the call to MsgWaitForMultipleObjects will *immediately* return (before the 15msec are up),
    when a new "general-input" WindowsMessage is detected in the Apps MessageQueue.

    A Sleep 1 will not return until 12-15msec are up (unless you introduced the already mentioned timeBeginPeriod/EndPeriod-calls in addition).

    So, what I've posted is an immediately reacting MessagePump, which still will avoid the "100% CPU-load issue"
    (in case one is careless enough to use DoEvents in a "naked way" inside such a MessagePump-loop).

    Olaf
    So all that is to avoid the 15 ms that in some cases Sleep may waste.

    But how to know what object handle to provide, for example in the case of an WinHTTPReuqest call I would not know where to go to get a handle.
    Anyway, in that particular case there is already that property WaitForResponse that can be used instead of the loop.

    In my real case I have some tasks that are driven by a timer, so what I need to wait is that the timer is not enabled any more.

    So, the task is something similar to this:

    Code:
    Private mStep As Long
    Private mCancelled as Boolean 
    
    Public Function GetSomething (nSomeParameter) as cWhatever
        tmrDoTask.Interval = 1
        mStep = 0
        tmrDoTask.Enabled
        Do Until Not tmrDoTask.Enabled
            Sleep 1 ' I doubt it's neccesary here
            DoEvents
        Loop
        Set GetSomething = mResult
    End Function
    
    Private Sub tmrDoTask_Timer()
        DoTask
    End Sub
    
    Private Sub DoTask()
        Static sFinished As Boolean
    
        If mStep = 1 Then
            Goto Step1
        ElseIf mStep = 2 Then
            Goto Step2
        ElseIf mStep = 3 Then
            Goto Step3
        End If
    
        ' the task begins here
        ' do initializing and first things
        mStep = 1
        Exit Sub
    
    Step1:
        If mCancelled Then tmrTask.Enabled = False: Exit Sub
        sFinished = False
        Do Until sFinished
            ' do something
            mStep = 2
            Exit Sub
    
    Step2:
            If mCancelled Then tmrTask.Enabled = False: Exit Sub
            ' do some other thing
            If SomeCondition then sFinished = true
            mStep = 3
            Exit Sub
    
    Step3:
        Loop
        Set GetSomething = [Some Result] ' Set result
        tmrTask.Enabled = False
    End Sub
    Last edited by Eduardo-; Apr 16th, 2017 at 04:43 PM.

  23. #23
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: ways to provide a synchronous response from an asynchronous task

    @Olaf. My reply to Eduardo was pertaining to his most recent reply to me.

    There are very few options and unless someone wants to create some thunk or COM solution to act as an async>sync go-between. At no point did I read that the requirement was to perform several async tasks, maybe I missed it, maybe there is some history on this thread we readers are not privy to? If a requirement was to wait for multiple simultaneous async requests to finish before returning, then that method obviously would have to track & wait on each task. Encapsulating those into a single class, control, whatever, could be another solution to simplify. But we may be interpreting this thread differently
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  24. #24

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    Don't know if this could apply, but maybe have your ocx be a container for another ocx. That inner ocx is created as a control array where each does its own thing and forwards events to your outer ocx. Kinda like a form hosting an array of Winsock controls.
    I don't understand how that would help.
    The OCX's all run in the same thread that the client application, I don't see any advantage in doing that.
    May be I'm missing something.

    And for sub-tasks I use class modules.
    Last edited by Eduardo-; Apr 16th, 2017 at 04:00 PM.

  25. #25
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by LaVolpe View Post
    ...At no point did I read that the requirement was to perform several async tasks, maybe I missed it,
    I guess you did, because the threads title already describes the problem fully:
    How to: "provide a synchronous response from an asynchronous task"
    a "synchronous response" (IMO) cannot be misunderstood as anything different than: "being the result of a synchronous (method-) call"

    The exact count of "async tasks to perform" was not specified but even if it's exactly 1, the problem is still the same:
    - async actions usually report their results in an Event
    - so, how to *wait* (inside the outer synchronous function) until that Event comes in

    Quote Originally Posted by LaVolpe View Post
    maybe there is some history on this thread we readers are not privy to?
    I'm quite certain, the prior thread which caused the question here was: http://www.vbforums.com/showthread.p...pload-progress

    But the problem is an old one - everyone who has written Rs232-stuff with the MSComm-OCX for serial interfaces,
    or is using the Winsock-OCX for his own TCP-stuff has the problem to write e.g. a function like this one here:

    Result = SendTCPRequest("SomeInputString") 'expecting and returning some OutPutString from the TCPServer-response (which came in over an Event)

    Olaf
    Last edited by Schmidt; Apr 16th, 2017 at 04:08 PM.

  26. #26

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Well, at least it helped me to be more secure that I'm not doing something stupid, because I know that a lot of people hate DoEvents, some even say it should never be used, but the fact is that so far, for my goal, there is not other way to do this.

    If anyone reading this knows that there is an intrinsic bug in DoEvent, please explain it.
    What I understand is that sometimes it's difficult to handle reentrancy properly, but there is no bug with DoEvents itself.

    Of course that it can cause problems, because virtually anything can happen (and change the state) after a DoEvents call.

  27. #27
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: ways to provide a synchronous response from an asynchronous task

    I don't think anyone has suggested there is any "bug" in the DoEvents() function.

    It is just a very costly and hazardous option, and one that can be avoided in most but not all cases. DoEvents() does a Sleep 0 in addition to the many other things it does. People often try to reduce its cost by adding a longer Sleep() next to it so it gets called less frequently, or they'll try only making the call every so many iterations of the busy-wait loop. Disabling most of the user interface helps avoid some of the more minor re-entrance time bombs.


    Typically:

    If you have some sort of request/response scenario, you dispatch your async request and then in your async response event handler you process the response when it arrives. Pretty simple.

    If the response can trigger another request, then issue the subsequent request in the response event handler. This normally means tracking state as you go so that when subsequent responses arrive the code knows which it is dealing with.

    In some cases you might build a work queue of multiple requests, then initiate the first one. As responses arrive they get processed and then the next request is issued. This continues until the work queue is empty.


    It sounds like you are committed to a QBasic-style straight line code pattern. To force-fit it you plan to use buzz loops with DoEvents() calls. Sure seems like a house of straw to me, but I don't think anyone is going to dissuade you.

  28. #28

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    The pseudo code a bit more complete, now handling reentrancy:

    Code:
    Private mStep As Long
    Private mCancelled As Boolean
    
    Public Function GetSomething(nSomeParameter) As cWhatever
        ' if there is a previous task running, then wait until it finishes
        ' in this example we run only one task at a time
        Do Until Not tmrDoTask.Enabled
            DoEvents
            If mCancelled Then Exit Function
        Loop
        
        If Not (nSomeParameter = mLastCallParameter) Then ' if yes, we already have the result ready from the last call
            tmrDoTask.Interval = 1
            mStep = 0
            tmrDoTask.Enabled
            Do Until Not tmrDoTask.Enabled
                Sleep 1 ' I doubt it's neccesary here
                DoEvents
                If mCancelled Then Exit Function
            Loop
        End If
        Set GetSomething = mResult
    End Function
    
    Private Sub tmrDoTask_Timer()
        DoTask
    End Sub
    
    Private Sub DoTask()
        Static sFinished As Boolean
    
        If mStep = 1 Then
            GoTo Step1
        ElseIf mStep = 2 Then
            GoTo Step2
        ElseIf mStep = 3 Then
            GoTo Step3
        End If
    
        ' the task begins here
        ' do initializing and first things
        mStep = 1
        Exit Sub
    
    Step1:
        If mCancelled Then tmrTask.Enabled = False: Exit Sub
        sFinished = False
        Do Until sFinished
            ' do something
            mStep = 2
            Exit Sub
    
    Step2:
            If mCancelled Then tmrTask.Enabled = False: Exit Sub
            ' do some other thing
            If SomeCondition Then sFinished = True
            mStep = 3
            Exit Sub
    
    Step3:
        Loop
        Set GetSomething = [Some Result] ' Set the result
        tmrTask.Enabled = False
    End Sub
    
    Public Sub Cancel()
        mCancelled = True
    End Sub

  29. #29
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Eduardo- View Post
    So all that is to avoid the 15 ms that in some cases Sleep may waste.
    Yes, some async actions (e.g when you wait for the response of a TCPServer sitting in a LAN or on localhost)
    are finished within 1-2msec.

    In that case you would seriously limit the overall-responsetime of your outer (synchronous) function,
    when you are using Sleep 15 (instead of using MsgWaitxxx or some similar API).

    Quote Originally Posted by Eduardo- View Post
    But how to know what object handle to provide, ...
    The MsgWaitxxx... example (as posted) doesn't need to be provided with a Handle -
    it works generically in such loops (when used as shown) - as a faster reacting alternative to the Sleep-API.

    Olaf

  30. #30

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by dilettante View Post
    It sounds like you are committed to a QBasic-style straight line code pattern. To force-fit it you plan to use buzz loops with DoEvents() calls. Sure seems like a house of straw to me, but I don't think anyone is going to dissuade you.
    Off course you can dissuade me!!!

    You can come up with your house of bricks and modern code (not QB-like) and tell me how would you provide a synchronous response from an asynchronous task given the conditions and goals already explained here, and of course, without any call to DoEvents.

  31. #31

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Schmidt View Post
    The MsgWaitxxx... example (as posted) doesn't need to be provided with a Handle -
    it works generically in such loops (when used as shown) - as a faster reacting alternative to the Sleep-API.

    Olaf
    Ahh, OK, it's like Sleep but it returns early if there is any event.
    Not I understand. It is good to know about it.

    I had see that function but I thought that it worked only with handles.
    Thanks.

  32. #32

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Eduardo- View Post
    (not QB-like)
    I think dilettante is talking about the GoTo's, another thing that some people hate and sometimes are very useful or even neccesary.

    Of course that routine could be redesigned to avoid the GoTo's, but I don't have that problem because I'm not a GoTo hater.

  33. #33
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Eduardo- View Post
    I think dilettante is talking about the GoTo's, another thing that some people hate and sometimes are very useful or even neccesary.

    Of course that routine could be redesigned to avoid the GoTo's, but I don't have that problem because I'm not a GoTo hater.
    I'm not a GoTo-hater but It still doesn't read very nicely (at least to me)...

    Here's a working example, on how I'd do your "multi-step"-async-stuff:

    Into a Class, named cAsyncWrap:
    Code:
    Option Explicit
    
    Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
    
    Public Enum eAsyncTaskState
      tsk_None
      tsk_InitDone
      tsk_Step1
      tsk_Step2
      tsk_Step3
    End Enum
    
    Private Type tAsyncTask
      State As eAsyncTaskState
      Cancelled As Boolean
      TimedOut As Boolean
      FinalResult As Variant
      ErrorString As String
    End Type
    
    Private mTask As tAsyncTask
     
    Public Function GetSomething(nSomeParameters)
        If mTask.State <> tsk_None Then Err.Raise vbObjectError, , "a task is still running"
        On Error GoTo 1
        
          DoInits nSomeParameters
          DoStep tsk_Step1
          DoStep tsk_Step2
          DoStep tsk_Step3
     
         GetSomething = mTask.FinalResult 'provide the Result
    1    mTask.State = tsk_None 'reset the State in either case (failure or not)
         mTask.ErrorString = Err.Description 'reflect the error-string (if there is one)
    End Function
     
    Public Sub Cancel()
      mTask.Cancelled = True
    End Sub
    
    Public Property Get State() As eAsyncTaskState
      State = mTask.State
    End Property
    
    Public Property Get LastErrorString() As String
      LastErrorString = mTask.ErrorString
    End Property
    
    'some Private Helper-Routines
    Private Sub DoInits(SomeInitParameters)
      mTask.FinalResult = Empty 'reset a few mTask.Members before the whole thing starts
      mTask.Cancelled = False
      mTask.TimedOut = False
      mTask.ErrorString = ""
      
      'do some other prior Class-Initializations (according to the passed Params)
      
      mTask.State = tsk_InitDone 'reflect the State-Increase (from prior state tsk_None)
    End Sub
    
    Private Sub DoStep(NextState As eAsyncTaskState)
      Select Case NextState
        Case tsk_Step1: 'trigger async actions for Step1 here (the Events will ensure that the mTask.State is increased)...
        Case tsk_Step2: 'trigger async actions for Step2 here (the Events will ensure that the mTask.State is increased)...
        Case tsk_Step3: 'trigger async actions for Step3 here (the Events will ensure that the mTask.State is increased)...
      End Select
      WaitFor NextState
    End Sub
    
    Private Sub WaitFor(NextState As eAsyncTaskState)
      Do Until mTask.Cancelled Or mTask.TimedOut Or mTask.State = NextState
        MsgWaitForMultipleObjects 0, ByVal 0&, 0&, 15, &H4FF '<- QS_ALLINPUT
        DoEvents
      Loop
      If mTask.Cancelled Then Err.Raise vbObjectError, , "Task was cancelled at step: " & NextState
      If mTask.TimedOut Then Err.Raise vbObjectError, , "Task timed out at step: " & NextState
    End Sub
    Thanks to the above class, the Form-Code is then reduced to:
    Code:
    Option Explicit
    
    Private AsyncWrap As New cAsyncWrap
    
    Private Sub Form_Load()
      Show
      
      Caption = "Waiting..."
        Dim Result: Result = AsyncWrap.GetSomething("foo")
      Caption = "Wait finished"
      
      If Tag = "x" Then Unload Me
    End Sub
    
    Private Sub Form_Click()
      AsyncWrap.Cancel
      If AsyncWrap.State Then Print "Cancelled at state: " & AsyncWrap.State
    End Sub
     
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
      If AsyncWrap.State <> tsk_None Then
        Cancel = 1
        If MsgBox("Async-Job still running, do you want to cancel it?", vbYesNo) = vbYes Then
          AsyncWrap.Cancel
          Tag = "x"
        End If
      End If
    End Sub
    Olaf

  34. #34

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Yes Olaf, I already said that it could be refactored to avoid the GoTo's, but they are there because I just modified a previous version of the function that used DoEvents inside instead of the Exit Sub's and the timer.
    But it could not handle reentrancy, that's why I made the changes.

    In my example the Step 1 would run only once, but the Steps 2 and 3 would run multiple times in a loop, I don't see that in yours.

    Also, your example relies in that the routine would trigger some asynch actions in every step, and it could be the case... or not...
    My example with the timer would work also in the case that every step performs a chunk of the whole task, even when these chunks are synchronous tasks each one.

    The asynchronous task in my example is the task my routine performs in chunks, not necessarily the individual actions that it triggers.

    But aside from that, I don't see that yours could handle reentrancy properly (and Lavolpe was right about that the two subjects are closely related).
    Why?
    In fact, the only thing you do with reentrancy is to raise an error.

    While (in my last example) I wait the prior task to finish and see if the result is the same that I need now, and if it isn't then I run another task. No error.

    Code:
    Public Function GetSomething(nSomeParameter) As cWhatever
        ' if there is a previous task running, then wait until it finishes
        ' in this example we run only one task at a time
        Do Until Not tmrDoTask.Enabled
            DoEvents
            If mCancelled Then Exit Function
        Loop
        
        If Not (nSomeParameter = mLastCallParameter) Then ' if yes, we already have the result ready from the last call
        '...
    [PS: OK, I could have put If (nSomeParameter <> mLastCallParameter) Then instead of If Not (nSomeParameter = mLastCallParameter) Then]

    The whole problem was in order to allow reentrancy, if I don't allow reentrancy just a few DoEvents inside the loop will do it (to unlock the UI), as I had done before.

    There are already traces of how the routine was originally, just remove the Exit Sub's and replace them with DoEvents, and of course remove the GoTo's and the mStep variable.

    Because what are the goals? as the title says: to return a synchronous result of an asynch task. And your code does that. Using DoEvents.
    But aside that it uses DoEvents, that seems already unavoidable, you can't handle reentrancy.

    Why? Because when you enter the function for the second time, it is because it comes from some DoEvents call inside the same function, and you can't wait for the first call to complete with another Loop and DoEvent call because it will never return to the first DoEvent call. The second time it will wait forever.
    DoEvents will only work if there is any event to occur, that's where the timer comes into play.
    Last edited by Eduardo-; Apr 16th, 2017 at 06:55 PM.

  35. #35

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Here is a version without GoTo's, and yes, it's more clear:

    Code:
    ' It uses tmrDoTask, a Timer
    Private mStep As Long
    Private mCancelled As Boolean
    Private mLastCallParameter
    Private mResult as cWhatever
    
    Public Function GetSomething(nSomeParameter) As cWhatever
        ' if there is a previous task running, then wait until it finishes
        ' in this example we run only one task at a time
        Do Until Not tmrDoTask.Enabled
            DoEvents
            If mCancelled Then Exit Function
        Loop
        
        If nSomeParameter <> mLastCallParameter Then ' if they are equal we already have the result ready from the last call
            mStep = 0
            DoTask
            Do Until Not tmrDoTask.Enabled
                Sleep 1 ' I doubt it's necessary here
                DoEvents
                If mCancelled Then Exit Function
            Loop
        End If
        Set GetSomething = mResult
        mLastCallParameter = nSomeParameter
    End Function
    
    Private Sub tmrDoTask_Timer()
        DoTask
    End Sub
    
    Private Sub DoTask()
        Static sFinished As Boolean
        
        If mStep = 0 Then
            sFinished = False
            ' the task begins here
            ' do initializing and first things
            mStep = 1
            tmrDoTask.Interval = 1
            tmrTask.Enabled = True
        ElseIf sFinished Then
            Set mResult = [Some Result] ' Set the result
            tmrTask.Enabled = False
        ElseIf mStep = 1 Then
            If mCancelled Then tmrTask.Enabled = False: Exit Sub
            ' do something
            mStep = 2
        ElseIf mStep = 2 Then
            If mCancelled Then tmrTask.Enabled = False: Exit Sub
            ' do some other thing
            mStep = 1
            If SomeCondition Then sFinished = True
        End If
    End Sub
    
    Public Sub Cancel()
        mCancelled = True
    End Sub
    I didn't add error trace code for the sake of simplicity.
    Last edited by Eduardo-; Apr 16th, 2017 at 08:18 PM.

  36. #36

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    I realize that there is a problem with the above code.
    It is not as my real case, because in my real case if the input parameter changes, I abort the previous task. So I don't have that problem in my real case.

    But the above example wouldn't work right.
    The problem is here:
    Code:
        ' if there is a previous task running, then wait until it finishes
        ' in this example we run only one task at a time
        Do Until Not tmrDoTask.Enabled
            DoEvents
            If mCancelled Then Exit Function
        Loop
        
        If nSomeParameter <> mLastCallParameter Then ' if they are equal we already have the result ready from the last call
        ' ...
    If the parameter in the second call is the same as the one in the first, there will be no problem, the second call will retun first and then inmediately will return the first call with the right result also because both are the same.

    But if the parameter in the second call changes, the second call will return with the right result and the first call will return with the result of the second.

    To address that issue it would be neccesary to add a collection or array to hold the results already processed and their input parameters.

    Something like this:

    Code:
    ' It uses tmrDoTask, a Timer
    Private mStep As Long
    Private mCancelled As Boolean
    Private mResults as New Collection
    Private mCurrentCallParameter
    
    Public Function GetSomething(nSomeParameter) As cWhatever
        On Error Resume Next
        Set GetSomething = mResults(CStr(nSomeParameter))
        On Error Goto 0
        If Not GetSomething Is Nothing then ' We had already this result
            Exit Function
        End If
    
        If tmrDoTask.Enabled Then
            ' if there is a previous task running, then wait until it finishes
            ' in this example we run only one task at a time
            Do Until Not tmrDoTask.Enabled
                DoEvents
                If mCancelled Then Exit Function
            Loop
            
            ' Let's try again, perhaps the last call that just finished has set the right result that we need in this call already
            On Error Resume Next
            Set GetSomething = mResults(CStr(nSomeParameter))
            On Error Goto 0
            If Not GetSomething Is Nothing then ' The previous call did it
                Exit Function
            End If
        End If
    
        mCurrentCallParameter = nSomeParameter
        mStep = 0
        DoTask
        Do Until Not tmrDoTask.Enabled
            Sleep 1 ' I doubt it's necessary here
            DoEvents
            If mCancelled Then Exit Function
        Loop
        End If
        Set GetSomething = mResults(CStr(nSomeParameter))
    End Function
    
    Private Sub tmrDoTask_Timer()
        DoTask
    End Sub
    
    Private Sub DoTask()
        Static sFinished As Boolean
        
        If mStep = 0 Then
            sFinished = False
            ' the task begins here
            ' do initializing and first things
            mStep = 1
            tmrDoTask.Interval = 1
            tmrTask.Enabled = True
        ElseIf sFinished Then
            mResults.Add [Some Result], CStr(mCurrentCallParameter) ' Set the result
            tmrTask.Enabled = False
        ElseIf mStep = 1 Then
            If mCancelled Then tmrTask.Enabled = False: Exit Sub
            ' do something
            mStep = 2
        ElseIf mStep = 2 Then
            If mCancelled Then tmrTask.Enabled = False: Exit Sub
            ' do some other thing
            mStep = 1
            If SomeCondition Then sFinished = True
        End If
    End Sub
    
    Public Sub Cancel()
        mCancelled = True
    End Sub
    Last edited by Eduardo-; Apr 16th, 2017 at 11:04 PM.

  37. #37
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Eduardo- View Post
    In my example the Step 1 would run only once, but the Steps 2 and 3 would run multiple times in a loop, I don't see that in yours.
    Well, it'd be easy enough to put a loop around "Steps 2 and 3" if you want that -
    I was not trying to write an 1:1 replacement for your code - it was just a bunch of suggestions
    (because I don't really see the need of a Timer in your scenario).

    Quote Originally Posted by Eduardo- View Post
    Also, your example relies in that the routine would trigger some asynch actions in every step,
    If you need normal non-async-routines, it'd be equally easy to define and call them within the larger workflow -
    can't see the problem really... (there already is a synchronous routine in the workflow - "DoInits", it
    would be easy enough to e.g. introduce a DoSynchronousStep between DoStep 1 and DoStep 2)

    Quote Originally Posted by Eduardo- View Post
    But aside from that, I don't see that yours could handle reentrancy properly...
    It is properly handled in so far, that it doesn't allow it (by raising an error to the outside -
    and thus immediately leaving the function - denying any "stacking" of such calls).

    Anything other than immediately jumping out of such a (still processing, still waiting to finish the prior task)
    routine, is asking for trouble - so why not "suppress" it already in the GUI-code (by handling the error nicely,
    offering to cancel the prior action in case the new one has "rights of ways").

    Olaf

  38. #38

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    OK, never mind.
    My real code needs to handle reentrancy, not to raise an error.
    But it's already doing so, with the timer.

    The timer is necessary if you don't mind to "ask for trouble".
    But there is no trouble that I can see.

    You can study the last code that I posted if you are interested in undersdtanding how I solved it and why the timer is necessary.
    Last edited by Eduardo-; Apr 17th, 2017 at 08:40 PM.

  39. #39
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Eduardo- View Post
    OK, never mind.
    My real code needs to handle reentrancy, not to raise an error.
    But it's already doing so, with the timer.

    The timer is necessary if you don't mind to "ask for trouble".
    But there is no trouble that I can see.

    You can study the last code that I posted if you are interested in undersdtanding how I solved it and why the timer is necessary.
    I've looked at it - and the last version is even worse than the previous ones.
    There's so many things wrong with it - I don't know where to start, seriously (I'm not trying to wind you up here)...

    I guess if you'd try to make a real Demo out of your "air-code" (something that works,
    using some Form-Code plus some Class-Code - something which mimicks your real scenario as best as it can),
    you'd see it yourself easily.

    Until then I also bow out of this discussion - and from what you wrote last - maybe LaVolpe was right,
    in what you *really* want to do, is "to handle multiple asynchronous Jobs parallel" -
    and for that purpose the threads topic (how to write a synchronous functions which waits for async results)
    is not the proper approach - because it would be "stack-based" (and "stack-up", when called multiple times before being finished)...

    I can only speculate that in your real scenario, you have to handle several uploads in parallel or something...
    and for that a Queue-based approach (not a Stack-based one as the thread is about) would be the better solution.

    And a Queue-based approach is better handled in a truly asynchronous manner (e.g. by a Class
    which just offers an "AddJob" Method on the outside - then placing these Jobs in an internal Queue-Collection,
    and then handling everything it has in that Queue internally - raising Events for the Jobs which are finished,
    and just "idling" when nothing is in the Queue anymore). And that approach would be doable without any
    DoEvents, just using a Class-internal timer...

    Olaf

  40. #40

    Thread Starter
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: ways to provide a synchronous response from an asynchronous task

    Quote Originally Posted by Schmidt View Post
    I've looked at it - and the last version is even worse than the previous ones.
    There's so many things wrong with it - I don't know where to start, seriously (I'm not trying to wind you up here)...
    I don't see enything wrong with it.
    And since you are not pointing one single problem that you say you see...

    Quote Originally Posted by Schmidt View Post
    I guess if you'd try to make a real Demo out of your "air-code" (something that works, using some Form-Code plus some Class-Code - something which mimicks your real scenario as best as it can),
    you'd see it yourself easily.

    Until then I also bow out of this discussion - and from what you wrote last - maybe LaVolpe was right,
    in what you *really* want to do, is "to handle multiple asynchronous Jobs parallel" -
    and for that purpose the threads topic (how to write a synchronous functions which waits for async results)
    is not the proper approach - because it would be "stack-based" (and "stack-up", when called multiple times before being finished)...

    I can only speculate that in your real scenario, you have to handle several uploads in parallel or something...
    and for that a Queue-based approach (not a Stack-based one as the thread is about) would be the better solution.

    And a Queue-based approach is better handled in a truly asynchronous manner (e.g. by a Class
    which just offers an "AddJob" Method on the outside - then placing these Jobs in an internal Queue-Collection,
    and then handling everything it has in that Queue internally - raising Events for the Jobs which are finished,
    and just "idling" when nothing is in the Queue anymore). And that approach would be doable without any
    DoEvents, just using a Class-internal timer...

    Olaf
    No, they are not uploads.
    The uploads are queued, as you said, and they have nothing to do with this. They are another part of the program and I not working on that yet.

    As I explained somewhere else in this thread, the synchronous functions works in order LIFO, as everything that is synchronous would do.
    There is no other way to do it anyway (if one wants to stay with synchronous functions).

    Real code I don't have to post, propose something and I'll do it, I have no problem.
    I would prefer something very close to the example I posted.

    But I think the example I already posted should serve to understand the logic.
    I can comment it more if you want.

    I guess you could do more or less the same launching new threads, but I'm doing this without launching new threads (and without adding dependency files).

    My real code is not too similar to this sample pseudocode.

    In my real case I have many different functions. When they are called, most of the functions (but not all) need to have an "environment" already set up. And doing this can take some time (several seconds).
    I do it the fist time that one of those functions are called.

    The problem is that while the program is doing this, while waiting for some asynch calls (WinHTTP requests) to finish and also for not locking the client program, I need to call DoEvents, and since I do it, in that time I could receive a call to other of these functions (while I'm setting up the environment).
    This second function needs also that enviroment ready, but that task was triggered already by the first function, so what can I do? Since these functions are all synchronous to the client program, I just need to wait until the environment is ready. Not only for the fist function that triggered it, but now also for the second.

    Of course, I could add a method PrepareEnvironment that the client program must call and wait until it finishes before calling any of these functions, but I wanted to automate that.

    I hope I didn't confuse you more. My sample posted here I think is easier.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width