Do Macros run serially or in parallel?

Hi,

In opening my database accounting system, I trigger via the Form: When
loading event, a macro, (Sub Init()), containing the database
initialisation routines.

As the Form loads it displays the first record in the database, including
the account number. To make things easier for the user, the Text modified
event on the account number control, triggers a macro, (Sub AccName()),
that displays the account name.

Sometimes the system has crashed because it seems that Sub AccName() has
fired before Sub Init() has finished. (Sub AccName() depends on some of
the structures that Sub Init() sets up.)

Is it possible that both Subs are running in parallel or do they always run
in series on a first come first served basis?

If the latter, are there coding techniques that can be used to ensure that
Sub Init() always runs first?

If the former, how can I ensure that Sub AccName() waits until Sub Init()
has finished?

Can someone point me to any articles on this subject?

Thanks,

Noel

Sadly, I do not have a definitive answer to your question, but I do have some thoughts... (see inline)

Hi,

In opening my database accounting system, I trigger via the Form: When
loading event, a macro, (Sub Init()), containing the database
initialisation routines.

As the Form loads it displays the first record in the database, including
the account number. To make things easier for the user, the Text modified
event on the account number control, triggers a macro, (Sub AccName()),
that displays the account name.

And I assume that this is built into the form.

Sometimes the system has crashed because it seems that Sub AccName() has
fired before Sub Init() has finished. (Sub AccName() depends on some of
the structures that Sub Init() sets up.)

Is it possible that both Subs are running in parallel or do they always run
in series on a first come first served basis?

Warning: Speculation... So insert the following text before each statement below: "I am making a wild unsubstantiated guess that"

1. The form is populated before Init() finishes
2. AccName() is called as soon as text is pushed into the control.

So, if this is the case, then AccName may be called before Init() finishes. If you really want to know if they run at the same time, you could do a not so quick test and modify each routine so that the macro does something like this:

Sub Init()
   Write current time to a file
   delay a second or two
   Do the work
   delay a second or two
   Write the current time to a file
End Sub

Use a different file for each subroutine, then compare the start and end time to see if they overlap. You can probably get away with using a simple print statement at the start and end.

If the latter, are there coding techniques that can be used to ensure that
Sub Init() always runs first?

If things run serially and AccName is called based on something done in Init(), it may be possible that Init() cannot complete until after AccName() finishes.

I suppose that another possibility is to set a "Global" variable such as "InitComplete". set to false when Init() starts, and set to true when it completes. Modify AccName to check for InitComplete. If things are serial, then you may need to simply return. If not, then have AccName wait for a few seconds and try again. If things are truly serial, then I would expect them to fail every time, and they don't fail every time, so it is likely not the case.

If the former, how can I ensure that Sub AccName() waits until Sub Init()
has finished?

Oh wait, see above! :slight_smile:

Hi :slight_smile:
I was just about to point out documentation at
https://wiki.documentfoundation.org/Documentation/Other_Documentation_and_Resources#Programmers
It's a 3rd party guide rather than one produced by the official
documentation team. However, it's written by Andrew Pitonyak and he
has done work in the docs team.

I normally recommend that people read through that while waiting for
an answer from one of the experts but Andrew has already replied.
Also i think i have handed that link out before so you've probably
already tried to find something relevant in there (or done my usual
trick of posting first and then reaching for documentation)
Regards from
Tom :slight_smile:

Thanks for the replies to my original post.

They gave me something to work on and I have made some progress. I think
that the macros run in series, but I am not yet sure of that.

For some reason the wait(2000) commands that I inserted into the the macros
don't seem to work and I am getting several lines with the identical time
stamps, like this -
1. 03/04/2014 23:44:57
2. 03/04/2014 23:44:57
3. 03/04/2014 23:44:57

I looked up the formatting for time stamps and
DD:MM:YYYY hh:mm:ss.00 is supposed to give fractions of a second, but I
don't know how to apply this to the Now() function.

The code I am using to print to the text files is -

     giInit = FreeFile()
     Open "D:\LibreOffice\InitfTransEntry.txt" for Output as giInit
     Print #giInit, "1. " + Now()

Any suggestions as to how I can format Now(), or should I use a different
function to get more accurate timing?

Thanks,

Noel

So, are you saying that you have:

Print #giInit, "1. " + Now()
Wait(2000)
Print #giInit, "2. " + Now()

And it prints the same value for now?

Hi Andrew,

