

package dev.tauri.choam

import org.openjdk.jcstress.annotations._
import org.openjdk.jcstress.annotations.Outcome.Outcomes
import org.openjdk.jcstress.annotations.Expect._
import org.openjdk.jcstress.infra.results.LLL_Result

import kcas.{ Ref, KCAS }

@KCASParams("Only one side of a `+` should be visible")
@Outcomes(Array(
  new Outcome(id = Array("(b,bar), (b,bar), (a,rab)"), expect = ACCEPTABLE, desc = "read first"),
  new Outcome(id = Array("(b,bar), (a,rab), (a,rab)"), expect = ACCEPTABLE, desc = "write first")
))
abstract class ChoiceTest(impl: KCAS) {

  private[this] val ref0 =
    Ref.mk("b")

  private[this] val ref1 =
    Ref.mk("foo")

  private[this] val ref2 =
    Ref.mk("bar")

  private[this] val choice: React[Unit, (String, String)] = {
    val mod1 = ref0.modify { s => (s(0) + 1).toChar.toString }
    val mod2 = ref0.modify { s => (s(0) - 1).toChar.toString }
    val ch1 = ref1.modifyWith {
      case "this will never match" => React.ret("x")
      case _ => React.retry
    }
    val ch2 = ref2.modify(_.reverse)
    (mod1 * ch1) + (mod2 * ch2)
  }

  private[this] val read: React[Unit, (String, String)] =
    React.consistentRead(ref0, ref2)

  @Actor
  def write(r: LLL_Result): Unit = {
    r.r1 = choice.unsafeRun
  }

  @Actor
  def read(r: LLL_Result): Unit = {
    r.r2 = read.unsafeRun
  }

  @Arbiter
  def arbiter(r: LLL_Result): Unit = {
    require(ref1.getter.unsafeRun eq "foo")
    r.r3 = read.unsafeRun
  }
}
