Index: doc/theses/thierry_delisle_PhD/code/relaxed_list.hpp
===================================================================
--- doc/theses/thierry_delisle_PhD/code/relaxed_list.hpp	(revision 009285338bda08fb17b1c6b1b634c908fe3da2d9)
+++ doc/theses/thierry_delisle_PhD/code/relaxed_list.hpp	(revision 47a541d4bbfffda62bc3524d84d32cee5aa50dae)
@@ -8,4 +8,5 @@
 #define BITMASK 2
 #define DISCOVER 3
+#define SNZM 4
 
 #ifndef VARIANT
@@ -25,4 +26,5 @@
 #include "utils.hpp"
 #include "snzi.hpp"
+#include "snzm.hpp"
 
 using namespace std;
@@ -71,5 +73,6 @@
 			"SNZI",
 			"BITMASK",
-			"DISCOVER"
+			"DISCOVER",
+			"SNZI + MASK"
 		};
 		return names[VARIANT];
@@ -80,5 +83,7 @@
 		, numLists(numLists)
 		#if VARIANT == SNZI || VARIANT == DISCOVER
-			, snzi( std::log2( numLists / 8 ) )
+			, snzi( 1, 8 )
+		#elif VARIANT == SNZM
+			, snzm( numLists )
 		#endif
 	{
@@ -112,5 +117,5 @@
 			if( !lists[i].lock.try_lock() ) continue;
 
-			#if VARIANT != SNZI && VARIANT != DISCOVER
+			#if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER
 				__attribute__((unused)) int num = numNonEmpty;
 			#endif
@@ -126,4 +131,6 @@
 				#elif VARIANT == SNZI
 					snzi.arrive(i);
+				#elif VARIANT == SNZM
+					snzm.arrive(i);
 				#elif VARIANT == BITMASK
 					numNonEmpty++;
@@ -138,5 +145,5 @@
 				#endif
 			}
-			#if VARIANT != SNZI && VARIANT != DISCOVER
+			#if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER
 				assert(numNonEmpty <= (int)numLists);
 			#endif
@@ -147,5 +154,5 @@
 			#ifndef NO_STATS
 				tls.pick.push.success++;
-				#if VARIANT != SNZI && VARIANT != DISCOVER
+				#if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER
 					tls.empty.push.value += num;
 					tls.empty.push.count += 1;
@@ -194,4 +201,38 @@
 				if(auto node = try_pop(i, j)) return node;
 			}
+		#elif VARIANT == SNZM
+			while(snzm.query()) {
+				tls.pick.pop.mask_attempt++;
+				unsigned i, j;
+				{
+					// Pick two random number
+					unsigned ri = tls.rng.next();
+					unsigned rj = tls.rng.next();
+
+					// Pick two nodes from it
+					unsigned wdxi = ri & snzm.mask;
+					unsigned wdxj = rj & snzm.mask;
+
+					// Get the masks from the nodes
+					size_t maski = snzm.masks(wdxi);
+					size_t maskj = snzm.masks(wdxj);
+
+					if(maski == 0 && maskj == 0) continue;
+
+					unsigned bi = rand_bit(ri >> snzm.depth, maski);
+					unsigned bj = rand_bit(rj >> snzm.depth, maskj);
+
+					assertf(bi < 64, "%zu %u", maski, bi);
+					assertf(bj < 64, "%zu %u", maskj, bj);
+
+					i = (bi << snzm.depth) | wdxi;
+					j = (bj << snzm.depth) | wdxj;
+
+					assertf(i < numLists, "%u %u", bj, wdxi);
+					assertf(j < numLists, "%u %u", bj, wdxj);
+				}
+
+				if(auto node = try_pop(i, j)) return node;
+			}
 		#elif VARIANT == BITMASK
 			int nnempty;
@@ -266,5 +307,5 @@
 		if( !list.lock.try_lock() ) return nullptr;
 
