HS Protocol

The nodes are in a ring. Every node has a UID \(u\).

Initialize:
  uid           // the unique ID of this process
  active := true
  status := null
  phase := 0

// The algorithm proceeds until a leader is chosen
while status = null do
  if active then
    forward_msg := (uid, distance = 2^phase, direction = forward)
    backward_msg := (uid, distance = 2^phase, direction = backward)
  else
    forward_msg := null
    backward_msg := null

  // ---------------------
  // Forward direction phase
  // ---------------------
  for step in 1 to 2^phase do
    // Send forward_msg to successor (u+1)
    send forward_msg to (u+1)
    // Receive a forward-traveling message from predecessor (u-1)
    forward_in := receive from (u-1)

    if forward_in != null then
      let (f_id, f_dist, f_dir) = forward_in
      if f_id > uid then
        // Found a larger ID
        active := false
      else
        // f_id <= uid
        f_dist := f_dist - 1
        if f_dist = 0 then
          // The message completed its intended distance
          if f_id = uid and active
            // Got our own ID back without interruption
            // This direction is "cleared" (no larger found)
            forward_msg := null
          else
            // If not our ID or we are inactive, discard
            forward_msg := null
        else
          // Continue forwarding smaller ID
          forward_msg := (f_id, f_dist, f_dir)
    else
      // No incoming message means no forwarding needed
      forward_msg := null

  // ---------------------
  // Backward direction phase
  // ---------------------
  for step in 1 to 2^phase do
    // Send backward_msg to predecessor (u-1)
    send backward_msg to (u-1)
    // Receive a backward-traveling message from successor (u+1)
    backward_in := receive from (u+1)

    if backward_in != null then
      let (b_id, b_dist, b_dir) = backward_in
      if b_id > uid then
        // Found a larger ID
        active := false
      else
        // b_id <= uid
        b_dist := b_dist - 1
        if b_dist = 0 then
          // The message completed its intended distance
          if b_id = uid and active
            // Got our own ID back without interruption
            backward_msg := null
          else
            backward_msg := null
        else
          backward_msg := (b_id, b_dist, b_dir)
    else
      backward_msg := null

  // ---------------------
  // End of phase checks
  // ---------------------
  if active and forward_msg = null and backward_msg = null then
    // No larger ID found this phase. We continue unless we detect a global stop.
    // If at any point we discover we are the only active process, we become leader.
    // (This detection can be via additional logic or known termination conditions.)
    if "no other active process" detected then
      status := leader
    else
      phase := phase + 1
  else
    // We encountered a larger ID or are waiting for another phase.
    if not active
      // We remain inactive and just pass messages for others in future phases.
      phase := phase + 1
    else
      // If still active but haven't confirmed leadership, proceed to next phase
      phase := phase + 1

Example

Below is a step-by-step example to illustrate how the Hirschberg–Sinclair (HS) algorithm works in practice. Consider a small ring and let us walk through a few phases to see how processes drop out until only the leader (the node with the largest ID) remains.

Setup:

Suppose we have a ring of \(8\) processes arranged in a circle:

P0 → P1 → P2 → P3 → P4 → P5 → P6 → P7 → (back to P0)

Each process has a unique identifier (ID). Let’s assign them as follows:

P0: ID = 3 P1: ID = 5 P2: ID = 10 P3: ID = 2 P4: ID = 9 P5: ID = 8 P6: ID = 4 P7: ID = 7

Thus, the ring with IDs is:

P0(3) P7(7) P1(5) P6(4) P2(10) P5(8) P3(2) P4(9)

Initially, all processes are active candidates.

Overview of the Algorithm’s Structure:

The HS algorithm runs in phases \(i = 0,1,2,\ldots\). In each phase \(i\):

  • Each active process sends out "challenge" messages with its ID up to \(2^{i}\) hops in both directions.
  • If a process with a smaller ID encounters a larger ID within that radius, it learns that it cannot be the leader and becomes inactive.
  • As phases progress, the distance doubles, but fewer processes remain active.

Eventually, after \(O(\log{n})\) phases, only the largest ID process remains active and declares itself the leader.

Phase 0 (i = 0)

Distance: \(2^0=1\) hop.

Every active process (which is all of them at this point) sends a challenge message containing its ID one hop in both directions.

  • P0(3) sends its ID (3) to P7 and P1.
  • P1(5) sends its ID (5) to P0 and P2.
  • P2(10) sends its ID (10) to P1 and P3.
  • P3(2) sends its ID (2) to P2 and P4.
  • P4(9) sends its ID (9) to P3 and P5.
  • P5(8) sends its ID (8) to P4 and P6.
  • P6(4) sends its ID (4) to P5 and P7.
  • P7(7) sends its ID (7) to P6 and P0.

