Timer & Threads

General FreeBASIC programming questions.
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Timer & Threads

Postby Dinosaur » Mar 26, 2017 0:12

Hi All

I am test running all my USB routines in a different thread.
Although a Dinosaur, this is my first ever attempt to use threads.

Using the examples I have been able to get a small dedicated program to work, such that
the output on an IO board was stable within 0.2 mSec at 500 msec. (confirmed with my PicoScope)

However, I seem to have problems with the Timer between the threads.
Can someone please tell me what the rules are about calling the Timer in both threads.
What is the best way to secure the time in both threads.?
Do I use the Main thread timer or create another in the second thread ? and if so, what happens when both try to access the Timer
at the same time.?

Do I really have to MutexLock every call to the Timer ?

Generally I do the following:

Code: Select all

Times.CalTime = Timer
Sleep 1000
Do      'Main Program Loop
   Times.mSec = (Timer - Times.CalTime) * 1000
   ' handle all other routines below here
   'and use the Times.mSec as a reference.
Loop


Regards
vdecampo
Posts: 2981
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Re: Timer & Threads

Postby vdecampo » Mar 26, 2017 0:36

You shouldn't have to mutex reading the timer, but IIRC the timer only has a resolution accuracy of 15ms. Also, at least under Windows, you cannot guarantee a thread will start executing immediately when it is called.

-Vince
deltarho[1859]
Posts: 1238
Joined: Jan 02, 2017 0:34
Location: UK

Re: Timer & Threads

Postby deltarho[1859] » Mar 26, 2017 3:37

but IIRC the timer only has a resolution accuracy of 15ms.

With a Wintel system the System Clock ticks at 64Hz (15.625ms). FB's Timer on my Wintel system is linked to the Performance Counter. Interestingly, PowerBASIC's Timer, on the other hand, is linked to the System Clock.
fxm
Posts: 8061
Joined: Apr 22, 2009 12:46
Location: Paris suburb, FRANCE

Re: Timer & Threads

Postby fxm » Mar 26, 2017 6:02

If you start multiple threads with their own timer code as above, as long as each thread uses its own "Times" object, there should be no problems.
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Re: Timer & Threads

Postby Dinosaur » Mar 26, 2017 12:36

Hi All

Many thanks for the replies.
I don't know what my timer is linked to in the Fitlet i , but I can get 0.2 msec reliability by doing a Tx to an I/O board
and then at 500 mSec another Tx on LINUX.
It will sit at exactly 500 mSec for minutes and then perhaps throw a 500.2.

Not having done threading before, I get a lot of segment violations when I try things, and sometimes I simply
don't see the reason.

May have to fire more questions on this subject.

Regards
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Re: Timer & Threads

Postby Dinosaur » Mar 26, 2017 17:44

Hi All

Need clarification on the use of opened ports between threads.
For example the code below works.

Code: Select all

Sub USBthread( ByVal userdata As Any Ptr )
    Dim As Integer Handle = Cint(userdata)
    Dim As String TxStr,RxStr
   Dim as Double StartTime
   '--------------------------------------
   TxStr = "!00306" + Chr(&H0d)               'Turn a bit ON
   Print #Handle,TxStr;
    StartTime = Timer * 1000
    Do
      If (Timer * 1000) - StartTime >= 500 Then
         TxStr = "!00406" + Chr(&H0d)         'Turn the same bit OFF
         Print #Handle,TxStr;
         Exit Do
      EndIf
    Loop
End Sub

   Times.CalTime = Timer
   Sleep 1000
   Times.mSec  = (Timer - Times.CalTime) * 1000
   Times.StartTime = Times.mSec
   USBInitialize   
   Dim as Any Ptr handle                     'handle for thread
   Dim As Integer usbHandle         
   usbHandle = USBInit.Handle                  'handle assigned after usb is initialized
   Dim as String TxStr
    Do
      Times.mSec  = (Timer - Times.CalTime) * 1000
      If Inkey <> "" then   Exit do
      If Times.mSec - Times.StartTime > 1500 Then      'every 1.5 sec
         Times.StartTime = Times.mSec
         TxStr = "!00306" + Chr(&H0d)
         Print #USBHandle,TxStr;               'here we send data in the main thread
         Print ".";
         handle = ThreadCreate(@USBThread,CPtr(Any Ptr,USBHandle))   'Start Output & Wait for 500 mSec
      EndIf
      If handle = 0 Then Exit Do
   Loop
   If handle <> 0 Then
      Print "Wait for Quit"
      ThreadWait(handle)
      Print "Gotit"
   EndIf
   End


