Timer & Threads

General FreeBASIC programming questions.
Dinosaur
Posts: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Timer & Threads

Post by Dinosaur »

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: 2992
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Re: Timer & Threads

Post by vdecampo »

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: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Timer & Threads

Post by deltarho[1859] »

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
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Timer & Threads

Post by fxm »

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: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Re: Timer & Threads

Post by Dinosaur »

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: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Re: Timer & Threads

Post by Dinosaur »

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
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Timer & Threads

Post by fxm »

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: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Re: Timer & Threads

Post by Dinosaur »

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
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Timer & Threads

Post by fxm »

You may also be interested in the following for the global variables versus threads:
http://www.freebasic.net/forum/viewtopi ... 98#p220198
http://www.freebasic.net/forum/viewtopi ... =2&t=21631
Last edited by fxm on Mar 27, 2017 8:35, edited 2 times in total.
Dinosaur
Posts: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Re: Timer & Threads

Post by Dinosaur »

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: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Re: Timer & Threads

Post by Dinosaur »

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
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Timer & Threads

Post by fxm »

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: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Re: Timer & Threads

Post by Dinosaur »

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
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Timer & Threads

Post by fxm »

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: 1481
Joined: Jul 24, 2005 1:13
Location: Hervey Bay (.au)

Re: Timer & Threads

Post by Dinosaur »

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
Post Reply