Yes, that is what seems to be happening. Even if there is a bit of code
between the Wait(2000) and the next Print #giInit, the time is the same.
Only if there is substantial code after the Wait(2000) does the time
change. For example -
1. 04/04/2014 13:50:24
2. 04/04/2014 13:50:24
3. 04/04/2014 13:50:24
4. 04/04/2014 13:50:24
5. B4 UpdateFundAndGenLed() 04/04/2014 13:50:24
6. 04/04/2014 13:50:28
7. 04/04/2014 13:50:28

After 5. the Init macro calls another macro.

Something odd seems to be happening with the Wait command. Perhaps I am
missing something. You can see why I was wanting to get fractions of a
second in the print statements.

Noel

Noel

A question inline.

Alex

Hi,

In opening my database accounting system, I trigger via the Form: When
loading event, a macro, (Sub Init()), containing the database
initialisation routines.

As the Form loads it displays the first record in the database, including
the account number. To make things easier for the user, the Text modified
event on the account number control, triggers a macro, (Sub AccName()),
that displays the account name.

Do you do this because the table associated with the form doesn't contain the Account Name, so you have to get the account name from some other table?

Noel , Andrew,

What system are you running ?

on windows the wait is doing OK

try this code

s1 = "1. " + Now()
Wait(2000)
s2 = "2. " + Now()
msgbox (s1 & chr(13)& s2)

s1 an s2 differs from 2 seconds (wait 2000)

Greetz

Fernand

Yes, I did the same test on Linux, and it works there as well.

Sub NowTest
   Dim s As String
   s = "1. " & Now()
   Wait(2000)
   s = s & CHR$(10) & "2. " & Now()
   MsgBox s
End Sub

Can you try running this method, and, then, after that works, use similar code in your event handler to print a message to the screen in the two places of interest to see if maybe the wait command fails to work in an event handler.

I did a quick test to see if the NOW function returned a value with precision greater than one second, and, it seems that it does not. if it did, then waiting for 500 milliseconds should clearly show fractional seconds, yet, my fractional seconds are always on the order of 10^-7

Sub NowTest
   Dim s As String
   Dim l As Double
   l = Now
   s = "1. " & Now() & " = " & l
   Dim dSec As Double
   Dim lSec As Long
   lSec = CLng((l*1440 - CLng(l*1440)) * 60)
   dSec = (l*1440 - CLng(l*1440)) * 60 -lSec
   s = "1. " & Now() & " = " & dSec

   Wait(1000)
   l = Now
   lSec = CLng((l*1440 - CLng(l*1440)) * 60)
   dSec = (l*1440 - CLng(l*1440)) * 60 -lSec
   s = s & CHR(10) & "2. " & Now() & " = " & dSec
   MsgBox s
End Sub

Oh, and you can use GetSystemTicks() to return the number of system ticks, which should be a better judge of when something occurred. Also, if Wait does not work, you can always use a busy wait based either on "now" or GetSystemTicks.

Hi,

Sorry I've been slow to respond to all your posts. It has taken me a while
to sort out what I think is happening.

Fernand, thanks for your code suggestion. I pasted it into the start of my
macros and the Wait() statement worked perfectly. Then I incorporated it
into my Init macro and discovered that none of my Wait() statements
worked. The same applied to the AccName macro. I was really puzzled and
probably spent longer than I should have on the problem, but I felt I
really needed an answer. Finally, through much trial and error I found
which macros Wait() worked in and which it didn't. My testing was not
exhaustive, but it seems that Wait() doesn't work in any macro that is
called from one of the Form's events. When I got back to reading my
emails, I discovered that Andrew was predicting that this might be the
case!

Then it occurred to me that, rather than use separate text files for
printing out the Now() values, if I used just one file, it would give me
the order of processing, and the exact times would not be so important. So
I did that and also added System Ticks / 1000. The result was as follows -

Init 1. 05/04/2014 23:20:37 43421.801
Init 2. 05/04/2014 23:20:37 43421.801
Init 3. 05/04/2014 23:20:37 43421.817
Init 4. 05/04/2014 23:20:37 43421.833
Init 5. 05/04/2014 23:20:37 43421.848
Init 6. 05/04/2014 23:20:40 43424.36
Init 7. 05/04/2014 23:20:40 43424.375
Name 1. 05/04/2014 23:20:40 43424.625
Name 2. 05/04/2014 23:20:40 43424.625
Name 3. 05/04/2014 23:20:40 43424.625