If I implement this into my "CGUI / Allegro" code the program logic works only if I dont try to use the port.
Even if I stop using the port 150 mSec either side of needing it in the other thread, the logic follows the path,
but the command that turn the bits On / Off dont work. Yet the usbhandle is still correct.

I either have to migrate all usb control to another thread or solve the conflict.

Any suggestions ??

Regards
fxm
Posts: 8061
Joined: Apr 22, 2009 12:46
Location: Paris suburb, FRANCE

Re: Timer & Threads

Postby fxm » Mar 26, 2017 18:21

Without making judgments about the security of the code (threads synchronized together only by a timing to avoid 'Print #' collisions), have you try to insert a 'Sleep' instruction in the two loops ?
For example a 'Sleep 1' just before each 'Loop' instruction.
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Re: Timer & Threads

Postby Dinosaur » Mar 26, 2017 19:11

Hi All

fxm, yes I have.

However, I am happy to receive constructive criticism.
My main thread has a Select Case sequence where it has to wait in a step for the usb thread to finish.
I have "synchronised" the threads by placing a ThreadWait there, but alas still no joy.

Am I correct in assuming that all global variables are available to the other thread ?
If not what are the restrictions.

Understandably, I could be modifying a variable in one thread and reversing it in another, but I can deal with that.
Right now I am migrating all usb comms to the other thread.

Regards
fxm
Posts: 8061
Joined: Apr 22, 2009 12:46
Location: Paris suburb, FRANCE

Re: Timer & Threads

Postby fxm » Mar 26, 2017 19:45

You may also be interested in the following for the global variables versus threads:
viewtopic.php?p=220198#p220198
viewtopic.php?f=2&t=21631
Last edited by fxm on Mar 27, 2017 8:35, edited 2 times in total.
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Re: Timer & Threads

Postby Dinosaur » Mar 26, 2017 21:49

Hi All

Managed to get it all working by putting all the usb comms in one thread.
The accuracy has suffered a little bit (compared to just one command in another thread).
My test program had 0.2 mSec accuracy / stability.
When applied to my complete application it is now 1 mSec.
But that is a huge improvement over 20 mSec.

fxm, thanks for the link, it amazes me how you guys just come up with these links, and no amount
of searching by me brings them up.

Will read that now, to see what unexpected traps are waiting for me.

Thanks to all for the help.

Regards
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Re: Timer & Threads

Postby Dinosaur » Mar 28, 2017 0:01

Hi ALL

After reading the links provided by fxm, I am confused (easy when you are 70).
Everything is working, but if I am to rely on it to run all day without fail, I need to make sure I am not breaking rules.

I had the impression that MutexLock / MutexUnlock allowed the locking of a single variable or udt or more.
However I can't see how to lock for example a single udt, without affecting the rest of the main thread.

So, I have a udt in the main thread called IOCtrl.
I read this udt in the usb thread to set io bits. Read only, I am not modifying it.

Is it the case that everything between the Lock / Unlock statements is locked ?
So:

Code: Select all

MutexLock
   If IOCtr.PortBit(1) > 0 then
      ' do whatever but only with this threads variables.
   EndIf
MutexUnlock
Does the Main thread keep working, until it gets to using IOCtrl, and then stop because it is locked ??
In other words, if I don't access IOCtrl in the Main thread for 1 hour, will it never encounter the lock during this hour ? and thus happily carry on.?

Regards

EDIT:
How does one know if a MutexLock was successful ? If the other thread does not not MutexUnLock, what happens to the other thread.
Does it simply halt until it is unlocked ? That would be disastrous.
fxm
Posts: 8061
Joined: Apr 22, 2009 12:46
Location: Paris suburb, FRANCE

Re: Timer & Threads

Postby fxm » Mar 28, 2017 8:09

Dinosaur wrote:I am confused (easy when you are 70).
I am 66.


When several threads (including the main thread) access to a same data and that access conflicts must be avoid, a mutex must be created (inducing a mutex handle, for example 'mid1').
In all concerned threads, each code part accessing this data must be enclosed inside a [MutexLock(mid1) ... MutexUnlock(mid1)] block.
At any time, only one among all concerned threads can executed its own instructions enclosed in the [MutexLock(mid1) ... MutexUnlock(mid1)] block.
There is not mutual locking between different mutexes (with different mutex handles).


