www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Bug w/structs and threads?

reply Nate <plummn comdel.net> writes:
Hello,

I've been tearing my hair out on this little piece of code but now I am 
thinking there is nothing wrong with it!

I have a struct that has 2 methods, one returns a rectangle (another 
struct) and the other changes the offset to know which rectangle to 
pull. Here is the odd part.. 95% of the time it works perfect. The other 
5% of the time currentOffset is always 0 (or whatever initially set) for 
the GetSourceRectangle method.

By using printf I see that in TimerElapsed (which is called from another 
thread) is setting the currentOffset properly 100% of the time. Its the 
other method that is not pulling the right value.

printf says...
running tile 66, offset is 1 (at top of timer elapsed)
running tile 66, offset is now 2 (at end of timer elapsed)
A anim 66 Returinging 0.000000 0.328125 (!!! its now at 2 this is 0 !!!)

Any ideas on a work around?

Here is a snip (attached is current code):

 Rectangle GetSourceRectangle(int offset)
 {
	/* This 90% of the time returns the proper
	 * source rectangle from the array but for
	 * 5-10 / 200 currentOffset is always the same
	 * (0 default)! */
 	if(currentOffset + offset < sourceRects.length)
 	{
 		return sourceRects[this.currentOffset];
 	}
 	else
 	{
 		return sourceRects[0];
	}
 }
 			
 void TimerElapsed(long l, long m)
 {
	/* This has workd 100% of the time */
 	if(CurrentTick() - lastTickUsed > (15 * animSpeed))
 	{
 		if(currentOffset + 1 < sourceRects.length)
 			this.currentOffset++;
 		else
 			this.currentOffset = 0;

 		this.lastTickUsed = CurrentTick();		
 	}
 }
Jun 05 2005
next sibling parent Nate <plummn comdel.net> writes:
Just to be a little more clear, if I place TimerElapsed(0,0); in the 
GetSourceRectangle method all tiles work but they all should work 
without it.

Nate wrote:
 Hello,
 
 I've been tearing my hair out on this little piece of code but now I am 
 thinking there is nothing wrong with it!
 
 I have a struct that has 2 methods, one returns a rectangle (another 
 struct) and the other changes the offset to know which rectangle to 
 pull. Here is the odd part.. 95% of the time it works perfect. The other 
 5% of the time currentOffset is always 0 (or whatever initially set) for 
 the GetSourceRectangle method.
 
 By using printf I see that in TimerElapsed (which is called from another 
 thread) is setting the currentOffset properly 100% of the time. Its the 
 other method that is not pulling the right value.
 
 printf says...
 running tile 66, offset is 1 (at top of timer elapsed)
 running tile 66, offset is now 2 (at end of timer elapsed)
 A anim 66 Returinging 0.000000 0.328125 (!!! its now at 2 this is 0 !!!)
 
 Any ideas on a work around?
 
 Here is a snip (attached is current code):
 
 Rectangle GetSourceRectangle(int offset)
 {
> /* This 90% of the time returns the proper > * source rectangle from the array but for > * 5-10 / 200 currentOffset is always the same > * (0 default)! */
     if(currentOffset + offset < sourceRects.length)
     {
         return sourceRects[this.currentOffset];
     }
     else
     {
         return sourceRects[0];
     }
 }
            
 void TimerElapsed(long l, long m)
 {
> /* This has workd 100% of the time */
     if(CurrentTick() - lastTickUsed > (15 * animSpeed))
     {
         if(currentOffset + 1 < sourceRects.length)
             this.currentOffset++;
         else
             this.currentOffset = 0;

         this.lastTickUsed = CurrentTick();       
     }
 }
------------------------------------------------------------------------ module Mapping.AnimatedTile; /* Copyright (C) 2005 Nate Plumm */ private import Drawing, Utilities.Tiles, Utilities.Timer; public struct AnimatedTile { private: Rectangle[] sourceRects; byte animSpeed; int frameCount = 0; long lastTickUsed = 0; int tileNumber = 0; int currentOffset = 0; public: bit useSecondSheet; static AnimatedTile opCall(int tileNumber, byte animSpeed, ushort[] animFrames, int frameCount) { AnimatedTile t; t.Setup(tileNumber, animSpeed, animFrames, frameCount); return t; } void Setup(int tileNumber, byte animSpeed, ushort[] animFrames, int frameCount) { this.tileNumber = tileNumber; this.animSpeed = animSpeed; this.frameCount = frameCount; sourceRects.length = frameCount; for(int i = 0; i < frameCount; i++) sourceRects[i] = Tiles.TileNumberToRectangle(animFrames[i], useSecondSheet); } Rectangle GetSourceRectangle(int offset) { if(this.currentOffset + offset < sourceRects.length) { return sourceRects[this.currentOffset]; } else { return sourceRects[0]; } } void TimerElapsed(long expectedTime, long realTime) // This is called from a different thread { if(CurrentTick() - lastTickUsed > (15 * animSpeed)) { if(this.currentOffset + 1 < sourceRects.length) this.currentOffset++; else this.currentOffset = 0; this.lastTickUsed = CurrentTick(); } } }
Jun 05 2005
prev sibling next sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
I try following:

1) volatile int currentOffset = 0; // to exclude it from optimisations.
I am not tested volatiles in D. But this variable must be volatile in C++ 
per your specification.

2) Your TimerElapsed must be changed to:
void TimerElapsed(long expectedTime, long realTime) // This is called from a 
different thread
{
   uint ct = CurrentTick();
   if(ct - lastTickUsed > (15 * animSpeed))
   {
    if(this.currentOffset + 1 < sourceRects.length)
       this.currentOffset++;
    else
       this.currentOffset = 0;
    this.lastTickUsed = ct;
   }
}


Andrew.





"Nate" <plummn comdel.net> wrote in message 
news:d7v82t$1lpv$1 digitaldaemon.com...
 Hello,

 I've been tearing my hair out on this little piece of code but now I am
 thinking there is nothing wrong with it!

 I have a struct that has 2 methods, one returns a rectangle (another
 struct) and the other changes the offset to know which rectangle to
 pull. Here is the odd part.. 95% of the time it works perfect. The other
 5% of the time currentOffset is always 0 (or whatever initially set) for
 the GetSourceRectangle method.

 By using printf I see that in TimerElapsed (which is called from another
 thread) is setting the currentOffset properly 100% of the time. Its the
 other method that is not pulling the right value.

 printf says...
 running tile 66, offset is 1 (at top of timer elapsed)
 running tile 66, offset is now 2 (at end of timer elapsed)
 A anim 66 Returinging 0.000000 0.328125 (!!! its now at 2 this is 0 !!!)

 Any ideas on a work around?

 Here is a snip (attached is current code):

 Rectangle GetSourceRectangle(int offset)
 {
 /* This 90% of the time returns the proper
 * source rectangle from the array but for
 * 5-10 / 200 currentOffset is always the same
 * (0 default)! */
 if(currentOffset + offset < sourceRects.length)
 {
 return sourceRects[this.currentOffset];
 }
 else
 {
 return sourceRects[0];
 }
 }

 void TimerElapsed(long l, long m)
 {
 /* This has workd 100% of the time */
 if(CurrentTick() - lastTickUsed > (15 * animSpeed))
 {
 if(currentOffset + 1 < sourceRects.length)
 this.currentOffset++;
 else
 this.currentOffset = 0;

 this.lastTickUsed = CurrentTick();
 }
 }
--------------------------------------------------------------------------------
 module Mapping.AnimatedTile;

 /* Copyright (C) 2005 Nate Plumm */

 private import Drawing, Utilities.Tiles, Utilities.Timer;

 public struct AnimatedTile
 {
 private:

 Rectangle[] sourceRects;
 byte animSpeed;
 int frameCount = 0;
 long lastTickUsed = 0;
 int tileNumber = 0;
 int currentOffset = 0;

 public:

 bit useSecondSheet;

 static AnimatedTile opCall(int tileNumber, byte animSpeed, ushort[] 
 animFrames, int frameCount)
 {
 AnimatedTile t;
 t.Setup(tileNumber, animSpeed, animFrames, frameCount);
 return t;
 }

 void Setup(int tileNumber, byte animSpeed, ushort[] animFrames, int 
 frameCount)
 {
 this.tileNumber = tileNumber;
 this.animSpeed = animSpeed;
 this.frameCount = frameCount;
 sourceRects.length = frameCount;

 for(int i = 0; i < frameCount; i++)
 sourceRects[i] = Tiles.TileNumberToRectangle(animFrames[i], 
 useSecondSheet);
 }

 Rectangle GetSourceRectangle(int offset)
 {
 if(this.currentOffset + offset < sourceRects.length)
 {
 return sourceRects[this.currentOffset];
 }
 else
 {
 return sourceRects[0];
 }
 }

 void TimerElapsed(long expectedTime, long realTime) // This is called from 
 a different thread
 {
 if(CurrentTick() - lastTickUsed > (15 * animSpeed))
 {
 if(this.currentOffset + 1 < sourceRects.length)
 this.currentOffset++;
 else
 this.currentOffset = 0;

 this.lastTickUsed = CurrentTick();
 }
 }
 } 
Jun 05 2005
parent Nate <plummn comdel.net> writes:
I've changed the code as suggested and the problem still remains. I 
tried putting volatile all over the place.

90% of the time it works and the other 10% currentOffset does not change 
that the other thread can see.

It may be worth mentioning that this consistently happens on the same 
tiles every time. When calling timerelapsed from its own thread 
everything works. The tile at index 66 for example (dynamic array of a 
struct), this always happens.

Andrew Fedoniouk wrote:
 I try following:
 
 1) volatile int currentOffset = 0; // to exclude it from optimisations.
 I am not tested volatiles in D. But this variable must be volatile in C++ 
 per your specification.
 
 2) Your TimerElapsed must be changed to:
 void TimerElapsed(long expectedTime, long realTime) // This is called from a 
 different thread
 {
    uint ct = CurrentTick();
    if(ct - lastTickUsed > (15 * animSpeed))
    {
     if(this.currentOffset + 1 < sourceRects.length)
        this.currentOffset++;
     else
        this.currentOffset = 0;
     this.lastTickUsed = ct;
    }
 }
 
 
 Andrew.
 
 
 
 
 
 "Nate" <plummn comdel.net> wrote in message 
 news:d7v82t$1lpv$1 digitaldaemon.com...
 
Hello,

I've been tearing my hair out on this little piece of code but now I am
thinking there is nothing wrong with it!

I have a struct that has 2 methods, one returns a rectangle (another
struct) and the other changes the offset to know which rectangle to
pull. Here is the odd part.. 95% of the time it works perfect. The other
5% of the time currentOffset is always 0 (or whatever initially set) for
the GetSourceRectangle method.

By using printf I see that in TimerElapsed (which is called from another
thread) is setting the currentOffset properly 100% of the time. Its the
other method that is not pulling the right value.

printf says...
running tile 66, offset is 1 (at top of timer elapsed)
running tile 66, offset is now 2 (at end of timer elapsed)
A anim 66 Returinging 0.000000 0.328125 (!!! its now at 2 this is 0 !!!)

Any ideas on a work around?

Here is a snip (attached is current code):


Rectangle GetSourceRectangle(int offset)
{
/* This 90% of the time returns the proper
* source rectangle from the array but for
* 5-10 / 200 currentOffset is always the same
* (0 default)! */
if(currentOffset + offset < sourceRects.length)
{
return sourceRects[this.currentOffset];
}
else
{
return sourceRects[0];
}
}

void TimerElapsed(long l, long m)
{
/* This has workd 100% of the time */
if(CurrentTick() - lastTickUsed > (15 * animSpeed))
{
if(currentOffset + 1 < sourceRects.length)
this.currentOffset++;
else
this.currentOffset = 0;

this.lastTickUsed = CurrentTick();
}
}
--------------------------------------------------------------------------------
module Mapping.AnimatedTile;

/* Copyright (C) 2005 Nate Plumm */

private import Drawing, Utilities.Tiles, Utilities.Timer;

public struct AnimatedTile
{
private:

Rectangle[] sourceRects;
byte animSpeed;
int frameCount = 0;
long lastTickUsed = 0;
int tileNumber = 0;
int currentOffset = 0;

public:

bit useSecondSheet;

static AnimatedTile opCall(int tileNumber, byte animSpeed, ushort[] 
animFrames, int frameCount)
{
AnimatedTile t;
t.Setup(tileNumber, animSpeed, animFrames, frameCount);
return t;
}

void Setup(int tileNumber, byte animSpeed, ushort[] animFrames, int 
frameCount)
{
this.tileNumber = tileNumber;
this.animSpeed = animSpeed;
this.frameCount = frameCount;
sourceRects.length = frameCount;

for(int i = 0; i < frameCount; i++)
sourceRects[i] = Tiles.TileNumberToRectangle(animFrames[i], 
useSecondSheet);
}

Rectangle GetSourceRectangle(int offset)
{
if(this.currentOffset + offset < sourceRects.length)
{
return sourceRects[this.currentOffset];
}
else
{
return sourceRects[0];
}
}

void TimerElapsed(long expectedTime, long realTime) // This is called from 
a different thread
{
if(CurrentTick() - lastTickUsed > (15 * animSpeed))
{
if(this.currentOffset + 1 < sourceRects.length)
this.currentOffset++;
else
this.currentOffset = 0;

this.lastTickUsed = CurrentTick();
}
}
} 
Jun 05 2005
prev sibling parent reply Nate <plummn comdel.net> writes:
There has to be a bug here. I finally got this to work as it should and 
in order to do that I had to have a set array rather than a dynamic 
array of my struct.

