Page 1 of 2

Proper Queue usage

Posted: Tue Jul 20, 2010 3:23 pm
by vsabino
Hi,

I'm a little confused with how to use queues.

I saw the example in \nburn\examples\RTOS\OSQueue:

/*-------------------------------------------------------------------
Q Post Task
------------------------------------------------------------------*/
void QPostTask( void *pdata )
{
while ( 1 )
{
for(int i=0; i<10; i++)
{
OSQPost( &MyQ, (void *)i);
if(i==5)
OSQPostFirst(&MyQ,(void *)0x100 );

}
OSTimeDly( TICKS_PER_SECOND * 20 );
}
}

The pointer to the address of the variable i is posted to the queue. Doesn't this dissapear once we exit the task scope?
Responding to myself, the variable exists always because it is allocated for the task?

What would happen if it were an ISR instead of a task? could I solve the problem by declaring i static?
Somebody told me that a static variable means the value is preserved for the next time the function is called, but it's memory location might not be the same, so posting a pointer to this address might not give me the variable I intended to. Is this true?

I wish there were better examples of queue usage in the nburn folder.

Regards,
Victor

Re: Proper Queue usage

Posted: Tue Jul 20, 2010 4:21 pm
by pbreed
You don't pass the pointer. (You are correct it can go out of scope)

First a little background...

Every time you see "void *" think :A place to store any 32 bit quantity.

That can be an int a DWORD, a pointer (if its a pointer make sure the thing it points to stays in scope) , a pointer to a c++ class etc...

So you want to pass an int i

(void *)i

You want the int back


if you have a void * p, then (int)p turns it back into an integer.


Or in case of the queue...

myQ;

OSQPost(&myQ, (void * i));

To get it back

BYTE err_result;
int i=(int) OSQPend( &myQ, timeout, &err_result );

if(err_result==OS_NO_ERR )
{
i has the value posted....
}




Paul

Re: Proper Queue usage

Posted: Wed Jul 21, 2010 9:25 am
by vsabino
Hi Paul,

Thanks for the reply.

"You don't pass the pointer.(You are correct it can go out of scope)"

I'm actually posting the value of i?

Example:
- int i is a local variable let's say at address 100.
- assign i = 5
- OSQPost(&myQ, (void * i));
I'm storing 5 into the queue?

Where can i go out of scope? In the Task, the ISR, or both?

Thanks,
Victor

Re: Proper Queue usage

Posted: Wed Jul 21, 2010 10:47 am
by tod
Victor,
Another way of saying what Paul said may help:

"A message queue (or simply a queue) is a uC/OS-II object that allows a task or an ISR to send pointer-sized variables to another task. Each pointer typically is initialized [but doesn't have to be] to point to some application-specific datat structure containing a message." - MicroC/OS-II The Real Time Kernel Second Edition - J. Labrosse

So yes you are passing the value of i and casting it to a pointer sized variable. i can go out of scope immediately after the call the same as any other pass-by-value parameter. (Which is why as you show in the example you pass a compiler constant 0x100). The queue will store the value of i, it doesn't treat it as a pointer, it just treats it as a pointer sized variable.

I think the only thing you have to be careful about here is passing a value of 0. NetBurner uses a modified version of the uC/OS so they may do things differently, but the above referenced book shows that the implementation of OSQPost can (based on the value of OS_ARG_CHECK_EN ) do the following:

if (msg ==(void*)0){
return (OS_ERR_POST_NULL_PTR);
}

where msg is the second parameter passed in.

Tod

Re: Proper Queue usage

Posted: Fri Jul 23, 2010 7:42 am
by vsabino
Hi Tod and Paul, thanks for the replies.

I got it to work, except for one case where I want to queue a float:

float float_speed;
OSQPost( &QueueIRQ3, (void *)float_speed);

I get this error: invalid cast from type 'float' to type 'void*'

Why is this? isn't a float represented as a 32-bit value as well?

regards,
Victor

Re: Proper Queue usage

Posted: Fri Jul 23, 2010 2:12 pm
by tod
It is 4 bytes long on the NetBurner but I don't believe the size of a float is defined by the C++ standard and that is why it's not a valid cast. My best piece of advice is find a better way (probably pass an actual pointer to a float). What are you going to do if you need to pass a double?

