A safer way to implement boolean flags

Introduction

Boolean flags are commonly used to disable a block of code while another is running: for example,

  private bool in_use;
  
  private void Process() {
    in_use = true;
    (...)
    in_use = false;
  }
  
  private void OnEvent() {
    if (in_use)
      return;
      
    (...)
  }
  Private InUse As Boolean;
  
  Private Sub Process()
    InUse = True
    (...)
    InUse = False
  End Sub
  
  Private Sub Process()
    If InUse Then Return
    
    (...)
  End Sub

This design has a major drawback : in_use blocks cannot be nested, since nesting two such blocks will turn in_use to false too early. In the following excerpt, the (...) section in the Process function will not run correctly, because in_use will have been set to false when it runs.

  private void Process() {
    in_use = true;
    Action1();
    Action2();
    (...) // in_use == false here (!)
    in_use = false;
  }

  private void Action1() {
    in_use = true;
    (...)
    in_use = false;
  }

  private void Action2() {
    in_use = true;
    (...)
    in_use = false;
  }
  Private Sub Process()
    InUse = True
    Action1()
    Action2()
    (...) ' InUse = False here (!)
    InUse = False
  End Sub

  Private Sub Action1()
    InUse = True
    (...)
    InUse = False
  End Sub

  Private Sub Action2()
    InUse = True
    (...)
    InUse = False
  End Sub

Such errors are difficult to spot in large applications, and often lead to hard-to-track bugs.

Robust boolean flags

A useful trick in such cases is to replace your boolean flags with boolean properties, linked to a counter : every time you set in_use to true, the counter is incremented; every time you set it to false, the counter is decremented. Retrieving in_use returns true when the counter is greater than 0, and false otherwise. That’s somewhat similar to the way semaphores work in parallel programming.

  using System.Diagnostics;

  private int users_count = 0;

  public bool in_use {
    get { 
      return users_count > 0;
    }

    set {
      users_count += (value ? 1 : -1);
      Debug.Assert(users_count >= 0);
    }
  }
  Imports System.Diagnostics
  
  Private UsersCount As Integer

  Public Property InUse As Boolean
    Get
      Return UsersCount > 0
    End Get

    Set(value As Boolean)
      UsersCount += If(value, 1, -1)
      Debug.Assert(UsersCount >= 0)
    End Set
  End Property

You can now nest in_use blocks safely.

Going further

The previous construct is not thread-safe, and might end up in an inconsistent state if an exception occurs in the body of an in_use block (in_use will never be set to false if an exception occurs in an in_use block).

We solve both problems by declaring a Flag class which allows for the following construct:

  private void Process() {
    using (Flag flag = new Flag("custom name")) {
      (...)
    }
  }
  
  private void OnEvent() {
    if (Flag.InUse("custom name"))
      return;
      
    (...)
  }
  Private Sub Process()
    Using flag As New Flag("custom name")
      (...)
    End Using
  End Sub
  
  Private Sub OnEvent()
    If Flag.InUse("custom name") Then Return

    (...)
  End Sub

Here is the implementation; the Flag class exposes one static method, InUse(string), which returns whether determine whether a resource designated by a string is in use. The Flag constructor registers a new user by incrementing the flag_users counter, while the destructor accordingly decrements the flag_users counter.

  class Flag : IDisposable {
    string name;
    private static Dictionary<string, int> flag_users = new Dictionary<string,int>();

    public static bool InUse(string name) {
      lock (flag_users)
        return (flag_users.ContainsKey(name) && flag_users[name] > 0);
    }

    public Flag(string name) {
      this.name = name; 

      lock (flag_users) {
        if (!flag_users.ContainsKey(name))
          flag_users.Add(name, 0);

        flag_users[name] += 1;
      }
    }
  
    public void Dispose() {
      lock (flag_users)
        flag_users[name] -= 1;
    }
  }
  Class Flag
    Implements IDisposable

    Private Name As String
    Private Shared FlagUsersCount As New Dictionary(Of String, Integer)()

    Public Shared Function InUse(name As String) As Boolean
      SyncLock FlagUsersCount
        Return (FlagUsersCount.ContainsKey(name) AndAlso FlagUsersCount(name) > 0)
      End SyncLock
    End Function

    Public Sub New(Name As String)
      Me.Name = Name

      SyncLock FlagUsersCount
        If Not FlagUsersCount.ContainsKey(Name) Then
          FlagUsersCount.Add(Name, 0)
        End If

        FlagUsersCount(Name) += 1
      End SyncLock
    End Sub

    Public Sub Dispose() Implements System.IDisposable.Dispose
      SyncLock FlagUsersCount
        FlagUsersCount(Name) -= 1
      End SyncLock
    End Sub
  End Class

How do you implement your own boolean flags? Post examples in the comments!

Leave a Reply

Your email address will not be published. Required fields are marked *