Should I submit this somewhere? This should work the same with either 
array type..no?

Nate wrote:
 Hello,
 
 I've been tearing my hair out on this little piece of code but now I am 
 thinking there is nothing wrong with it!
 
 I have a struct that has 2 methods, one returns a rectangle (another 
 struct) and the other changes the offset to know which rectangle to 
 pull. Here is the odd part.. 95% of the time it works perfect. The other 
 5% of the time currentOffset is always 0 (or whatever initially set) for 
 the GetSourceRectangle method.
 
 By using printf I see that in TimerElapsed (which is called from another 
 thread) is setting the currentOffset properly 100% of the time. Its the 
 other method that is not pulling the right value.
 
 printf says...
 running tile 66, offset is 1 (at top of timer elapsed)
 running tile 66, offset is now 2 (at end of timer elapsed)
 A anim 66 Returinging 0.000000 0.328125 (!!! its now at 2 this is 0 !!!)
 
 Any ideas on a work around?
 
 Here is a snip (attached is current code):
 
 Rectangle GetSourceRectangle(int offset)
 {
> /* This 90% of the time returns the proper > * source rectangle from the array but for > * 5-10 / 200 currentOffset is always the same > * (0 default)! */
     if(currentOffset + offset < sourceRects.length)
     {
         return sourceRects[this.currentOffset];
     }
     else
     {
         return sourceRects[0];
     }
 }
            
 void TimerElapsed(long l, long m)
 {
> /* This has workd 100% of the time */
     if(CurrentTick() - lastTickUsed > (15 * animSpeed))
     {
         if(currentOffset + 1 < sourceRects.length)
             this.currentOffset++;
         else
             this.currentOffset = 0;

         this.lastTickUsed = CurrentTick();       
     }
 }
