HN user

alecrn

165 karma
Posts2
Comments14
View on HN

My understanding is there are only scheduling guarantees around synchronization points between goroutines through chans or mutexes and stuff.

Go's concurrency model is inspired by Hoare's communicating sequential processes which kinda has the same idea: http://usingcsp.com/cspbook.pdf

For instance in this program:

  func main() {
  	point1 := make(chan bool)
  	point2 := make(chan bool)
  
  	go func() {
  		point1 <- true
  		fmt.Println("hello")
  		point2 <- true
  	}()
  
  	<-point1
  	time.Sleep(3 * time.Second)
  	<-point2
  }
An unbuffered channel read is always matched with a corresponding write. Let's call the point at which `point1 <- true` occurs and `<-point1` occurs T1 and the point at which `point2 <- true` occurs and `<-point2` occurs T2. fmt.Println("hello") and time.Sleep(3*time.Second) are both guaranteed to occur between T1 and T2. If we didn't have T2 there's no guarantee fmt.Println("hello") would run before the program exits.

Maybe I'm wrong but this is my understanding of Hoare's and go's concurrency model.

But I don't think in go atomic ops are considered synchronization in the sense that they force two goroutines to synchronize at a particular point like a chan. I.e. a chan send in one goroutine must be matched with a chan receive in another (unless they're buffered). If you have an atomic operation between two synchronization points I'd expect the only guarantee is that it occurs between the two points, and when it does it happens atomically.

Yeah, this particular instance seems ok to me. This one makes the example feel weirder:

  func main() {
  	runtime.GOMAXPROCS(runtime.NumCPU())
  	fmt.Println(runtime.NumCPU(), runtime.GOMAXPROCS(0))
  	started := make(chan bool)
  
  	go func() {
  		started <- true
  		for {
  			atomic.AddUint64(&a, uint64(1))
  		}
  	}()
  
  	<-started
  	for {
  		fmt.Println(atomic.LoadUint64(&a))
  		time.Sleep(time.Second)
  	}
  }
Here we explicitly wait until the goroutine is started, so we know it's scheduled by the time our other loop runs. Here on my computer with go1.8 linux/amd64 it still optimizes out the while loop, which makes sense as nothing changed that would convince the optimizer the loop should remain given the compiler's current logic.

If you add time.Sleep(time.Millisecond) to the goroutine loop or any other synchronization it works fine. I'm having trouble thinking of a real world example where you'd want an atomic operation going ham in a loop without any sort of time or synchronization. At the very least a channel indicating when the loop is done would cause the loop to compile.

The Tau Manifesto 11 years ago

I always liked this as well, and in this case, tau is basically that unit. In fact, maybe we could think of tau as being short for "turn".

One full rotation = tau

Half a rotation = 1/2 tau

1/10 a rotation = 1/10 tau

That's very interesting! The alternatives to TCP that can be written in UDP which are optimized for a certain application are especially interesting to me. Working at the UDP level can present a lot of interesting performance options, kinda like working in C instead of Python.

Something I've been curious about is if there are proxies that can convert protocols. So for instance, on your local machine, you could have a proxy that turns TCP connections on port 8000 into FASP connections to another machine. This would let you use an ordinary web browser over FASP.

You could even pipe the proxies, e.g., TCP -> FASP -> MinimaLT [1]. That way any program could have really fast data transfer over an encrypted tunnel.

[1] http://cr.yp.to/tcpip/minimalt-20130522.pdf

That's the reason I wrote this for myself, and why I made https://filegrave.com, as I thought it could be useful to others as well.

Git is already a great tool with a workflow for handling incremental updates that I happen to be used to working with, so there was no need to reinvent the wheel.