Dinosaur wrote:So, I have a udt in the main thread called IOCtrl.
I read this udt in the usb thread to set io bits. Read only, I am not modifying it.

Is it the case that everything between the Lock / Unlock statements is locked ?
So:

Code: Select all

MutexLock
   If IOCtr.PortBit(1) > 0 then
      ' do whatever but only with this threads variables.
   EndIf
MutexUnlock
Does the Main thread keep working, until it gets to using IOCtrl, and then stop because it is locked ??
In other words, if I don't access IOCtrl in the Main thread for 1 hour, will it never encounter the lock during this hour ? and thus happily carry on.?
Yes for all.

In your example, we can restrict the locking to the only concerned IOCtrl access by using a local memory:

Code: Select all

MutexLock(mid1)
   Dim As ... IOCtrPortBit1 = IOCtr.PortBit(1)
MutexUnlock(mid1)
If IOCtrPortBit1 > 0 then
   ' do whatever but only with this threads variables.
EndIf


Dinosaur wrote:How does one know if a MutexLock was successful ? If the other thread does not MutexUnLock, what happens to the other thread.
Does it simply halt until it is unlocked ? That would be disastrous.
Yes.

The code inside a [MutexLock(mid1) ... MutexUnlock(mid1)] block must be as short as possible.
You can use use different mutexes (with different handles), one for each independent part of global variables (no mutual locking between different mutexes).
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Re: Timer & Threads

Postby Dinosaur » Mar 28, 2017 14:15

Hi All

fxm, hope you don't mind me clarifying this to the point that I understand it thoroughly.

Main Thread uses IOCtrl in hundreds of places, and NO MutexLock is used.

USB Thread uses IOCtrl in one place only.
My options:

1. In usb thread
MutexLock(udtLock)
Copy IOCtrl to USBIOCtrl 'get Main thread udt
MutexUnLock(udtLock)

Do whatever .........

MutexLock(udtLock)
Copy usbIOCtrl to IOCtrl 'restore Main thread udt
MutexUnlock


In Main thread do nothing, and pay the penalty of sometimes the IOCtrl being locked.

REgards
fxm
Posts: 8061
Joined: Apr 22, 2009 12:46
Location: Paris suburb, FRANCE

Re: Timer & Threads

Postby fxm » Mar 28, 2017 16:45

I understood that:
- Only your 'USBthreadSub()' uses a mutex.
- Your main program (corresponding to the main thread) does not use mutex.

1)
The main program (main thread using none mutex) is never blocked:
- The blocking occurs only when there are at least two [MutexLock ... MutexUnlock] blocks in execution competition at the same time.
- Any code outside a [MutexLock ... MutexUnlock] block is never blocked for execution.
- Only code inside a [MutexLock ... MutexUnlock] block may be blocked for execution (if there is execution competition with another mutex block).

2)
If there is always only one running occurrence of the 'USBthreadSUB()', the included [MutexLock ... MutexUnlock] blocks are useless because there is no possibility of competition between those.

3)
If you want an IOCtrl access exclusion between USB thread and Main thread, the two must have [MutexLock ... MutexUnlock] blocks around the IOCtrl accesses to preserve.
Dinosaur
Posts: 1077
Joined: Jul 24, 2005 1:13
Location: Searcy AR USA
Contact:

Re: Timer & Threads

Postby Dinosaur » Mar 28, 2017 17:03

Hi All

fxm

The usb thread runs all day, and simply has to use the IOCtrl state of bits to set outputs.
Within the usbThread it calls a TxRx subroutine approximately every 25 mSec
It should do this all day.

So, what is the danger of not using any Mutex blocks.?
Is it that the Main may set a bit, and the usb thread won't see that bit until perhaps the next time (25 mSec later) ?

If that is so, and the usb thread itself is safe, then I am happy to leave mutex alone.
The purpose of the usb thread is to improve the accuracy of the "On Time" of certain bits.
Main sets the bit to ON in IOCtrl udt, and the usb thread turns it off when the time has elapsed.
Having an accurate "ON Time" that happens 25 mSec later is not a problem.


Regards

Return to “General”

Who is online

Users browsing this forum: No registered users and 4 guests