-		#if VARIANT != SNZI && VARIANT != DISCOVER
+		#if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER
 			__attribute__((unused)) int num = numNonEmpty;
 		#endif
@@ -291,4 +332,6 @@
 			#elif VARIANT == SNZI
 				snzi.depart(w);
+			#elif VARIANT == SNZM
+				snzm.depart(w);
 			#elif VARIANT == BITMASK
 				numNonEmpty--;
@@ -306,10 +349,10 @@
 		// Unlock and return
 		list.lock.unlock();
-		#if VARIANT != SNZI && VARIANT != DISCOVER
+		#if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER
 			assert(numNonEmpty >= 0);
 		#endif
 		#ifndef NO_STATS
 			tls.pick.pop.success++;
-			#if VARIANT != SNZI && VARIANT != DISCOVER
+			#if VARIANT != SNZM && VARIANT != SNZI && VARIANT != DISCOVER
 				tls.empty.pop.value += num;
 				tls.empty.pop.count += 1;
@@ -459,4 +502,6 @@
 	#if VARIANT == SNZI || VARIANT == DISCOVER
 		snzi_t snzi;
+	#elif VARIANT == SNZM
+		snzm_t snzm;
 	#else
 		std::atomic_int numNonEmpty  = { 0 };  // number of non-empty lists
