Search code examples
multithreadingdelphimutexdelphi-2007

Race Condition - Can Mutexes Be More Flexible?


I have a TThread object named TTestThread. When the threads are created, they make a local copy of the global configuration variables. The user is allowed to change the global configuration variables at any time, and when he does so, all threads are notified of the change via their local UpdateLocalCopyOfConfigVariables variable. The threads don't rely on the global configuration variables directly because they can be modified at any point by the user, which would create a race condition if the threads were to access them at the same time.

Here's TTestThread:

type
  TTestThread = class(TThread)
  private
    LocalConfigA : String;
    LocalConfigB : Integer;
    procedure UpdateLocalConfigIfNecessary;
  protected
    procedure Execute; override;
  public
    UpdateLocalCopyOfConfigVariables : Boolean;
    constructor Create;
  end;

implementation

constructor TTestThread.Create;
begin
  inherited Create(false);
  UpdateLocalCopyOfConfigVariables := true;
end;

procedure TTestThread.UpdateLocalConfigIfNecessary;
begin
  WaitForSingleObject(ConfigurationLocker, INFINITE);
  if (UpdateLocalCopyOfConfigVariables) then
  begin
    LocalConfigA := GlobalConfigA;
    LocalConfigB := GlobalConfigB;
    UpdateLocalCopyOfConfigVariables := false;
  end;
  ReleaseMutex(ConfigurationLocker);
end;

procedure TTestThread.Execute;
begin
  while (not(Terminated)) do
  begin
    UpdateLocalConfigIfNecessary;
    // Do stuff
  end; 
end;

As you can see, I have a mutex in place to avoid the sort of race condition described earlier. WaitForSingleObject(ConfigurationLocker, INFINITE); is called when the user changes the global configuration variables:

procedure ChangeGlobalConfigVariables(const NewGlobalConfigA : String ; NewGlobalConfigB : Integer);
var
  I : Integer;
begin
  WaitForSingleObject(ConfigurationLocker, INFINITE);

  GlobalConfigA := NewGlobalConfigA;
  GlobalConfigB := NewGlobalConfigB;

  for I := 0 to ThreadList.Count - 1 do
    TTestThread(ThreadList[I]).UpdateLocalCopyOfConfigVariables := true;

  ReleaseMutex(ConfigurationLocker);
end;

The thing is, while it prevents the threads from updating their local copy of configuration variables at the same time the global configuration variables are being changed, it also prevents two threads from updating their local configuration at the same time – even if the global configuration variables are not being changed. As far as I know, race condition is an issue when there's writing going on. If the global configuration variables are the same, then the threads can all update their local copy at the same time with no issues.

Am I right? If so, is there a way to fix this issue? Granted, it's not a big one, but I still feel like there has to be a better solution...


Solution

  • It seems a good point for TMultiReadExclusiveWriteSynchronizer work