From 26f5bf3c3e727dd7fd51d7bd0f3d1dde0f8dd04e Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Wed, 17 May 2023 15:29:52 +0200 Subject: [PATCH] Init --- .bsp/sbt.json | 1 + .gitignore | 3 + .scalafmt.conf | 2 + build.sbt | 22 +++++ project/build.properties | 1 + project/metals.sbt | 6 ++ project/plugins.sbt | 1 + project/project/metals.sbt | 6 ++ project/project/project/metals.sbt | 6 ++ src/main/scala/Gen.scala | 68 +++++++++++++ src/main/scala/Main.scala | 52 ++++++++++ .../concurrent/simple/simple-futures.scala | 95 +++++++++++++++++++ 12 files changed, 263 insertions(+) create mode 100644 .bsp/sbt.json create mode 100644 .gitignore create mode 100644 .scalafmt.conf create mode 100644 build.sbt create mode 100644 project/build.properties create mode 100644 project/metals.sbt create mode 100644 project/plugins.sbt create mode 100644 project/project/metals.sbt create mode 100644 project/project/project/metals.sbt create mode 100644 src/main/scala/Gen.scala create mode 100644 src/main/scala/Main.scala create mode 100644 src/main/scala/concurrent/simple/simple-futures.scala diff --git a/.bsp/sbt.json b/.bsp/sbt.json new file mode 100644 index 0000000..15e163b --- /dev/null +++ b/.bsp/sbt.json @@ -0,0 +1 @@ +{"name":"sbt","version":"1.8.2","bspVersion":"2.1.0-M1","languages":["scala"],"argv":["/nix/store/grp5dj6bvnfcrd002mivmp36jldjzhph-openjdk-19.0.2+7/lib/openjdk/bin/java","-Xms100m","-Xmx100m","-classpath","/nix/store/g5hx4a8pqfhyn61iiillw0xn4y2a5zl4-sbt-1.8.2/share/sbt/bin/sbt-launch.jar","-Dsbt.script=/nix/store/g5hx4a8pqfhyn61iiillw0xn4y2a5zl4-sbt-1.8.2/bin/sbt","xsbt.boot.Boot","-bsp"]} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ccf853 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.metals +target +.bloop diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..c662861 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,2 @@ +version = "3.6.1" +runner.dialect = scala3 diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..2438554 --- /dev/null +++ b/build.sbt @@ -0,0 +1,22 @@ +scalaVersion := "3.1.3" + +enablePlugins(ScalaNativePlugin) + +// set to Debug for compilation details (Info is default) +logLevel := Level.Info + +// import to add Scala Native options +import scala.scalanative.build._ + +// defaults set with common options shown +nativeConfig ~= { c => + c.withLTO(LTO.none) // thin + .withMode(Mode.releaseFast) // releaseFast + .withGC(GC.immix) // commix + .withMultithreadingSupport(true) + .withCompileOptions(c.compileOptions ++ Seq("-g")) +} + +scalacOptions ++= Seq( + "-explain" +) diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..f344c14 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version = 1.8.2 diff --git a/project/metals.sbt b/project/metals.sbt new file mode 100644 index 0000000..05fd2b3 --- /dev/null +++ b/project/metals.sbt @@ -0,0 +1,6 @@ +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.6") + diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..bcfde7e --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.0-SNAPSHOT") diff --git a/project/project/metals.sbt b/project/project/metals.sbt new file mode 100644 index 0000000..05fd2b3 --- /dev/null +++ b/project/project/metals.sbt @@ -0,0 +1,6 @@ +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.6") + diff --git a/project/project/project/metals.sbt b/project/project/project/metals.sbt new file mode 100644 index 0000000..05fd2b3 --- /dev/null +++ b/project/project/project/metals.sbt @@ -0,0 +1,6 @@ +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.6") + diff --git a/src/main/scala/Gen.scala b/src/main/scala/Gen.scala new file mode 100644 index 0000000..b4462b8 --- /dev/null +++ b/src/main/scala/Gen.scala @@ -0,0 +1,68 @@ +package gen + +import scala.annotation.tailrec + +object Gen: + import scala.scalanative.runtime.Continuations.* + enum Seqn[+T] { + case Empty + case Next(t: T, nx: () => Seqn[T]) + } + + type CanGen[-T] = BoundaryLabel[Seqn[T]] + + def apply[T](f: CanGen[T] ?=> Unit): Iterator[T] = + new Iterator[T] { + var buf = boundary { f; Seqn.Empty } + + def hasNext: Boolean = buf != Seqn.Empty + + def next() = buf match + case Seqn.Empty => ??? + case Seqn.Next(t, nx) => + buf = nx() + t + } + + def put[T](value: T)(using CanGen[T]): Unit = + suspend[Seqn[T]](f => Seqn.Next(value, f)) +end Gen + +import Gen.* + +def allRightTriangles = Gen { + var c = 1 + var a = 1 + var b = 1 + + def nextState() = + if (a * a + b * b > c * c) then + a += 1 + b = a + if (a >= c) then + c += 1 + a = 1 + b = 1 + else b += 1 + + def toOk() = + while a * a + b * b != c * c do nextState() + + while (true) do + toOk() + put((a, b, c)) + nextState() +} + +def fibonacci = Gen { + @tailrec def go(a: Int, b: Int): Unit = + put(a) + go(b, (a + b)) + go(1, 1) +} + +def rightTriangles(upTo: Int) = Gen { + allRightTriangles + .takeWhile((_, _, c) => c <= upTo) + .foreach(put) +} diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala new file mode 100644 index 0000000..f03802e --- /dev/null +++ b/src/main/scala/Main.scala @@ -0,0 +1,52 @@ +import gen.rightTriangles +import concurrent.simple.* +import gen.fibonacci + +object Test { + def main(args: Array[String]): Unit = + testGen() + testGenCompare() + // testSimpleFutures() + + def testGen(): Unit = + // println(rightTriangles(1500).toList) + println(fibonacci.take(1000).toList) + + def testGenCompare(): Unit = + val iter = new Iterator[(Int, Int, Int)] { + var c = 1 + var a = 1 + var b = 1 + + def nextState() = + if (a * a + b * b > c * c) then + a += 1 + b = a + if (a >= c) then + c += 1 + a = 1 + b = 1 + else b += 1 + + def toOk() = + while a * a + b * b != c * c do nextState() + + def hasNext = + toOk() + c <= 1500 + + def next() = + toOk() + val res = (a, b, c) + nextState() + res + } + println(iter.toList) + + def testSimpleFutures(): Unit = + val futures = (1 to 10000) + .map(i => Future { i.toLong }) + val futSq = futures.map(f => Future { f.await * f.await }) + val sum = Future { futSq.map(_.await).sum } + println(Scheduler.blocking { sum.await }) +} diff --git a/src/main/scala/concurrent/simple/simple-futures.scala b/src/main/scala/concurrent/simple/simple-futures.scala new file mode 100644 index 0000000..0bc2256 --- /dev/null +++ b/src/main/scala/concurrent/simple/simple-futures.scala @@ -0,0 +1,95 @@ +package concurrent.simple + +import scala.collection.mutable.ListBuffer +import scala.scalanative.runtime.Continuations.{boundary, suspend} +import scala.scalanative.unsafe.Tag +import scala.collection.mutable.ArrayDeque +import scala.annotation.tailrec + +/* A single threaded scheduler */ +object Scheduler: + private val taskQueue = ArrayDeque[Runnable]() + + def schedule(task: Runnable): Unit = + synchronized { + taskQueue.addOne(task) + notify() + } + + def nextTask(): Option[Runnable] = synchronized { + if taskQueue.isEmpty then None + else Some(taskQueue.removeLast()) + } + + def blocking[T](f: Async ?=> T): T = + var result: Option[T] = None + schedule(() => { + Future.async { + val v = f; + synchronized { result = Some(v); notifyAll() } + } + }) + + // spawn a bunch of threads + for (i <- 2 to 8) do + Thread + .ofPlatform() + .start(() => + println(s"starting thread $i") + next(synchronized { result.isDefined }) + ) + + next(synchronized { result.isDefined }) + result.get + + @tailrec private def next(condition: => Boolean): Unit = + if condition then () + else + nextTask() match + case None => synchronized { if !condition then wait() } + case Some(value) => value.run() + next(condition) + +trait Async: + def await[T](f: Future[T]): T + +class Future[+T](body: Async ?=> T): + private var result: Option[T] = None + private var waiting: ListBuffer[T => Unit] = ListBuffer() + private def addWaiting(k: T => Unit): Unit = waiting += k + + def await(using a: Async): T = a.await(this) + + private def complete(): Unit = + Future.async { + val value = body + result = Some(value) + for k <- waiting do Scheduler.schedule(() => k(value)) + waiting.clear() + } + + Scheduler.schedule(() => complete()) + +object Future: +// a handler for Async + def async(body: Async ?=> Unit): Unit = + boundary[Unit] { + given Async with { + def await[T](f: Future[T]): T = + try { + f.result match + case Some(x) => x + case None => suspend[T, Unit](s => f.addWaiting(s)) + } catch + case (e: Exception) => { + println(e) + throw e + } + } + body + } +end Future + +// def Test(x: Future[Int], xs: List[Future[Int]]) = +// Future: +// x.await + xs.map(_.await).sum