Index: doc/theses/thierry_delisle_PhD/code/snzm.hpp
===================================================================
--- doc/theses/thierry_delisle_PhD/code/snzm.hpp	(revision 47a541d4bbfffda62bc3524d84d32cee5aa50dae)
+++ doc/theses/thierry_delisle_PhD/code/snzm.hpp	(revision 47a541d4bbfffda62bc3524d84d32cee5aa50dae)
@@ -0,0 +1,186 @@
+#pragma once
+
+#include "utils.hpp"
+
+
+class snzm_t {
+	class node;
+public:
+	const unsigned depth;
+	const unsigned mask;
+	const int root;
+	std::unique_ptr<snzm_t::node[]> nodes;
+
+	snzm_t(unsigned numLists);
+
+	void arrive(int idx) {
+		int i = idx & mask;
+		nodes[i].arrive( idx >> depth);
+	}
+
+	void depart(int idx) {
+		int i = idx & mask;
+		nodes[i].depart( idx >> depth );
+	}
+
+	bool query() const {
+		return nodes[root].query();
+	}
+
+	size_t masks( unsigned node ) {
+		/* paranoid */ assert( (node & mask) == node );
+		return nodes[node].mask;
+	}
+
+private:
+	class __attribute__((aligned(64))) node {
+		friend class snzm_t;
+	private:
+
+		union val_t {
+			static constexpr char Half = -1;
+
+			uint64_t _all;
+			struct __attribute__((packed)) {
+				char cnt;
+				uint64_t ver:56;
+			};
+
+			bool cas(val_t & exp, char _cnt, uint64_t _ver) volatile {
+				val_t t;
+				t.ver = _ver;
+				t.cnt = _cnt;
+				/* paranoid */ assert(t._all == ((_ver << 8) | ((unsigned char)_cnt)));
+				return __atomic_compare_exchange_n(&this->_all, &exp._all, t._all, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+			}
+
+			bool cas(val_t & exp, const val_t & tar) volatile {
+				return __atomic_compare_exchange_n(&this->_all, &exp._all, tar._all, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+			}
+
+			val_t() : _all(0) {}
+			val_t(const volatile val_t & o) : _all(o._all) {}
+		};
+
+		//--------------------------------------------------
+		// Hierarchical node
+		void arrive_h() {
+			int undoArr = 0;
+			bool success = false;
+			while(!success) {
+				auto x{ value };
+				/* paranoid */ assert(x.cnt <= 120);
+				if( x.cnt >= 1 ) {
+					if( value.cas(x, x.cnt + 1, x.ver ) ) {
+						success = true;
+					}
+				}
+				/* paranoid */ assert(x.cnt <= 120);
+				if( x.cnt == 0 ) {
+					if( value.cas(x, val_t::Half, x.ver + 1) ) {
+						success = true;
+						x.cnt = val_t::Half;
+						x.ver = x.ver + 1;
+					}
+				}
+				/* paranoid */ assert(x.cnt <= 120);
+				if( x.cnt == val_t::Half ) {
+					/* paranoid */ assert(parent);
+					parent->arrive();
+					if( !value.cas(x, 1, x.ver) ) {
+						undoArr = undoArr + 1;
+					}
+				}
+			}
+
+			for(int i = 0; i < undoArr; i++) {
+				/* paranoid */ assert(parent);
+				parent->depart();
+			}
+		}
+
+		void depart_h() {
+			while(true) {
+				auto x = (const val_t)value;
+				/* paranoid */ assertf(x.cnt >= 1, "%d", x.cnt);
+				if( value.cas( x, x.cnt - 1, x.ver ) ) {
+					if( x.cnt == 1 ) {
+						/* paranoid */ assert(parent);
+						parent->depart();
+					}
+					return;
+				}
+			}
+		}
+
+		//--------------------------------------------------
+		// Root node
+		void arrive_r() {
+			__atomic_fetch_add(&value._all, 1, __ATOMIC_SEQ_CST);
+		}
+
+		void depart_r() {
+			__atomic_fetch_sub(&value._all, 1, __ATOMIC_SEQ_CST);
+		}
+
+		//--------------------------------------------------
+		// Interface node
+		void arrive() {
+			/* paranoid */ assert(!is_leaf);
+			if(is_root()) arrive_r();
+			else arrive_h();
+		}
+
+		void depart() {
+			/* paranoid */ assert(!is_leaf);
+			if(is_root()) depart_r();
+			else depart_h();
+		}
+
+	private:
+		volatile val_t value;
+		volatile size_t mask = 0;
+		class node * parent = nullptr;
+		bool is_leaf = false;
+
+		bool is_root() {
+			return parent == nullptr;
+		}
+
+	public:
+		void arrive( int bit ) {
+			/* paranoid */ assert( is_leaf );
+			/* paranoid */ assert( (mask & ( 1 << bit )) == 0 );
+
+			__atomic_fetch_add( &mask, 1 << bit, __ATOMIC_RELAXED );
+			arrive_h();
+
+		}
+
+		void depart( int bit ) {
+			/* paranoid */ assert( is_leaf );
+			/* paranoid */ assert( (mask & ( 1 << bit )) != 0 );
+
+			depart_h();
+			__atomic_fetch_sub( &mask, 1 << bit, __ATOMIC_RELAXED );
+		}
+
+		bool query() {
+			/* paranoid */ assert(is_root());
+			return value._all > 0;
+		}
+	};
+};
+
+snzm_t::snzm_t(unsigned numLists)
+	: depth( std::log2( numLists / 8 ) )
+	, mask( (1 << depth) - 1 )
+	, root( (1 << (depth + 1)) - 2 )
+	, nodes(new node[ root + 1 ]())
+{
+	int width = 1 << depth;
+	for(int i = 0; i < root; i++) {
+		nodes[i].is_leaf = i < width;
+		nodes[i].parent = &nodes[(i / 2) + width ];
+	}
+}
Index: doc/theses/thierry_delisle_PhD/code/utils.hpp
===================================================================
--- doc/theses/thierry_delisle_PhD/code/utils.hpp	(revision 009285338bda08fb17b1c6b1b634c908fe3da2d9)
+++ doc/theses/thierry_delisle_PhD/code/utils.hpp	(revision 47a541d4bbfffda62bc3524d84d32cee5aa50dae)
@@ -106,4 +106,5 @@
 }
 
+static inline unsigned rand_bit(unsigned rnum, size_t mask) __attribute__((artificial));
 static inline unsigned rand_bit(unsigned rnum, size_t mask) {
 	unsigned bit = mask ? rnum % __builtin_popcountl(mask) : 0;