If you REALLY, REALLY insist on doing this you can bend the compiler to your will (all the squeamish out there should avert their eyes). Note that I wrapped the conversions up in methods so at least there is some lipstick on this pig. In real code I would wrap all this up in some static Utility class.

Code: Select all

//WARNING - Here be dragons - You need to add comments to explain why this was required.
union DangerousCastUnion
{
	void* voidPtr;
	float theFloat;
};

void AssertFloatSize()
{
	if (sizeof(float)!= sizeof(void*))
	{
		assert("Floating point is no longer the size of a pointer variable. My bad, but your problem.");
		abort();//In case NDEBUG is defined and the assert is a nop
	}
}
void* DangerCastFloatToVoidPtr(float value)
{
	AssertFloatSize();
	DangerousCastUnion cast_union;
	cast_union.theFloat = value;
	return  cast_union.voidPtr; //reinterpret_cast<void*>(my_union.dangerous_cast);
}

float DangerCastVoidPtrToFloat(void* value)
{
	AssertFloatSize();
	DangerousCastUnion cast_union;
	cast_union.voidPtr = value;
	return  cast_union.theFloat; //reinterpret_cast<void*>(my_union.dangerous_cast);
}

//test code
	float my_float = 1.45;
	void* some_void_ptr = DangerCastFloatToVoidPtr(my_float);
	cout << "void pointer:"<<some_void_ptr<< endl;
	float new_float = DangerCastVoidPtrToFloat(some_void_ptr);
	cout << "Float from void *:" << new_float << endl;

If I saw this in someone else's code though I would wonder why I was cursed with maintaining their code. You should justify this kind of hack in comments so a future maintenance programmer can decide if was really worth it. In a real app I would probably use something better than assert() and abort(). I want some way to guarantee that the maintenance programmer has some way of seeing the problem right where it occurred. If you have exceptions enabled a throw() might be suitable. abort() may be OK if the netburner traps on it and winaddr2line gives us the line of the failure.

Tod

Re: Proper Queue usage

Posted: Fri Jul 23, 2010 3:03 pm
by lgitlitz
Floating point numbers should not be cast to a pointer. I believe this is due to the fact that floating point numbers are converted when you cast them and often have their values modified. For example, take the float with a value of 1/3. If you actually look at the memory this is stored as 0x3EAAAAAB. Now if you cast this float to an int the result will be rounded down to 0, not the values stored in memory for the float which is 0x3EAAAAAB. So how is the compiler supposed to convert a float to a pointer or physical address? The vast majority of floats are not even within the range of addressable memory.

You can trick the compiler to allowing to you to cast the actual value stored in memory of the float to a void* but it is not going to look pretty. Be careful not to access this pointer later as an address or it will trap the processor. Make sure to overly comment your code if you do this because you will be lost if you try to re-read this code in a few months.

float x = 1.23;
void* y = &x;
DWORD z = *(DWORD*)y;
y = (void*)z;

-Larry

Re: Proper Queue usage

Posted: Fri Jul 23, 2010 4:23 pm
by tod
I agree with everything Larry said but for fun I tested 1.0/3.0 using my method
void pointer:0x3eaaaaab
Float from void *:0.333333

I'm just running this through Eclipse and a normal C++ project, not building it for the NB.

Tod

Re: Proper Queue usage

Posted: Thu Jul 29, 2010 11:27 am
by vsabino
Thanks all for the replies.

So I guess putting the float into a queue is not good practice; specially after seeing the warnings and workarounds to do so.

So what would be the proper method of doing this?
Tod wrote: "....(probably pass an actual pointer to a float). What are you going to do if you need to pass a double?..."
My point exactly.

If I pass a pointer to a float, then I must make sure the float stays in memory all the time, so I should declare it as a static variable; or I might just as well make it a global variable.
What would be most advisable?

Thanks,
Victor

Re: Proper Queue usage

Posted: Thu Jul 29, 2010 4:35 pm
by pbreed
float is 32 bits, ok to stuff into a queue, double is 64 bits, not ok.

Paul