Nutshell

Thoughts come and go, words stay eternal

28 Jul 2022

[program] Lock Free Update Data Struct

Abstract

  1. Instead of updating date in-place, create new one then update it’s reference, aka pointer
  2. It can only archive Eventually Consistent without lock
  3. The memory barrier of most high-level languages does not ensure that one thread will always be able to read the latest value that modified by other thread
  4. In a multithreading environment, if you need to ensure that the latest value is accessed every time for variables with simultaneous read and write operations, you must use other synchronization mechanisms to ensure that the CPU row cache is invalidated and synchronized, such as atomic or lock

Introduction

Rule:

  1. update pointer, rather than update pointer’s data
  2. free old object
    1. advance program language do this by GC, low program language must done by itself
  3. The thread that hold the old refrence will always use the old one, until it read new one
var rcuCfg *RcuCfg

type RcuCfg struct {
	cfg  *Cfg
	lock sync.Mutex //
}

func newCfgFromOld(old *Cfg) {
	cfg := &Cfg{}
	copy(cfg, old)
	return cfg
}

func updateCfg(cfg *Cfg) *Cfg {
	// doSomething
	return cfg
}

func rcuRead() *Cfg {
	return rcuCfg.cfg
}

func rcuUpdate() {
	rcuCfg.lock.Lock()
	defer rcuCfg.lock.Unlock()
	cfg := newCfgFromOld(rcuCfg.cfg)
	rcuCfg.cfg = updateCfg(cfg)
}

Reference

  1. https://www.kernel.org/doc/html/latest/RCU/whatisRCU.html
  2. https://doc.rust-lang.org/std/sync/atomic/struct.AtomicPtr.html
  3. https://pkg.go.dev/sync/atomic#Pointer
  4. https://en.wikipedia.org/wiki/Memory_ordering
  5. https://en.wikipedia.org/wiki/Memory_barrier
  6. https://go.dev/ref/mem
  7. https://en.wikipedia.org/wiki/MESI_protocol
  8. godbolt - online compiler explorer
  9. https://en.cppreference.com/w/cpp/atomic/memory_order
  10. https://en.cppreference.com/w/cpp/atomic/memory_order