What happens at each receiver?

  • When P7 receives ID(3) from P0, it compares 3 to its own ID(7). Since 7 > 3, P7 knows there is a smaller contender (3) behind it, but P7 doesn’t drop out for that reason. Instead, P0 might realize it has encountered someone bigger. Similarly, P1 sees ID(3) from P0 and compares 3 < 5, so P1 is bigger than P0.
  • More importantly, each challenge message that moves outward checks if it encounters a bigger ID. At the end of this step, each process looks at what messages it sent and what “larger IDs” it learned of.

Let’s consider one process at a time:

  • P0(3):
    • P0 sent ID(3) to P1 and P7.
    • P1 has ID(5), which is greater than 3. Thus, P0 learns there is a process with ID(5) nearby.
    • Similarly, P7 has ID(7), which is also greater than 3.
    • Conclusion: P0 finds two processes in its immediate neighborhood larger than itself. P0 becomes inactive.
  • P1(5):
    • P1 sent ID(5) to P0(3) and P2(10).
    • P2(10) is greater than 5, so P1 discovers a larger ID to one side.
    • Therefore, P1 becomes inactive.
  • P2(10):
    • P2 sent ID(10) to P1(5) and P3(2).
    • Both neighbors have smaller IDs (5 and 2), so P2 sees no one larger within 1 hop.
    • P2 remains active (so far it’s the “king of the hill” in its immediate area).
  • P3(2):
    • Sent ID(2) to P2(10) and P4(9).
    • P2(10) and P4(9) are both larger than 2.
    • P3 becomes inactive.
  • P4(9):
    • Sent ID(9) to P3(2) and P5(8).
    • P3(2) is smaller, P5(8) is smaller. No larger ID found.
    • P4 stays active.
  • P5(8):
    • Sent ID(8) to P4(9) and P6(4).
    • P4(9) is larger than 8.
    • P5 becomes inactive.
  • P6(4):
    • Sent ID(4) to P5(8) and P7(7).
    • P5(8) is larger than 4 and P7(7) is also larger.
    • P6 becomes inactive.
  • P7(7):
    • Sent ID(7) to P6(4) and P0(3).
    • Both 4 and 3 are smaller than 7, so P7 sees no larger ID within 1 hop.
    • P7 remains active.
  • Result after Phase 0:
    • Active: P2(10), P4(9), P7(7)
    • Inactive: P0(3), P1(5), P3(2), P5(8), P6(4)

Phase 1 (i = 1)

Distance: \(2^1=2\) hops.

Now only P2, P4, and P7 are active. They will send out their IDs 2 hops in both directions.

Consider each active node:

  • P2(10):
    • Two hops clockwise: P2 → P3 → P4. Encounters P3(2) and then P4(9). Among these, P4(9) is large but still smaller than 10.
    • Two hops counterclockwise: P2 → P1 → P0. Encounters IDs 5 and 3, both smaller than 10.
    • No larger ID found. P2 remains active.
  • P4(9):
    • Two hops clockwise: P4 → P5 → P6. Encounters IDs 8 and 4, both smaller than 9.
    • Two hops counterclockwise: P4 → P3 → P2. Encounters ID 2 and then ID 10. Uh-oh, P4 finds a larger ID (10) at P2.
    • P4 discovers that there’s a bigger contender (P2) within 2 hops. P4 becomes inactive.
  • P7(7):
    • Two hops clockwise: P7 → P0 → P1. Encounters IDs 3 and 5, both smaller than 7.
    • Two hops counterclockwise: P7 → P6 → P5. Encounters IDs 4 and 8. Here, 8 is actually larger than 7. But wait, P5 was marked inactive at the end of the last phase. Even though P5 is inactive, it still has ID=8, which is known to be larger than 7. Generally, an inactive node will forward messages, but the key point is that P7 sees a node with a bigger ID in the path. Let’s assume the algorithm’s variant we’re using stops the challenge once a bigger ID is discovered or that P7 eventually learns about P5’s larger ID from the returned messages.
    • P7 discovers that ID=8 is larger than 7, so P7 becomes inactive.
  • Result after Phase 1:
    • Active: P2(10)
    • Inactive: P0(3), P1(5), P3(2), P4(9), P5(8), P6(4), P7(7)

    Now only P2 with ID=10 remains active.

Phase 2 (and beyond)

By the start of Phase 2, there is only one active process left: P2(10). Since no other processes are active, P2 automatically becomes the leader. The algorithm typically checks a few more phases or has a termination condition, but effectively, no one will challenge P2 anymore, and P2 knows it’s the largest remaining contender.

Final Outcome:

P2 (with ID = 10) is the leader.

Notes

  • This is from Distributed algorithms. Nancy A. Lynch. 1997
  • This is also called the HS algorithm (Hirschberg and Sinclair).
  • The round complexity is \(O(n\log{n})\).
  • The communication complexity is \(O(n\log{n})\).
  • It also works if the nodes do not start at the same time.