views:

181

answers:

4

I have some code that will be accessed from two threads:


class Timer{
public:
   void Start(){
      start_ = clock_->GetCurrentTime();
      running_ = true;
   }

   void Read(){
      if(running_){
         time_duration remaining = clock_->GetCurrentTime() - start_;
         Actions(remaining)
      }
   }

private:
   void Actions(time_duration remaining);
   time start_;
   bool running;
};

I've looked at some other timers available in various libraries, but didn't find any that fit my requirements, thus I'm rolling my own...

The Start() method is called (once only) from one thread. The Read() method is called very rapidly from another thread, the calls will start coming in before Start() is called.

Obviously it is very important that the start_ variable is initialized before the running_ flag is set. This could be solved by adding a mutex that gets grabbed when the Start() method is entered...and is grabbed before running_ is checked in the Read() method...but it seems somewhat unnecessary. If everything here executes in order, then there are no problems. I have no problem with the fact that a Read() could happen while the other thread is in the Start() routing, getting the time from the clock for example...the Read()s happen fast enough that its just not a big deal.

Anyway, I was looking for a way to ensure that the compiler/processor will execute the

  start_ = clock_->GetCurrentTime();
  running_ = true;

instructions in order they are listed above. (Or if I'm overlooking something else).

A: 

I'm not sure whether I understand your question, but I think you want to prevent that Read is being executed when the Start method hasn't been set (or hasn't been executed completely) ?

If that's the case, can't you solve your problem by using an AutoResetEvent or ManualResetEvent (depending on what behavior you want).

In the Read method, you could specify that the Read method should wait when the Auto/ManualResetEvent hasn't been set, and at the end of the Start method, you can set the Event.

Frederik Gheysels
+3  A: 

You need to make start_ and running_ volatile, and then introduce a memory barrier between the two assignments in Start().

Douglas Leeder
I was thinking something along these lines...but I was looking for a bit more detail.Is volatile supported by all compilers? Do you know any good documentation on memory barriers?
teeks99
A: 

Why not just stick a

if (_running == false) Start();

In the Read() method. And either protect Start with a mutex or define it as "critical" to ensure its single threaded.

James Anderson
I don't want to be running every time that Read() is called...a lot of the time nothing should happen when Read() is called.
teeks99
+1  A: 

Why not get rid of the "running" flag and use whether the "start" variable is empty as the condition? You don't specify a language but some sort of volatile marker for "start" would be a good idea too. This also assumes that "start" can be written atomically. For example:

class Timer{
public:   
  void Start(){
    start_ = clock_->GetCurrentTime(); 
  }
  void Read(){
  if(nullptr != start_){
     time_duration remaining = clock_->GetCurrentTime() - start_;
     Actions(remaining)      
  }   
}
private:   
  void Actions(time_duration remaining);
  volatile time start_;
};
Stu Mackellar
This doesn't quite work, because start_ isn't a small atomic thing.
teeks99
We're really getting to the point when the language would need to be specified to go any further. The above would probably work because all we care about is that the value of start_ is no longer 0, we don't have to accurately know its new value.
Stu Mackellar
The language is C++. The type of start_ is boost::posix_time::ptime. I don't believe writing a ptime object is a atomic operation, so in the above example, the if check in read (and more importantly the line after it) could be executed with only a partial value in start_.
teeks99
You're right. You could always create start_ on the heap and store a pointer to it instead. However, it's probably better design to use a separate flag in this situation. The same principle applies though. The volatile keyword should always be available in any C++ compiler so no worries there.
Stu Mackellar