This is a mirror of official site: http://jasper-net.blogspot.com/

Even if you have a lock, you can borrow some lock-free techniques

| Tuesday, May 3, 2011
Even if you prefer to use a lock (after all, they are much easier to program), you can borrow some lock-free techniques. For example, consider this:

CRITICAL_SECTION g_cs;
GORILLADATA g_data;

void PokeGorilla(double intensity)
{
  EnterCriticalSection(&g_cs);
  DeformGorilla(intensity, &g_data);
  Reticulate(&g_data.spline);
  int stress = CalculateTension(&g_data.spline);
  if (stress < 25)      g_data.mood = RELAXED;
  else if (stress < 50) g_data.mood = ANNOYED;
  else                  g_data.mood = ANGRY;
  DeleteObject(g_data.hbmGorilla);
  g_data.hbmGorilla = RenderGorilla(&g_data);
  LeaveCriticalSection(&g_cs);
}
There are some concerns here. First of all, there's the lock hierarchy issue: If reticulating a spline takes the geometry lock, that may violate our lock hierarchy.

If the lock g_cs is a hot lock, you may be concerned that all this gorilla stuff will hold the lock for too long. Maybe rendering a gorilla is a slow and complicated operation because it's hard to get the fur just right.

These issues become less onerous if you switch to a lock-free algorithm, but that's an awful lot of work, and it's hard to get right. But maybe you can do just 20% of the work to get 80% of the benefit.

void PokeGorilla(double intensity)
{
  // Capture
  EnterCriticalSection(&g_cs);
  GORILLADATA data = g_data; // typo fixed
  LeaveCriticalSection(&g_cs);

  // Recalculate based on captured data
  DeformGorilla(intensity, &data);
  Reticulate(&data.spline);
  int stress = CalculateTension(&data.spline);
  if (stress < 25)      data.mood = RELAXED;
  else if (stress < 50) data.mood = ANNOYED;
  else                  data.mood = ANGRY;
  data.hbmGorilla = RenderGorilla(&data);

  // Commit
  EnterCriticalSection(&g_cs);
  HBITMAP hbmToDelete = g_data.hbmGorilla;
  g_data = data;
  LeaveCriticalSection(&g_cs);
  DeleteObject(hbmToDelete);
}
Here, we use the capture/try/commit model. We capture the state of the gorilla into a local variable, then perform our update based on that captured state. The spline reticulation takes place without any locks held, which avoids introducing a lock hierarchy violation. And rendering the gorilla is done without any locks held, which avoids introducing a choke point on the lock. After the calculations are done, we then re-enter the lock and commit the changes.

This pattern uses a last-writer-wins model. If another thread pokes the gorilla while we are still calculating the previous gorilla state, we will overwrite that gorilla state when we complete. For some scenarios, that's acceptable. But maybe the gorilla's emotional state needs to be an accumulation of all the times he's ben poked. We want to detect that somebody has poked the gorilla while we were busy calculating so that we can incorporate that new information into the final result.

Posted via email from Jasper-net

0 comments: