Abstract 1 Introduction 2 Preliminaries 3 Solving set agreement using SRN𝒌 objects and registers 4 Implementing SRN𝒌 objects from solutions to set agreement and registers 5 SRN𝒌 is weaker than SRN𝒌 6 Closing Remarks References Appendix A Why an approach to showing properties of WRN𝒌 objects does not work

A Hierarchy of Unrestricted Deterministic Objects with Consensus Number 1

Warren Zhu ORCID Department of Computer Science, University of Toronto, Canada
Abstract

The consensus number of a shared object is the maximum number of processes that can solve consensus in a wait-free manner using copies of the object and registers. In 2016, to prove that an object is not fully characterized by its consensus number, Afek, Ellen and Gafni showed that for all integers n2, there exists an infinite sequence of deterministic objects of consensus number n with strictly increasing computational power. In 2018, Daian, Losa, Afek, and Gafni constructed an infinite sequence of deterministic objects of consensus number 1 with strictly decreasing computational power, but the single operation that each of these objects supports is restricted in how it can be used during an execution. As restrictions can have an effect on an object’s consensus number, it was left as an open question whether the same result holds without this restriction.

In this paper, we construct an infinite sequence of unrestricted deterministic objects with strictly decreasing computational power. All objects in this sequence have consensus number 1.

Keywords and phrases:
Shared Memory, Wait-free, Set Agreement, Consensus Hierarchy
Copyright and License:
[Uncaptioned image] © Warren Zhu; licensed under Creative Commons License CC-BY 4.0
2012 ACM Subject Classification:
Theory of computation Distributed computing models
Acknowledgements:
I want to thank my advisor Faith Ellen for her many helpful discussions. I also want to thank Sam Toueg and Roei Tell for their feedback and advice.
Funding:
This work was supported in part by the Natural Science and Engineering Research Council grant RGPIN-2020-04178.
Editors:
Andrei Arusoaie, Emanuel Onica, Michael Spear, and Sara Tucci-Piergiovanni

1 Introduction

In 1991, Herlihy [10] classified objects based on their consensus number, which is the maximum number of processes that can solve consensus in a wait-free manner using copies of the object and registers. An object has an infinite consensus number if it is possible to solve consensus for any number of processes using copies of the object and registers. This classification partially characterizes an object’s computational power: if object O has a consensus number n and object O has a larger consensus number, then in systems with more than n processes, there is no implementation of O using copies of O and registers. Furthermore, in systems of n processes, there is a implementation of every object using copies of O and registers.

However, in 2016, Afek, Ellen, and Gafni [2] showed that the consensus number of a deterministic object does not fully characterize its computational power when it is at least 2. This was done by introducing the deterministic objects On,k for all integers n,k2. Object On,k has consensus number n and can be implemented from a suitably initialized On,k+1 object. Furthermore, it was shown that there is no implementation of an On,k+1 object using On,k objects and registers in systems of nk+n+k processes. Therefore, for all n2, the infinite sequence On,2,On,3, has strictly increasing computational power and consists only of deterministic objects with consensus number n.

In 2018, Daian, Losa, Afek, and Gafni [8] introduced a deterministic object, 1sWRNk, with consensus number 1, for all integers k3. Each 1sWRNk object supports a single operation that takes two inputs, a value and an integer modulo k. It requires that, in any execution, there are at most k invocations of the operation on the 1sWRNk object, each with a different second input. For integers k>k, 1sWRNk objects and registers can implement 1sWRNk objects in systems of k or fewer processes, but there is no implementation of 1sWRNk objects using 1sWRNk objects and registers in systems of k processes. Hence, the infinite sequence 1sWRN3,1sWRN4, has strictly decreasing computational power.

However, restrictions on the number of times that an object can be accessed can change an object’s consensus number. For example, consider the n-consensus object, for integers n1. It supports a single operation, propose, that can be invoked at most n times. This operation takes a non-negative integer as input and returns the input of its first call. Note that an n-consensus object has consensus number n. Consider a modified 1-consensus object that can be accessed an unlimited number of times. The first call of the modified object’s operation returns its own input. Subsequent calls return . While a 1-consensus object has consensus number 1, the consensus task for 2 processes can be solved using a copy of the modified object and 2 single-writer registers: Each process writes its input to its register, calls propose with this input, and outputs its own value if it is returned. Otherwise, it outputs the value it reads from the register to which the other process wrote.

Although an unrestricted version of a 1sWRNk object, WRNk, was introduced in [8], the paper did not show that this object has consensus number 1, nor that WRNk is strictly more powerful than WRNk for k>k. In fact, we show in Appendix A that their argument for 1sWRNk objects does not work for WRNk objects. Thus, it is natural to ask if a similar infinite sequence exists for unrestricted deterministic objects with consensus number 1.

In this paper, we show that such a sequence does exist by introducing a deterministic object, SRNk, with consensus number 1, for all integers k3. This object supports one operation that takes an integer modulo k as input. Unlike the 1sWRNk object, there is no restriction on how an SRNk object can be used. Using known properties of the set agreement task, we show that, for k>k, an SRNk object can be implemented from SRNk objects and registers in systems of k or fewer processes, but an SRNk object cannot be implemented from SRNk objects and registers in systems of k processes. Thus, the infinite sequence SRN3,SRN4, has strictly decreasing computational power.

In Section 2, we provide our model description and definitions of relevant tasks and objects. In Section 3, we provide a solution to the (k1)-set agreement task for k processes using SRNk objects and registers. In Section 4, we provide an implementation of SRNk objects in systems of k processes using an arbitrary solution to the (k1)-set agreement task for k processes and registers. We then show how the existence of this implementation implies that SRNk objects have consensus number 1. In Section 5, we use properties of the set agreement task to show that, for k>k, SRNk objects are weaker than SRNk objects. In Section 6, we describe some related results and directions for future research.

2 Preliminaries

We consider the standard asynchronous shared memory model [10], where processes communicate using objects. Each object O has a set of possible values and supports a set of operations. We only consider sequentially specified objects. This means that each operation (together with its input) maps the current value of an object to a set of possible new values and responses. When an operation is applied to O, a value and response are chosen from the result of the mapping. An operation is deterministic if, for each input, it maps the current value to a single value and a single response. An object is deterministic if all of its supported operations are deterministic.

A system of n processes consists of the set of processes {pi|in}, where n is the additive group of integers modulo n. Each process, pi, has a unique identifier, i. In each step, a process applies an operation to an object. Each process must take steps according to the algorithm it is executing, and these steps are scheduled by an adversary. Processes are susceptible to crash failures: they may stop taking steps before they have finished performing an algorithm. A faulty process is one that crashes. Every algorithm considered in this paper is wait-free. This means that every non-faulty process completes its algorithm within a finite number of its own steps regardless of what other processes do.

A configuration specifies the state of every process and the value of every shared object. An execution is an alternating sequence of configurations and steps, starting from an initial configuration. An implementation of an object O consists of a representation of O from a set of base objects and algorithms for each process to perform when applying an operation supported by O. In an execution of the implementation, , processes call operations of O using these algorithms. A linearization of is a sequential execution, , consisting of all completed operations in and a subset of the uncompleted operations in . satisfies two conditions. First, the result of each completed operation in is the same as in . Second, for any two operations op and op in , if op is completed in before op begins, then op occurs before op in . An implementation of O is said to be linearizable if there is a linearization of every execution of the implementation [11]. All implementations considered in this paper are linearizable.

In a task, every process starts with an input and is supposed to produce an output. A solution to a task is a set of objects and an algorithm that describes how processes use these objects to produce a suitable output. In the consensus task, the output values in each execution must satisfy the following properties.

  • Validity: Every output is the input of some process.

  • Agreement: All outputs have the same value.

The consensus number of an object is the maximum number of processes that can solve the consensus task using copies of the object and registers. An object has an infinite consensus number if it is possible to solve consensus for any number of processes using copies of the object and registers.

A generalization of the consensus task is the k-set agreement task [6]. In this task, agreement is replaced with the k-agreement property.

  • 𝒌-agreement: There are at most k different output values.

Note that the 1-set agreement task is another name for the consensus task. The relationship between different set agreement tasks has been partially characterized by Chaudhuri and Reiners [7].

Theorem 1 (Partial Order Theorem).

Let n>k and m>j be positive integers. The k-set agreement task for n processes can be solved using copies of arbitrary solutions to the j-set agreement task for m processes and registers if and only if kjn/m or kjn/m+nmn/m.

In particular, for integers k>k>1, the (k1)-set agreement task for k processes cannot be solved using copies of arbitrary solutions to the (k1)-set agreement task for k processes and registers.

For integers k1, the SAk object can be used to solve the k-set agreement task. It supports the non-deterministic operation, propose, which takes a non-negative integer as input. In any sequence of propose operations invoked on the object, each propose operation op returns a value that was either the input of op or the input of a previous propose operation. In any execution, propose returns at most k distinct values. The sequential specification of an SAk object is given in Algorithm 1. Chan, Hadzilacos, and Toueg [3] showed that SAk objects can be implemented in systems of n>k processes from snapshot objects and any solution to the k-set agreement task for n processes.

Algorithm 1 The sequential specification of an SAk object.

A snapshot object supports the atomic operations scan and update. A scan returns a view, which consists of the values of all components of the object. An update(i,x) overwrites the value of component i of the object with the value x. We also allow processes to read individual components of a snapshot object. This can be implemented by having processes perform a scan and then throw away all other components of the result. Note that snapshot objects can be implemented from registers [1].

In this paper, we introduce the deterministic family of set-and-read-next objects. For integers k3, an SRNk object supports the operation srn: k{0,1}. For all ik and sequences of srn calls invoked on an SRNk object, an srn(i) call returns 1 if and only if some srn(i+1) call was invoked earlier in the sequence. Otherwise, it returns 0. Another way of thinking of the SRNk object is to consider an array of registers, R[0k1]. Each register in this array is initialized to 0. When an srn(i) call is invoked for some ik, the SRNk object atomically writes 1 to R[i] and returns the value in R[i+1]. The pseudocode for this definition is given in Algorithm 2.

Algorithm 2 The sequential specification of an SRNk object.

3 Solving set agreement using SRN𝒌 objects and registers

In this section, we show that the (k1)-set agreement task for k3 processes can be solved using an SRNk object and k registers. This is done in Algorithm 3. Each process pi writes its input to its register R[i], calls srn(i) on the SRNk object, and outputs its own value if 0 is returned. If 1 is returned, the process reads and outputs from R[i+1].

Lemma 2.

Algorithm 3 solves the (k1)-set agreement task for k3 processes.

Proof.

Let pi be a process. If the srn(i) call on line 2 returns 0, then pi outputs vi. If the srn(i) call returns 1, then pi+1 performed line 2 before pi did. Therefore, pi+1 performed line 1 before pi performed line 2, so pi reads and outputs vi+1 from R[i+1] on line 5. As pi outputs either vi or vi+1, validity is satisfied.

If any process is faulty, there are at most k1 different output values. So, suppose every process is non-faulty. Then, the first srn call performed returns 0 and the k-th srn call performed returns 1. This implies that there exists some jk such that the srn(j) call invoked by pj returns 0 and the srn(j+1) call invoked by pj+1 returns 1. Each process pi either outputs vi or vi+1. As neither pj nor pj+1 output vj+1, no process outputs vj+1. Thus, there are at most k1 different output values, so (k1)-agreement is satisfied.

Algorithm 3 Solution to the (k1)-set agreement task for processes {pi:ik}.
Corollary 3.

For integers kk3, the (k1)-set agreement task for k processes can be solved using an SRNk object and registers.

This can be done by having k processes perform Algorithm 3 and having the remaining kk processes output their own input values.

4 Implementing SRN𝒌 objects from solutions to set agreement and registers

4.1 The Implementation

In this section, we implement an SRNk object for k3 processes p0,,pk1 using an arbitrary solution to the (k1)-set agreement task for k processes and registers. We use snapshot objects, which can be implemented from registers [1]. We also use an SAk1 object, which can be implemented in systems of k processes from any solution to the (k1)-set agreement task for k processes and registers [3].

The implementation is given in Algorithm 4. It takes inspiration from Daian, Losa, Afek, and Gafni’s [8] implementation of an 1sWRNk object, but modifications have been made to allow each process to perform operations an unlimited number of times. As a result, the linearizations of our executions are significantly more complicated, as, in our case, we have to handle infinite executions, executions where multiple processes perform the same operation concurrently, as well as executions where the same operation is performed multiple times, but different values are returned.

Our implementation uses a k-component snapshot object R to represent the array of k registers of the SRNk object. Each srn(i) call begins by reading from R[i]. If an srn(i) call reads 0 from R[i], it updates R[i] to 1.

The first linearized srn call must return 0. Some of the first srn calls may access an SAk object, S. A process performing one of these srn calls uses its ID as the input to S.propose(). If the propose call returns this input, the srn call returns 0. In particular, the first srn call to invoke propose returns 0. To prevent srn calls that start later from accessing S, we use a doorway. The doorway is closed before any srn call returns.

The srn calls that do not access S or do not return 0 immediately after their access to S help one another to return appropriate values. Each of these srn calls will perform a scan of R (on line 7 or 16) and store the resulting view (on line 9 or 18) in another snapshot object, T, in the component corresponding to the number of 1’s that the view contains. Since each component of R can only change from 0 to 1, in any execution, the views in T are ordered by when they are taken.

Algorithm 4 Implementation of an SRNk object for processes {px:xk}.

An srn(i) call that reads 1 from R[i] (on line 1) returns the (i+1)-th component of its view of R (on line 20). Now consider an srn(i) call that reads 0 from R[i] and either does not access S or does not return 0 immediately after its access to S. If it finds a view stored in T in which R[i]=1 and R[i+1]=0, it returns 0. Otherwise, it returns 1. Note that this srn call cannot simply return the (i+1)-th component of its view of R. To see why, consider the following incorrect execution, where k4. First, an srn(3) call performs a solo execution, closing the doorway before returning 0. Then, an srn(0) call, c0, reads 0 from R[0] and updates it to 1 and an srn(1) call, c1, reads 0 from R[1] and updates it to 1. Next, c0 performs a scan of R and returns 1. An srn(2) call, c2, then performs a solo execution, updating R[2] to 1 before returning 1. Finally, c1 performs a scan of R and returns 1. Since c0 returns 1, it must be linearized after c1 and, since c1 returns 1, it must be linearized after c2. However, this cannot happen since c0 returns before c2 starts.

It is possible that no srn call reads an open doorway. For example, consider an execution in which a call of srn(i) reads R[i]=0 on line 1, updates R[i] to 1 on line 2, and then another call of srn(i) reads 1 from R[i] on line 1, and executes lines 16 to 20. The doorway is closed on line 19 before any process can read an open doorway, so no process will access the SAk1 object, S, during the execution or in any extension of it. When there are at most k concurrent srn calls, the algorithm still guarantees that one of the first srn calls return 0 even if no srn call reads an open doorway. This is because of the views of R that are contained in T.

However, our implementation does not always work when there are more than k concurrent srn calls. For example, consider the following execution that begins with a call of srn(i) that reads 0 from R[i] on line 1 and updates R[i] to 1 on line 2, for each ik. Then, a second call of srn(0) reads 1 from R[0] on line 1 and closes the doorway on line 19. Note that, in every extension of this execution, every scan of R on lines 7 and 16 consists only of 1s, so no call returns 0 on line 13. Moreover, no call reads an open doorway, so no call returns 0 on line 6. Since all srn calls returns 1, the execution cannot be linearized.

4.2 Properties of the Implementation

Fix a non-empty execution of our implementation of SRNk in which every srn call returns.

Observation 4.

Every srn call returns 0 or 1.

Lemma 5.

For all a such that 1ak, no two srn calls update T[a] with different views.

Proof.

A process only updates T[a] with a view of R containing a 1s. R is an atomic snapshot object that initially has 0 in all its components and the only updates to it change a single component to 1. Thus, each distinct view of R consists of a unique number of 1s.

Observation 6.

The doorway closes before any srn call returns.

Note that the doorway can be open when a process performs line 19. For example, suppose an srn(i) call performs lines 1 and 2 and goes to sleep. Then, another srn(i) call performs line 1 followed by lines 16 to 19.

Lemma 7.

If no srn calls encounter an open doorway on line 3, T is updated with a view of R that contains at least one 1 and one 0.

Proof.

Suppose no srn calls encounter an open doorway on line 3. Then, line 4 is never performed. Therefore, the doorway is closed on line 19. Let c be the first srn call to perform line 19. Let i and p respectively be the input of c and the process that invoked it. Since c performs line 19, c reads 1 from R[i] on line 1. Thus, some other srn(i) call c updated R[i] to 1 before c performs line 1. Let p be the process that invoked c.

The doorway is open before c performs line 19. Therefore, by Lemma 6, no srn call returns before c performs line 19. Thus, pp. Note that both p and p start but do not return an srn(i) call before c performs line 19. As there are only k processes, this implies that there exists a jk such that no srn(j) call has started before c performs line 19. Therefore, if r is the result of the scan performed during c, then r[i]=1 and r[j]=0. T is updated with this view on line 18 of c.

Lemma 8.

At least one srn call returns 0.

Proof.

First, suppose at least one srn call encounters an open doorway on line 3. Of these srn calls, in the first srn call to perform line 5, propose returns its input. Therefore, this srn call returns 0 on line 6.

So, suppose that no srn call encounters an open doorway on line 3. By Lemma 7, T is updated with a view, r, of R, before the doorway closes. Furthermore, r contains at least one 1 and one 0. As r consists of only 1’s and 0’s, there exists ik such that r[i]=1 and r[i+1]=0. As r[i]=1, at least one srn(i) call c has updated the value of R[i] to 1 on line 2. As no srn call encounters an open doorway on line 3, c performs line 3 after the doorway closes. This implies that when c performs a scan of T on line 10, T is already updated with r. Thus, c returns 0 on line 13.

Lemma 9.

Let ik. Let c be an srn(i) call that returns 0 on line 13 or 20. Then, before c returns, T is updated with some view, r, of R, such that r[i]=1 and r[i+1]=0.

Proof.

Suppose c returns 0 on line 13. Then, there exists an integer 1b<k such that the result, t, of the scan of T performed on line 10 satisfies t[b][i]=1 and t[b][i+1]=0. In other words, before c performs line 10, T was updated with some view, r, of R such that r[i]=1 and r[i+1]=0.

So, suppose c returns 0 on line 20. Then, c reads 1 from R[i] on line 1. Thus, the result, r, of the scan of R performed on line 16 satisfies r[i]=1. As c returns 0, r[i+1]=0. r is updated to a component of T on line 18.

Lemma 10.

There exists an ik such that no srn(i) call returns 0.

Proof.

Let Q be the set of inputs ik such that some srn(i) call reads an open doorway on line 3.

First, suppose |Q|=k. Then, Q=k. By Lemma 6, the doorway closes before any srn call returns. Thus, all srn calls that read an open doorway are concurrent. Since there are only k processes and some srn(i) call reads an open doorway for each ik, each process first calls srn with a different input. All of these calls read an open doorway on line 3 before any of them close the doorway on line 4. Since these calls all update R to 1 on line 2, the result of each scan of R performed on line 7 and 16 during the run has 1 in every component. Thus, no srn call returns 0 on line 13 or 20. Some process does not get its identifier back from S on line 5. Let i be the input to the first srn call by this process. Since this srn(i) call does not perform line 6 and all other srn(i) calls read R[i]=1 on line 1, no srn(i) call performs line 6. Thus, no srn(i) call returns 0.

So, suppose |Q|k1. For the sake of contradiction, assume that, for all ik, some srn(i) call returns 0. For each ik, the first srn(i) call that performs line 1 reads 0 from R[i] and so performs line 2. For each ikQ, let ci be the first srn(i) call that performs line 2. Let C={ci:ikQ}. Thus, |C|=k|Q|.

Let cj be the last call in C to perform line 2. Since jQ, no srn(j) call reads an open doorway on line 3. Thus, no srn(j) call returns 0 on line 6. By assumption, some srn(j) call returns 0. By Lemma 9, this implies that a scan of R such that its result, r, satisfies r[j]=1 and r[j+1]=0, is performed before the call returns. Thus, R[j+1] is updated from 0 to 1 after R[j] is. Since R[j] is updated from 0 to 1 when cj performs line 2 and cj is the last call in C to perform line 2, it follows that (j+1)Q. This implies some srn(j+1) call reads an open doorway on line 3. Thus, the scan that resulted in r is performed before the doorway closes.

Let c be the srn call in which the scan that resulted in r is performed. Let D be the set of srn calls that read an open doorway on line 3. Then |D||Q|.

Since the doorway is closed before any srn call performs line 7, the scan that resulted in r is performed on line 16. Thus, cCD. Since the scan that resulted in r is performed before the doorway closes, c starts before the doorway closes. By definition, all calls in D perform line 3 before the doorway closes. Furthermore, the scan that resulted in r is performed after cj performs line 2. Thus, all calls in CD{c} start before the doorway closes. By Lemma 6, no calls return prior to the doorway closing. Therefore, at least |C|+|D|+1k+1 srn calls execute concurrently, which cannot occur with just k processes. Thus, by contradiction, there exists an ik such that no srn(i) call returns 0.

Lemma 11.

Let ik. Let c be an srn(i) call that returns 1. Then all srn(i) calls that start after c returns also return 1.

Proof.

If c returns on line 14, then c performs a scan of R with result r on line 7. As c has updated R[i] to 1 on line 2, r[i]=1. Since c does not return on line 13, r[i+1]=1. Similarly, if c returns 1 on line 20, r[i+1]=1. Thus, before c returns, R[i+1] is updated to 1. Let c be an srn(i) call that starts after c returns. When c performs line 1, R[i]=1. Thus, c performs a scan of R on line 16 with result r and then returns r[i+1] on line 20. Since R[i+1] is updated to 1 before c returns, r[i+1]=1. Therefore, c returns 1.

Lemma 12.

Let ik. Let c be an srn(i) call. If some srn(i+1) call returns before c starts, then c returns 1.

Proof.

Suppose that some srn(i+1) call returns before c starts. This implies that before c starts, R[i+1] has been updated to 1 and the doorway is closed.

If c reads R[i]=1 on line 1, then the result, r, of the scan of R performed by c on line 16 satisfies r[i+1]=1. Thus, if c reads R[i]=1 on line 1, c returns 1 on line 20.

So, suppose c reads R[i]=0 on line 1. Then, R[i+1] was updated from 0 to 1 before R[i] was. Thus, no scan of R performed in any srn call results in a view, r, such that r[i]=1 and r[i+1]=0. As the doorway is closed before c starts, c does not return on line 6. c also does not return on line 13. Therefore, c returns 1 on line 14.

Lemma 13.

Let ik. Let c be an srn(i) call that does not return on line 6. If c performs a scan of R on line 7 or 16 before R[i+1] is updated to 1, then c returns 0.

Proof.

Suppose that c performs a scan of R on line 7 or 16 before R[i+1] is updated to 1. Then, the result of the scan, r, satisfies r[i+1]=0. Furthermore, since c either reads R[i]=1 on line 1 or updates R[i] to 1 on line 2, it follows that r[i]=1.

If c reads R[i]=1 on line 1, it returns r[i+1] on line 20, which is equal to 0. So, suppose c reads R[i]=0 on line 1. Then, it updates T with r on line 9 and performs a scan of T on line 10. As T has been updated with r prior to the scan, c returns 0 on line 13.

Distinguished Calls.

For each ik, let 𝒟i be the set of srn(i) calls that start before any srn(i) call returns. Note that 𝒟i is non-empty as long as there is at least one srn(i) call in the execution. In this case, we distinguish exactly one srn(i) call di𝒟i as follows.

  • If there exists a call in 𝒟i that returns 0, of these calls, let di be the one that starts first.

  • Otherwise, let di be the srn(i) call that performs line 2 first.

There are two very useful properties regarding these distinguished calls.

Lemma 14.

Let ik such that 𝒟i. If di returns 1, then all srn(i) calls return 1.

Proof.

From the way that di is chosen, if di returns 1, then all calls in 𝒟i return 1. Furthermore, as the first srn(i) call to return is in 𝒟i and thus returns 1, by Lemma 11, all srn(i) calls that start after it returns also return 1.

Lemma 15.

Let ik. Let c be an srn(i) call. If 𝒟i+1= or c returns before di+1 starts, then c returns 0.

Proof.

If 𝒟i+1=, then no srn(i+1) calls are performed. If 𝒟i+1 and di+1 returns 1, then by definition, di+1 is the srn(i+1) call that updates R[i+1] from 0 to 1. In both cases, c returns before R[i+1] is updated to 1. Note that c either returns on line 6 or performs line 7 or 16. By Lemma 13, c also returns 0 in the latter case.

So, suppose 𝒟i+1 and di+1 returns 0. For the sake of contradiction, assume c returns 1. By the contrapositive of Lemma 13, some srn(i+1) call c updates R[i+1] to 1 before c returns. As c returns before di+1 starts, c starts before di+1 starts. By definition, out of all the srn(i+1) calls that return 0, di+1 starts first. Thus, c returns 1.

As c returns 1 before di+1 starts, R[i+1] is updated to 1 before di+1 starts. Therefore, di+1 reads R[i+1]=1 on line 1 and performs lines 16 to 20. As di+1 returns 0, R[i+2]=0 when di+1 performs line 16. As c returns 1, c updates T with a view, r, of R, such that r[i+1]=1. As c returns before di+1 starts, r[i+2]=0. Since c executes line 2 and returns 1, c returns 1 on line 14. As c does not return on line 13, c executes line 10 before c updates T with r. This implies a scan of R is performed on line 7 of c before di+1 starts. Let r be the result of the scan. As c updates R[i+1] to 1 on line 2, r[i+1]=1. Since c also returns 1, r[i+2]=1. Thus, before di+1 starts, R[i+2] is updated to 1. Therefore, di+1 returns 1. This is a contradiction, so c returns 0.

Execution Graph.

The execution graph (of the execution) is the directed graph 𝒢=(𝒱,), where 𝒱 is the set of srn calls in the execution and (u,v) if and only if one of the following properties are satisfied for some ik:

  1. 1.

    u is an srn(i) call that returns 0 and v is an srn(i+1) call

  2. 2.

    u=di+1 is a distinguished call and v is an srn(i) call that returns 1

As shown in Lemmas 16 and 19, 𝒢 is acyclic and encodes nice temporal relationships between srn calls.

Note that consecutive nodes in 𝒢 have input values that differ by 1 modulo k. Additionally, in any directed path consisting of strictly increasing input values, all srn calls, except possibly the last one, return 0. Similarly, in any directed path consisting of strictly decreasing input values, all srn calls, except possibly the last one, are distinguished and all srn calls, except possibly the first one, return 1. Furthermore, 𝒢 does not contain any directed path chch1ch, where ch and ch are srn(h) calls and let ch1 is an srn(h1) call. This is because (ch,ch1) would imply that ch1 returns 1 but (ch1,ch) would imply that ch1 returns 0.

Lemma 16.

𝒢 contains no directed cycles.

Proof.

For the sake of contradiction, suppose that 𝒢 contains a directed cycle.

First, suppose that at least one node in the cycle returns 1. By the definition of 𝒢, all nodes that can be reached from this node also return 1. Furthermore, each edge of the cycle goes from an srn(i+1) call to an srn(i) call for some ik. As each node in a directed cycle has an outgoing edge, all nodes in the cycle are distinguished srn calls. Thus, the cycle contains distinguished calls of every possible input. Therefore, for all ik, di returns 1. By Lemma 14, all srn calls return 1. This contradicts Lemma 8.

So, suppose that all nodes in the cycle return 0. Then, each edge in the cycle goes from an srn(i) call to an srn(i+1) call for some ik, so the cycle contains calls of every possible input. This means that for every input, at least one srn call returns 0. This contradicts Lemma 10.

Thus, by contradiction, 𝒢 contains no directed cycles.

Lemma 17.

Let W be a directed path with strictly increasing input values from some srn(i) call ci to some srn(j) call cj. Suppose that the doorway is closed before ci starts. Then, for all srn(h) calls chW such that chcj, ci starts before R[h+1] is updated to 1.

Proof.

We will prove this lemma by induction.

Suppose ci reads R[i]=1 on line 1. Then, ci performs a scan of R on line 16. As ci returns 0, the result of the scan, r, satisfies r[i+1]=0. Thus, ci performs the scan before R[i+1] is updated to 1, so ci starts before R[i+1] is updated to 1.

Now, suppose ci reads R[i]=0 on line 1. This implies ci starts before R[i] is updated to 1. As ci does not read an open doorway, ci returns on line 13. By Lemma 9, before ci returns, T is updated with a view, r, of R, such that r[i]=1 and r[i+1]=0. This implies R[i] is updated to 1 before R[i+1] is updated to 1. Thus, ci starts before R[i+1] is updated to 1.

Let chW where hi and hj, and assume that ci starts before R[h] is updated to 1. Hence, ci starts before ch reads R[h]=1 on line 1 or updates R[h] to 1 on line 2. Since the doorway is closed before ci starts, ch returns 0 on line 13 or 20. By Lemma 9, before ch returns, T is updated with a view, r, of R, such that r[h]=1 and r[h+1]=0. This implies R[h] is updated to 1 before R[h+1] is. As ci starts before R[h] is updated to 1, ci starts before R[h+1] is updated to 1.

Lemma 18.

Let W be a directed path with strictly decreasing input values from some srn(i) call ci to some srn(j) call cj. If the srn(i) call that updates R[i] from 0 to 1 returns 1, then R[i+1] is updated to 1 before cj updates T.

Proof.

Since cj returns 1, it performs a scan of R before it updates T. Hence, if R[i+1] is updated to 1 before cj performs a scan of R, then it is updated to 1 before cj updates T. So, suppose that cj performs a scan of R before R[i+1] is updated to 1. As cj returns 1, the contrapositive of Lemma 13 implies that R[j+1] is updated to 1 before cj performs a scan of R. This implies the result of the scan, r, satisfies r[i+1]=0 and r[j+1]=1.

Let ci be the srn(i) call that updates R[i] from 0 to 1. Suppose that ci returns 1. Then it performs line 2 and returns on line 14. By the contrapositive of Lemma 13, R[i+1] is updated to 1 before ci performs a scan of R on line 7.

We prove that R[i+1] is updated to 1 before cj updates T by strong induction on the length of W. Suppose that the lemma is true for all directed paths of shorter length.

If r[i]=1, then ci performs a scan of T on line 10 before cj updates T with r on line 9 or 18. This is because r[i+1]=0 and ci does not return on line 13. In this case, R[i+1] is updated to 1 before cj updates T.

So, suppose that r[i]=0. If W is a path of length 1, then i=j+1, so r[i]=1. Thus, W has length greater than 1. Recall that r[j+1]=r[j]=1. Let ch be the last srn call in W such that its input, h, satisfies r[h]=0. Then r[h1]=1 and h1 is not equal to i and j. Let ch1 be the srn(h1) call following ch in W. Since ch1 is a distinguished srn call that returns 1, it performs line 2 and returns on line 14. Moreover, r[h1]=1 and r[h]=0, so ch1 performs a scan of T on line 10 before cj updates T with r.

Let W be the prefix of path W that ends with ch1. By the induction hypothesis applied to W, R[i+1] is updated to 1 before ch1 updates T on line 9. This occurs before ch1 performs a scan of T on line 10, which is before cj updates T.

Lemma 19.

Let W be a directed path from some srn(i) call ci to some srn(j) call cj. Then, ci starts before cj returns.

Proof.

By Lemma 6, if ci starts before the doorway is closed, ci starts before any srn call returns. So, assume that ci starts after the doorway is closed.

We need to consider three separate cases, as W can consist of strictly increasing input values, strictly decreasing input values, or increasing input values followed by decreasing input values.

First, we consider the case where W consists of strictly increasing input values. As there is some srn(j1) call in W, by Lemma 17, ci starts before R[j] is updated to 1. Since R[j] is updated to 1 before cj returns, this implies that ci starts before cj returns.

Now, we consider the case where W consists of strictly decreasing input values. Suppose ci starts before R[i] is updated to 1. If the length of W is 1, then j=i+1 and cj returns 1. By the contrapositive of Lemma 13, R[j+1] is updated to 1 on line 2 before cj performs a scan of R on line 7 or 16. This implies R[i] is updated to 1 before cj updates T. Thus, ci starts before cj returns. On the other hand, if the length of W is greater than 1, then i1j. Hence, the srn(i1) call immediately after ci in W is di1 and it returns 1. This implies di1 is the srn(i1) call that updates R[i1] from 0 to 1. Thus, by applying Lemma 18 to the suffix of W starting at di1, we see that R[i] is updated to 1 before cj updates T. Once again, ci starts before cj returns.

So, suppose ci starts after R[i] is updated to 1. Thus, ci reads R[i]=1 on line 1 and performs lines 16 to 20. Note that ci=di and ci is not the srn(i) call that updates R[i] to 1. Thus, ci returns 0. This implies that the result, r, of the scan that ci performs on line 16 satisfies r[i+1]=0. Therefore, ci performs line 16 before R[i+1] is updated to 1. Let c be the srn(i) call that updates R[i] from 0 to 1. As c is not distinguished and starts before ci, c returns 1. By Lemma 18, R[i+1] is updated to 1 before cj returns. Thus, ci performs line 16 before cj returns.

Finally, we consider the case where W consists of increasing input values followed by decreasing input values. In other words, W contains some sub-path chch+1ch, where hk, ch and ch are srn(h) calls and ch+1 is an srn(h+1) call. Since (ch,ch+1), it follows that ch returns 0. Thus, by definition, dh also returns 0. Similarly, since (ch+1,ch), ch returns 1. Therefore, chdh. By definition of 𝒢, ch has no outgoing edges, so ch=cj.

First, suppose ci=ch. Since ci is an srn(h) call that returns 0, it follows by Lemma 11 that ci starts before cj returns. Now, suppose cich. Then, there exists an srn(j1) call in W. By Lemma 17, ci starts before R[j] is updated to 1. Note that R[h] is updated to 1 before cj returns. Thus, ci starts before cj returns.

4.3 Linearization

We first linearize finite executions of Algorithm 4 in which every invocation of srn returns. Then, we show how to linearize any execution, even if it is infinite or processes crash.

Linearizing finite executions where every srn call returns.

Fix an execution . Let 𝒢=(𝒱,) be its execution graph. Let f be the srn call in 𝒱 that returns first, and let F be the set of srn calls in 𝒱 that start before f returns. In particular, fF. By Lemma 16, 𝒢 contains no directed cycles. Thus, either f is a source, or there exists a source qf in 𝒢 such that there is a directed path from q to f. By Lemma 19, q starts before f returns, so qF.

Algorithm 5 returns a linearization of the execution. This algorithm topologically sorts 𝒢, while guaranteeing that if an srn call u returns before another srn call v starts in , then u is ordered before v. For u,v𝒱, let uv denote that u occurs before v in the linearization.

Algorithm 5 Linearization of finite executions in which every srn call returns.
Lemma 20.

Let u,v be srn calls in 𝒱. If u returns before v starts in , then uv.

Proof.

Let us prove the contrapositive. Suppose vu. Let G^, f^, and F^ be the respective values of G, f, and F when v is added to . Then, u is a vertex in G^ and vF^. By definition of F^, v starts before f^ returns in . Furthermore, since f^ is the first call in G^ to return and u is a vertex in G^, f^ returns at or before u returns. Thus, v starts before u returns in .

Lemma 21.

Let ik. Let v be an srn(i) call in 𝒱. Then, v returns 1 in if and only if there exists an srn(i+1) call u in 𝒱 such that uv.

Proof.

First, suppose v returns 1 in . By the contrapositive of Lemma 15, 𝒟i+1 and di+1 starts before v returns. By part 2 of the definition of execution graph, (di+1,v). In each iteration of the while loop of Algorithm 5, only sources are added to the end of . Therefore, di+1 is added to before v, so di+1v.

Now, suppose v returns 0 in . Let u in 𝒱 be any srn(i+1) call. By part 1 of the definition of execution graph, (v,u). In each iteration of the while loop of Algorithm 5, only sources are added to the end of . Thus, vu. Therefore, there do not exist any srn(i+1) calls u such that uv. By Lemmas 20 and 21, Algorithm 5 returns a linearization of the execution.

Linearizing arbitrary executions.

Now, consider an execution of our implementation of SRNk that may be infinite and may contain invocations of srn that do not return. Let s1 be a step in after which no incomplete srn call takes another step.

Initially, R[i]=0 for all ik. Note that each process may read R[i]=0 on line 1 only during its first call of srn(i). If a process reads R[i]=1 on line 1, it only performs lines 1 and 16 to 20. Let s2 be a step in after which no srn call performs any of lines 2 to 14.

Let s be whichever of s1 and s2 occurs last. Let H be the prefix of up to and including s and let H′′ be the remainder of . Let I be the subset of incomplete srn calls in that update R on line 2 or T on line 18. Let be the execution that starts with H, but with the removal of the incomplete srn calls not in I. This is followed by the continuation of each call in I until it completes, which is then followed by the steps in H′′.

The calls removed from H do not modify any of the shared objects, so the behavior of the other calls is the same in and in . Processes that take steps in H′′ do not invoke any of the srn calls in I. Therefore, the state of the processes that take steps in H′′ are the same in and immediately before any step in H′′ is performed. During H′′, only lines 1 and 16 to 20 are performed, so R is the only shared object that is read. As R is only updated on line 2, the calls in I do not update R after s in . Thus, the behaviour of each call in except for those in I is the same as in , so is a valid execution.

Let J be the set of srn calls that start in H. Consider the shortest prefix of in which every call in J returns. Let be the continuation of this execution in which each incomplete srn call takes steps until it completes. Since these incomplete srn calls start in H′′, the only shared object they read from is R. Since R is only updated prior to H′′, the value returned by each of these calls is the same in and in . Notice is a finite execution in which every srn call returns. Then, Algorithm 5 produces a linearization of . Let be the sequential execution obtained from by appending the remaining calls in in the order in which they start.

In , each srn call returns the same value as in , which is the value returned in . Consider any srn(i) call, c, that is in , but not . This implies that, in , c starts in H′′. First, suppose c returns 1 in . Then, some srn(i+1) call occurred before c in . This srn(i+1) call also occurs in . Hence, R[i+1] is updated to 1 (on line 2) during . As line 2 is not performed during H′′, R[i+1]=1 immediately prior to H′′. Thus, c returns 1 in . Now, suppose c returns 0 in . Then no srn(i+1) call has occurred in , which implies no srn(i+1) call occurs during . As line 2 is not performed during H′′, it follows that R[i+1]=0 throughout H′′. Thus, c returns 0 in . Therefore, each srn call in returns the same value as in .

Let c be an srn call in that returns before another srn call c begins. First, suppose that c. Then c. As c returns before c starts in , c returns before c starts in . Therefore, c occurs before c in and, hence, in as well. Now suppose that c. If c, then c occurs before c in , since is a prefix of . If c, then c occurs before c in , because c starts before c in . Thus, is a linearization of .

contains every completed call in , so it contains every completed call in . Furthermore, each completed call in returns the same value as in and, hence, as in . If an srn call c returns before another srn call c begins in , then c returns before c in . Therefore, c occurs before c in . Thus, is a linearization of .

Lemma 22.

For k3, SRNk objects can be implemented in systems of k or fewer processes from any solution to the (k1)-set agreement task and registers.

An important consequence of this lemma is that SRNk objects have consensus number 1.

Lemma 23.

For k3, SRNk objects have consensus number 1.

Proof.

For the sake of contradiction, suppose that SRNk objects have a consensus number of at least 2. This would imply that the consensus task for 2 processes can be solved using SRNk objects and registers. Thus, by Lemma 22, the consensus task for 2 processes can be solved using any solution to the (k1)-set agreement task for k processes and registers. This contradicts Theorem 1.

5 SRN𝒌 is weaker than SRN𝒌

Let k,k be integers such that k>k3. In this section, we show that SRNk objects are strictly weaker than SRNk objects. Therefore, the infinite sequence (SRN3,SRN4,) has strictly decreasing computational power.

Lemma 24.

An SRNk object can be implemented from SRNk objects and registers in systems of k or fewer processes.

Proof.

Corollary 3 states that a solution to the (k1)-set agreement task for k processes can be implemented from SRNk objects and registers in systems of k processes. Thus, by Lemma 22, an SRNk object can be implemented from SRNk objects and registers in systems of k or fewer processes.

Lemma 25.

An SRNk object cannot be implemented from SRNk objects and registers in systems of k processes.

Proof.

For the sake of contradiction, assume that an SRNk object can be implemented from SRNk objects in systems of k processes. By Lemma 22, an SRNk object can be implemented in systems of k or fewer processes from any solution to the (k1)-set agreement task for k processes. Therefore, an SRNk object can be implemented in systems of k processes from any solution to the (k1)-set agreement task for k processes and registers. Thus, by Corollary 3, a solution to the (k1)-set agreement task for k processes can be implemented from any solution to the (k1)-set agreement task for k processes and registers. This contradictions Theorem 1.

6 Closing Remarks

This paper, in conjunction with the work of Afek, Ellen, and Gafni [2], shows that consensus number does not fully characterize the computational power of a deterministic object.

There are more refined classifications of an object’s computational power. The k-set agreement number of a shared object is the largest integer nk such that the k-set agreement task for nk processes can be solved using copies of the object and registers [9]. An object has an infinite k-set agreement number if it is possible to solve k-set agreement for any number of processes using copies of the object and registers. The set agreement power of an object is the infinite sequence of the object’s set agreement numbers, (n1,n2,) [3].

Chan, Hadzilacos, and Toueg [3] have shown that there exists a non-deterministic object with set agreement power n for all sequences n=(n1,n2,) such that nk+12nk for k1. These objects have a fixed number of ports, with the restriction that 2 or more processes cannot be simultaneously poised to perform operations on the same port in any execution. The behavior of operations supported by this object depend on the port on which the operation is performed on. An interesting consequence of this result is that there exists uncountably many objects with distinct computational power. Thus, it would be interesting to see if the same result holds for deterministic objects without the restriction of ports.

Set agreement power does not fully characterize the computational power of objects. Chan, Hadzilacos and Toueg [5] showed that for every n12, there exists a deterministic object O and a non-deterministic object O such that O and O both have no ports and have set agreement power (n1,n2,), but O cannot be implemented from copies of O and registers. Chan, Hadzilacos and Toueg [4] also proved a similar result for deterministic objects. For all n2, the (n+1)-P&G deterministic object has the same set agreement power as an n-consensus object, but cannot be implemented from n-consensus objects and registers. However, unlike the previous pair of objects, P&G objects have ports, and the behavior of operations depend on the port on which it is performed on.

It is open whether the same result holds for deterministic objects without ports, as well as for objects with consensus number n1=1. Furthermore, it is unclear whether the number of deterministic objects corresponding to each set agreement power is infinite. If this is the case, proving this will require ingenuity, as the infinite sequences of deterministic objects constructed in both this paper and the paper by Afek, Ellen and Gafni [2] relied on known properties of the set agreement task.

References

  • [1] Yehuda Afek, Hagit Attiya, Danny Dolev, Eli Gafni, Michael Merritt, and Nir Shavit. Atomic snapshots of shared memory. J. ACM, 40(4):873–890, September 1993. doi:10.1145/153724.153741.
  • [2] Yehuda Afek, Faith Ellen, and Eli Gafni. Deterministic objects: Life beyond consensus. In Proceedings of the 2016 ACM Symposium on Principles of Distributed Computing, PODC ’16, pages 97–106, 2016. doi:10.1145/2933057.2933116.
  • [3] David Yu Cheng Chan, Vassos Hadzilacos, and Sam Toueg. On the number of objects with distinct power and the linearizability of set agreement objects. In 31st International Symposium on Distributed Computing (DISC 2017), volume 91 of Leibniz International Proceedings in Informatics (LIPIcs), pages 12:1–12:14, 2017. doi:10.4230/LIPIcs.DISC.2017.12.
  • [4] David Yu Cheng Chan, Vassos Hadzilacos, and Sam Toueg. On the classification of deterministic objects via set agreement power. In Proceedings of the 2018 ACM Symposium on Principles of Distributed Computing, PODC ’18, pages 71–80, 2018. doi:10.1145/3212734.3212775.
  • [5] David Yu Cheng Chan, Vassos Hadzilacos, and Sam Toueg. Life beyond set agreement. Distributed Computing, 33(3):255–277, June 2020. doi:10.1007/s00446-020-00372-5.
  • [6] Soma Chaudhuri. More choices allow more faults: Set consensus problems in totally asynchronous systems. Information and Computation, 105(1):132–158, 1993. doi:10.1006/inco.1993.1043.
  • [7] Soma Chaudhuri and Paul Reiners. Understanding the set consensus partial order using the borowsky-gafni simulation. In Distributed Algorithms, pages 362–379, 1996.
  • [8] Eli Daian, Giuliano Losa, Yehuda Afek, and Eli Gafni. A Wealth of Sub-Consensus Deterministic Objects. In 32nd International Symposium on Distributed Computing (DISC 2018), volume 121 of Leibniz International Proceedings in Informatics (LIPIcs), pages 17:1–17:17, 2018. doi:10.4230/LIPIcs.DISC.2018.17.
  • [9] Carole Delporte-Gallet, Hugues Fauconnier, Eli Gafni, and Petr Kuznetsov. Set-Consensus Collections are Decidable. In 20th International Conference on Principles of Distributed Systems (OPODIS 2016), volume 70 of Leibniz International Proceedings in Informatics (LIPIcs), pages 7:1–7:15, 2017. doi:10.4230/LIPIcs.OPODIS.2016.7.
  • [10] Maurice Herlihy. Wait-free synchronization. ACM Trans. Program. Lang. Syst., 13(1):124–149, January 1991. doi:10.1145/114005.102808.
  • [11] Maurice Herlihy and Jeannette Wing. Linearizability: a correctness condition for concurrent objects. ACM Trans. Program. Lang. Syst., 12(3):463–492, July 1990. doi:10.1145/78969.78972.

Appendix A Why an approach to showing properties of WRN𝒌 objects does not work

One important step in [8] was to show that 1sWRNk objects can be implemented in systems of k processes from solutions to set agreement and registers. Their implementation appears in Algorithm 6. They used this result to show that 1sWRNk objects have consensus number 1 and that 1sWRNk objects are weaker than 1sWRNk for k>k.

Algorithm 6 Implementation of an 1sWRNk object from solutions to set agreement.

In their implementation, each process is only allowed to call wrn at most once. Furthermore, the first input of each call must match the process’s identifier. In this section, we show that if processes are allowed to perform operations multiple times, the unrestricted object obtained from this implementation is either non-linearizable or non-deterministic.

Consider the following three executions of Algorithm 6 for any k3:

  • E1 is a sequential execution in which p0 performs wrn(0,1), p1 performs wrn(1,1), p2 performs wrn(2,1), and p0 performs wrn(0,1). In this execution, the wrn(1,1) call returns .

  • E2 is the same, except that the order of the middle operations are interchanged. In this execution, the result, r, of the scan of R performed on line 6 of the wrn(2,1) call satisfies r[0]=1 and r[1]=. T[2] is then updated to r on line 7. As a result, the second wrn(0,1) call returns on line 11.

  • In E, process p0 first completes an wrn(0,1) call. Then, p1 performs wrn(1,1) and p2 performs wrn(2,1) concurrently, alternating steps. Before any scans of R are performed, R[0], R[1], and R[2] are all updated to 1. This implies that the result, r, of any scan of R performed on line 6 satisfies r[0]=r[1]=r[2]=1. Both calls return, with the wrn(1,1) call returning 1. Finally, p0 calls wrn(0,1), which returns 1.

If this is a linearizable, deterministic object, the linearization of the 4 operations and the values that they return in E must be the same as in E1 or E2. But this is not the case. Therefore, this implementation cannot be used to show that WRNk objects have consensus number 1 and that WRNk objects are weaker than WRNk for k>k. In fact, it is unknown whether these two properties are true.