Comment by tucnak

Comment by tucnak 9 days ago

6 replies

> Which means another thread can observe an interface value having a vtable from one object but data from another, and can then execute a method from one object on data from another object, potentially re-interpreting fields as values of other types.

If this were the case, then surely someone could construct a program with goroutines, loops and a handful of interface variables—that would predictably fail, right? I wouldn't know how to make one. Could you, or ChatGPT for that matter, make one for demo's sake?

kokada 9 days ago

I am also curious, I keep reading from this thread folks talking that this is possible, but I can't see to find anything searching in Google/DDG.

There is this document from Golang devs itself[1], that says:

> Reads of memory locations larger than a single machine word are encouraged but not required to meet the same semantics as word-sized memory locations, observing a single allowed write w. For performance reasons, implementations may instead treat larger operations as a set of individual machine-word-sized operations in an unspecified order. This means that races on multiword data structures can lead to inconsistent values not corresponding to a single write. When the values depend on the consistency of internal (pointer, length) or (pointer, type) pairs, as can be the case for interface values, maps, slices, and strings in most Go implementations, such races can in turn lead to arbitrary memory corruption.

Fair, this matches what everyone is saying in this thread. But I am still curious to see this in practice.

[1]: https://go.dev/ref/mem

Edit: I found this example from Dave Cheney: https://dave.cheney.net/2014/06/27/ice-cream-makers-and-data.... I am curious if I can replicate this in e.g.: Java.

Edit 2: I can definitely replicate the same bug in Scala, so it is not like Go is unique for the example in that blog post.

  • tsimionescu 9 days ago

    > Edit 2: I can definitely replicate the same bug in Scala, so it is not like Go is unique for the example in that blog post.

    Could you share some details on the program and the execution environment? Per my understanding of the Java memory model, a JVM should not experience this problem. Reads and writes to references (and to all 32 bit values) are explicitly guaranteed to be atomic, even if they are not declared volatile.

    • kokada 7 days ago

          import java.util.concurrent.Executors
          import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future}
      
          trait IceCreamMaker {
            def hello(): Unit
          }
      
          class Ben(name: String) extends IceCreamMaker {
            override def hello(): Unit = {
              println(s"Ben says, 'Hello my name is $name'")
            }
          }
          class Jerry(name: String) extends IceCreamMaker {
            override def hello(): Unit = {
              println(s"Jerry says, 'Hello my name is $name'")
            }
          }
      
          object Main {
            implicit val context: ExecutionContextExecutor = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2))
      
            def main(args: Array[String]): Unit = {
              val ben = new Ben("Ben")
              val jerry = new Ben("jerry")
              var maker: IceCreamMaker = ben
              def loop0: Future[Future[Future[Future[Any]]]] = {
                maker = ben
                Future { loop1 }
              }
              def loop1: Future[Future[Future[Any]]] = {
                maker = jerry
                Future { loop0 }
              }
              Future { loop0 }
              while (true) {
                maker.hello()
              }
            }
        }
      
      
      Here. I am not saying that JVM shouldn't have a stronger memory model, after thinking for a while I think the issue is the program itself. But feel free to try to understand.
tsimionescu 9 days ago

Sure, here is an example:

https://go.dev/play/p/_EJ4EvYntr2

When you run this you will see that occasionally it prints something other than 11 or 100. If it doesn't happen in one run, run it again a few times.

An equivalent Java program will never print anything else.