------------------------------------------------------------------------ module Mapping.AnimatedTile; /* Copyright (C) 2005 Nate Plumm */ private import Drawing, Utilities.Tiles, Utilities.Timer; public struct AnimatedTile { private: Rectangle[] sourceRects; byte animSpeed; int frameCount = 0; long lastTickUsed = 0; int tileNumber = 0; int currentOffset = 0; public: bit useSecondSheet; static AnimatedTile opCall(int tileNumber, byte animSpeed, ushort[] animFrames, int frameCount) { AnimatedTile t; t.Setup(tileNumber, animSpeed, animFrames, frameCount); return t; } void Setup(int tileNumber, byte animSpeed, ushort[] animFrames, int frameCount) { this.tileNumber = tileNumber; this.animSpeed = animSpeed; this.frameCount = frameCount; sourceRects.length = frameCount; for(int i = 0; i < frameCount; i++) sourceRects[i] = Tiles.TileNumberToRectangle(animFrames[i], useSecondSheet); } Rectangle GetSourceRectangle(int offset) { if(this.currentOffset + offset < sourceRects.length) { return sourceRects[this.currentOffset]; } else { return sourceRects[0]; } } void TimerElapsed(long expectedTime, long realTime) // This is called from a different thread { if(CurrentTick() - lastTickUsed > (15 * animSpeed)) { if(this.currentOffset + 1 < sourceRects.length) this.currentOffset++; else this.currentOffset = 0; this.lastTickUsed = CurrentTick(); } } }
Jun 06 2005
parent reply James Dunne <james.jdunne gmail.com> writes:
Are you running a dual-processor PC?  Are you running Windows or Linux?

I would first check that you're not trying to write to currentOffset at the same
time you're reading from currentOffset in two separate threads.  Use
synchronized blocks around your accesses to currentOffset to see if this fixes
your problem.

Also, volatile keyword is a *statement* modifier in D, not a declaration
modifier.  This means that whenever you want to set the value of currentOffset,
you do as such:

volatile currentOffset = my_new_value;

In article <d8366d$1hdm$1 digitaldaemon.com>, Nate says...
There has to be a bug here. I finally got this to work as it should and 
in order to do that I had to have a set array rather than a dynamic 
array of my struct.

Should I submit this somewhere? This should work the same with either 
array type..no?

Nate wrote:
 Hello,
 
 I've been tearing my hair out on this little piece of code but now I am 
 thinking there is nothing wrong with it!
 
 I have a struct that has 2 methods, one returns a rectangle (another 
 struct) and the other changes the offset to know which rectangle to 
 pull. Here is the odd part.. 95% of the time it works perfect. The other 
 5% of the time currentOffset is always 0 (or whatever initially set) for 
 the GetSourceRectangle method.
 
 By using printf I see that in TimerElapsed (which is called from another 
 thread) is setting the currentOffset properly 100% of the time. Its the 
 other method that is not pulling the right value.
 
 printf says...
 running tile 66, offset is 1 (at top of timer elapsed)
 running tile 66, offset is now 2 (at end of timer elapsed)
 A anim 66 Returinging 0.000000 0.328125 (!!! its now at 2 this is 0 !!!)
 
 Any ideas on a work around?
 
 Here is a snip (attached is current code):
 
 Rectangle GetSourceRectangle(int offset)
 {
> /* This 90% of the time returns the proper > * source rectangle from the array but for > * 5-10 / 200 currentOffset is always the same > * (0 default)! */
     if(currentOffset + offset < sourceRects.length)
     {
         return sourceRects[this.currentOffset];
     }
     else
     {
         return sourceRects[0];
     }
 }
            
 void TimerElapsed(long l, long m)
 {
> /* This has workd 100% of the time */
     if(CurrentTick() - lastTickUsed > (15 * animSpeed))
     {
         if(currentOffset + 1 < sourceRects.length)
             this.currentOffset++;
         else
             this.currentOffset = 0;

         this.lastTickUsed = CurrentTick();       
     }
 }
------------------------------------------------------------------------ module Mapping.AnimatedTile; /* Copyright (C) 2005 Nate Plumm */ private import Drawing, Utilities.Tiles, Utilities.Timer; public struct AnimatedTile { private: Rectangle[] sourceRects; byte animSpeed; int frameCount = 0; long lastTickUsed = 0; int tileNumber = 0; int currentOffset = 0; public: bit useSecondSheet; static AnimatedTile opCall(int tileNumber, byte animSpeed, ushort[] animFrames, int frameCount) { AnimatedTile t; t.Setup(tileNumber, animSpeed, animFrames, frameCount); return t; } void Setup(int tileNumber, byte animSpeed, ushort[] animFrames, int frameCount) { this.tileNumber = tileNumber; this.animSpeed = animSpeed; this.frameCount = frameCount; sourceRects.length = frameCount; for(int i = 0; i < frameCount; i++) sourceRects[i] = Tiles.TileNumberToRectangle(animFrames[i], useSecondSheet); } Rectangle GetSourceRectangle(int offset) { if(this.currentOffset + offset < sourceRects.length) { return sourceRects[this.currentOffset]; } else { return sourceRects[0]; } } void TimerElapsed(long expectedTime, long realTime) // This is called from a different thread { if(CurrentTick() - lastTickUsed > (15 * animSpeed)) { if(this.currentOffset + 1 < sourceRects.length) this.currentOffset++; else this.currentOffset = 0; this.lastTickUsed = CurrentTick(); } } }
Jun 07 2005
parent Sean Kelly <sean f4.ca> writes:
In article <d844rs$2bkr$1 digitaldaemon.com>, James Dunne says...
Are you running a dual-processor PC?  Are you running Windows or Linux?

I would first check that you're not trying to write to currentOffset at the same
time you're reading from currentOffset in two separate threads.  Use
synchronized blocks around your accesses to currentOffset to see if this fixes
your problem.
And make sure you're using the same object for synchronization in each block that accesses the same thing. ie.
Also, volatile keyword is a *statement* modifier in D, not a declaration
modifier.  This means that whenever you want to set the value of currentOffset,
you do as such:

volatile currentOffset = my_new_value;
It's worth noting that all volatile does in D is ensure the compiler doesn't optimize around the volatile block--it does nothing to prevent CPU caching of writes. For that, you need a third-party library like Ben Hinkle's locks library--it has compareAndSet methods and such. (alternately, for the PC, use an asm block with the 'lock' instruction, as it is a full membar). Sean
Jun 07 2005