This demonstrates that macro Init ran before macro Name.

From this, and a number of other tests I ran, I believe that -

1. Macros are always processed serially on a first come first served basis.
2. If macro A calls macro B, then Macro A waits until macro B has
completed before the remaining code of macro A is processed.

These were the conclusions that I was hoping for. As well, I now have a
mechanism for checking out other timing situations where I'm not sure what
is going on.

So thank you all for your input - it has been an interesting journey!

Noel

Hi,

Sorry I've been slow to respond to all your posts. It has taken me a while
to sort out what I think is happening.

Many of us are very busy and totally understand when it takes time to respond!

Fernand, thanks for your code suggestion. I pasted it into the start of my
macros and the Wait() statement worked perfectly. Then I incorporated it
into my Init macro and discovered that none of my Wait() statements
worked. The same applied to the AccName macro. I was really puzzled and
probably spent longer than I should have on the problem, but I felt I
really needed an answer. Finally, through much trial and error I found
which macros Wait() worked in and which it didn't. My testing was not
exhaustive, but it seems that Wait() doesn't work in any macro that is
called from one of the Form's events. When I got back to reading my
emails, I discovered that Andrew was predicting that this might be the
case!

So if you really wanted wait to work inside an event handler, you would need to write your own and then simply perform a busy wait. Something like this code that won't work as written (too lazy to look up syntax and such)

tempLong = getSystemTicks()
StopWhen = tempLong + someDeltaValue
Do While StopWhen < getSystemTicks()
   Do something to eat a bit of time
   For i = 0 To 1000
       For j = 0 To 1000
           doubleTemp = i + j
       Next
   Next
End

Then it occurred to me that, rather than use separate text files for
printing out the Now() values, if I used just one file, it would give me
the order of processing, and the exact times would not be so important. So
I did that and also added System Ticks / 1000. The result was as follows -

I purposely suggested two different files because.... if things were not serial, then you might have two places attempting to write to the same file at the same time (concurrency problem). My concern was that:

1. Macro A updates the form and control returns to Macro A that continues to run.
2. The form event is triggered calling Macro B before Macro A finishes.

So, if one macro updated a field on a form, which caused an event to be triggered, then the triggered event might be running in a separate thread (at the same time) as the macro that updated the field. In fact, if this was not the case, then I expected that the behavior would always be the same, and it sounded like the behavior was not always the same for you.

Hi Noel

So are you trigering these events and running the macros to populate a form from two separate tables?

Alex

Hi Andrew,

I had forgotten about the possibility of concurrency. So I did some more
testing and discovered that I was a bit too quick in coming to the
conclusions outlined in my last post.

I ported my system, (from Paradox), a couple of years ago and did have
concurrency problems then. I did what you suggested a few posts back and
used a global variable, (gbinitfTransEntry), to indicate when the
initialisation routines were in progress. If gbInitfTransEntry was false,
I simply exited the relevant Sub and repeated it at the end of the
initialisation process. In my first lot of testing I thought that I had
disabled all the places where this was happening, but there was one I
missed and as a result it looked like the problem no longer existed.

As an aside, is there any utility available for printing out a list of all
of the subs that are called from a Form's events? The one I missed was on
a Sub Form control. I had checked all the other controls on the form, but
missed that one! (You can get at Forms and Sub Forms, only by using the
Form Navigator button.)

Anyway, I stumbled over the gbinitfTransEntry code I had missed, disabled
the Exit Sub line, and the Name Sub started crashing because the
Initialisation had not been completed. By setting an appropriate
breakpoint in the code I found that the Name Sub called from the Sub Form
control Event, (After record change), went straight to the top of the Calls
list, stalling the Init Sub that had been processing. I tried your
suggestion of a Busy Wait, but while it did create a delay, as far as I can
tell, there does not seem to be any way to enable the Init Sub to proceed
until the Name Sub has concluded. (To achieve that I had to reinstated the
Exit Sub line. Otherwise the system just crashed.)

So, unless proved otherwise, I think that using a global variable to exit
certain Subs and repeat them later, is the only way to overcome this sort
of problem.

What prompted my question in the first place was attempting to run the
accounting system on a Windows XP PC. (My PC runs Windows 7.) On XP I was
getting what I thought might have been concurrency or timing errors. I now
have some tools that will no doubt help iron out those problems.

Thanks again for your help.

Noel