Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplatform Gears (1) #20

Merged
merged 65 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
d66a32e
Example I/O integration on top
belamenso Mar 17, 2023
cfb2aa5
Posix like IO (only File and SocketUDP rewritten so far)
belamenso Apr 23, 2023
7fb831a
Old channel codet
belamenso Jun 21, 2023
a889d90
SyncChannel working
belamenso Jun 23, 2023
d17338c
Working version of channels and tests
belamenso Jun 27, 2023
a053613
Move summary
belamenso Jun 27, 2023
e440454
Remove old and inaccurate comments in measureTimes.scala
belamenso Jun 27, 2023
559289a
Add pandoc PDF, make tables narrower and fix pandoc list rendering is…
belamenso Jun 27, 2023
2d92644
dump versions
m8nmueller Sep 30, 2023
6059db6
some cleanups
m8nmueller Sep 30, 2023
6f39d41
Fix completion group linking
m8nmueller Sep 30, 2023
10746ca
Introduce AsyncFoundations and VThread implementation (channels missing)
m8nmueller Oct 2, 2023
39fdc3e
fix timing of onComplete in SourceBehavior
m8nmueller Oct 2, 2023
71125c1
Unify package, private methods, split cancel/awaitCompletion
m8nmueller Oct 5, 2023
0fc8f56
remove object monitors from VThreadFoundations
m8nmueller Oct 5, 2023
3cfe65e
wrap future body in group by default
m8nmueller Oct 5, 2023
1ea43b2
Migrate AsyncFoundations to AsyncSupport with dependent types
m8nmueller Oct 9, 2023
08d545d
Introduce persistent cancelled state to CompletionGroup
m8nmueller Oct 10, 2023
5c1fb17
Implement await/sleep cancellation ontop of CompletionGroups
m8nmueller Oct 11, 2023
bda1dcc
Replace SchedulerSupport with Scheduler
m8nmueller Oct 12, 2023
df687dc
reintroduce separation between SuspendSupport and AsyncSupport
m8nmueller Oct 17, 2023
c523ac8
Introduce ListenerLock (WIP)
m8nmueller Oct 23, 2023
b96dddb
skip duplicate object passing
m8nmueller Oct 23, 2023
54fb764
add LockContext
m8nmueller Oct 23, 2023
1c58d72
Use `FiniteDuration` in schedule
natsukagami Oct 24, 2023
b564039
implement multiLock (untested)
m8nmueller Oct 23, 2023
ad57f79
fix groups for Async.blocking, uninterruptible, and future completion
m8nmueller Oct 26, 2023
72c29f4
Remove filter, replace lockMulti by strongly typed lockBoth
m8nmueller Nov 3, 2023
0f65eea
fix new listener inconsistencies
m8nmueller Nov 4, 2023
ab17a40
Update poll interface to return based on only Source's state
m8nmueller Nov 5, 2023
337d5fb
measureTimes fix to make measureRaceOverheadVsJava run
m8nmueller Nov 5, 2023
f30b43d
Fix Listener#lockBoth IllegalMonitorStateException
m8nmueller Nov 5, 2023
baf3c89
OriginalSource doc
m8nmueller Nov 8, 2023
25bb932
introduce Listener#tapLock/mapLock
m8nmueller Nov 9, 2023
8e8578c
implement an alternative listener api with explicit continuation
m8nmueller Nov 6, 2023
f5eb0d7
implement no-allocating listeners (with release boundary)
m8nmueller Nov 11, 2023
5113e28
release with tailrec
m8nmueller Nov 11, 2023
104c136
early WIP: toplock implementation
m8nmueller Nov 16, 2023
5ae5d92
WIP: more stuff to toplock implementation
m8nmueller Nov 16, 2023
bcfbc20
Adapt other stuff to the new listener API
natsukagami Nov 16, 2023
9a1383a
Implement lockBoth
natsukagami Nov 17, 2023
7948549
Make Gone and Locked case objects
natsukagami Nov 17, 2023
9f235cf
Clean up when locking fails
natsukagami Nov 17, 2023
bc683da
Update tests so they work
natsukagami Nov 17, 2023
bb9de16
Some renaming and add some additional documentation
natsukagami Nov 20, 2023
8e0d708
race release should compare heldLock to marker
natsukagami Nov 20, 2023
e55a1fa
Revamp documentation overall
natsukagami Nov 20, 2023
c8e0851
Small tweaks to `race` implementation
natsukagami Nov 20, 2023
00bd343
Tiny micro-optimization on race listener
natsukagami Nov 21, 2023
a38f42e
Drop the race listener upon failing of the inner listener lock
natsukagami Nov 27, 2023
e3e0f46
Drop the listener wrapping behavior for race's poll
natsukagami Nov 27, 2023
8cdb509
Move `release` into the API of `listenerLock`
natsukagami Nov 27, 2023
4b1d6ba
Fix correct lock ordering
natsukagami Nov 27, 2023
a2141f6
Add helper function releaseLock
natsukagami Nov 27, 2023
5c15f23
Reorder and improve documentation of Listener
natsukagami Nov 30, 2023
3edd0c5
Clarify the implementation of `race` to conform with Listener logic
natsukagami Nov 30, 2023
a58d778
Implement crashing on conflicting locks in lockBoth
natsukagami Nov 30, 2023
3d5854a
Improve doc comments
natsukagami Dec 1, 2023
93c1778
Apply suggestions from code review
natsukagami Dec 1, 2023
05136dc
Update comment
natsukagami Dec 1, 2023
b76e66b
Add conflicting case of same top-lock
natsukagami Dec 1, 2023
7c79d29
Make acceptingListener callback take source as well
natsukagami Dec 1, 2023
2047e09
Avoid channel multiplexer tests
natsukagami Dec 1, 2023
5eab8c1
Merge pull request #3 from natsukagami/explicit-cont-listener
m8nmueller Dec 1, 2023
a688902
tests, workflow, directory layout
m8nmueller Dec 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ jobs:
- uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 20
java-version: 21
- name: Test
run: sbt test
1 change: 0 additions & 1 deletion .jvmopts
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
--enable-preview
1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ lazy val root = project
name := "Gears",
organization := "ch.epfl.lamp",
version := "0.1.0-SNAPSHOT",
javaOptions += "--enable-preview --version 19",
libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test
)
Binary file added docs/ch1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/ch2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/ch3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/ch4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/ch5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
519 changes: 519 additions & 0 deletions docs/summary-2023-06.md

Large diffs are not rendered by default.

Binary file added docs/summary-2023-06.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.8.2
sbt.version=1.9.6
5 changes: 0 additions & 5 deletions src/main/scala/Main.scala

This file was deleted.

140 changes: 140 additions & 0 deletions src/main/scala/PosixLikeIO/PIO.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package PosixLikeIO

import gears.async.{Async, Future, given}
import Future.Promise

import java.net.{DatagramPacket, DatagramSocket, InetAddress, InetSocketAddress, ServerSocket, Socket}
import java.nio.ByteBuffer
import java.nio.channels.{AsynchronousFileChannel, CompletionHandler, SocketChannel}
import java.nio.charset.{Charset, StandardCharsets}
import java.nio.file.{Path, StandardOpenOption}
import scala.Tuple.Union
import scala.concurrent.ExecutionContext
import scala.util.{Failure, Success, Try}


class File(val path: String) {
private var channel: Option[AsynchronousFileChannel] = None

def isOpened: Boolean = channel.isDefined && channel.get.isOpen

def open(options: StandardOpenOption*): File =
assert(channel.isEmpty)
val options1 = if (options.isEmpty) Seq(StandardOpenOption.READ) else options
channel = Some(
AsynchronousFileChannel.open(Path.of(path), options1*))
this

def close(): Unit =
if (channel.isDefined)
channel.get.close()
channel = None

def read(buffer: ByteBuffer): Future[Int] =
assert(channel.isDefined)

val p = Promise[Int]()
channel.get.read(buffer, 0, buffer, new CompletionHandler[Integer, ByteBuffer] {
override def completed(result: Integer, attachment: ByteBuffer): Unit = p.complete(Success(result))
override def failed(e: Throwable, attachment: ByteBuffer): Unit = p.complete(Failure(e))
})
p.future

def readString(size: Int, charset: Charset = StandardCharsets.UTF_8): Future[String] =
assert(channel.isDefined)
assert(size >= 0)

val buffer = ByteBuffer.allocate(size)
val p = Promise[String]()
channel.get.read(buffer, 0, buffer, new CompletionHandler[Integer, ByteBuffer] {
override def completed(result: Integer, attachment: ByteBuffer): Unit =
p.complete(Success(charset.decode(attachment.slice(0, result)).toString()))
override def failed(e: Throwable, attachment: ByteBuffer): Unit = p.complete(Failure(e))
})
p.future

def write(buffer: ByteBuffer): Future[Int] =
assert(channel.isDefined)

val p = Promise[Int]()
channel.get.write(buffer, 0, buffer, new CompletionHandler[Integer, ByteBuffer] {
override def completed(result: Integer, attachment: ByteBuffer): Unit = p.complete(Success(result))
override def failed(e: Throwable, attachment: ByteBuffer): Unit = p.complete(Failure(e))
})
p.future


def writeString(s: String, charset: Charset = StandardCharsets.UTF_8): Future[Int] =
write(ByteBuffer.wrap(s.getBytes(charset)))

override def finalize(): Unit = {
super.finalize()
if (channel.isDefined)
channel.get.close()
}
}

class SocketUDP() {
private var socket: Option[DatagramSocket] = None

def isOpened: Boolean = socket.isDefined && !socket.get.isClosed

def bindAndOpen(port: Int): SocketUDP =
assert(socket.isEmpty)
socket = Some(DatagramSocket(port))
this

def open(): SocketUDP =
assert(socket.isEmpty)
socket = Some(DatagramSocket())
this

def close(): Unit =
if (socket.isDefined)
socket.get.close()
socket = None

def send(data: ByteBuffer, address: String, port: Int): Future[Unit] =
assert(socket.isDefined)

Async.blocking:
Future:
val packet: DatagramPacket = new DatagramPacket(data.array(), data.limit(), InetAddress.getByName(address), port)
socket.get.send(packet)

def receive(): Future[DatagramPacket] =
assert(socket.isDefined)

Async.blocking:
Future[DatagramPacket]:
val buffer = Array.fill[Byte](10 * 1024)(0)
val packet: DatagramPacket = DatagramPacket(buffer, 10 * 1024)
socket.get.receive(packet)
packet

override def finalize(): Unit = {
super.finalize()
if (socket.isDefined)
socket.get.close()
}
}

object PIOHelper {
def withFile[T](path: String, options: StandardOpenOption*)(f: File => T): T =
val file = File(path).open(options*)
val ret = f(file)
file.close()
ret

def withSocketUDP[T]()(f: SocketUDP => T): T =
val s = SocketUDP().open()
val ret = f(s)
s.close()
ret

def withSocketUDP[T](port: Int)(f: SocketUDP => T): T =
val s = SocketUDP().bindAndOpen(port)
val ret = f(s)
s.close()
ret
}
35 changes: 35 additions & 0 deletions src/main/scala/PosixLikeIO/examples/clientAndServerUDP.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package PosixLikeIO.examples

import gears.async.{Async, Future, given}
import gears.async.AsyncOperations.*
import PosixLikeIO.{PIOHelper, SocketUDP}

import java.net.DatagramPacket
import java.nio.ByteBuffer
import java.nio.file.StandardOpenOption
import scala.concurrent.ExecutionContext


@main def clientAndServerUDP(): Unit =
given ExecutionContext = ExecutionContext.global
Async.blocking:
val server = Future:
PIOHelper.withSocketUDP(8134): serverSocket =>
val got: DatagramPacket = serverSocket.receive().result.get
val messageReceived = String(got.getData.slice(0, got.getLength), "UTF-8")
val responseMessage = (messageReceived.toInt + 1).toString.getBytes
serverSocket.send(ByteBuffer.wrap(responseMessage), got.getAddress.toString.substring(1), got.getPort)
sleep(50)

def client(value: Int): Future[Unit] =
Future:
PIOHelper.withSocketUDP(): clientSocket =>
val data: Array[Byte] = value.toString.getBytes
clientSocket.send(ByteBuffer.wrap(data), "localhost", 8134).result.get
val responseDatagram = clientSocket.receive().result.get
val messageReceived = String(responseDatagram.getData.slice(0, responseDatagram.getLength), "UTF-8").toInt
println("Sent " + value.toString + " and got " + messageReceived.toString + " in return.")


Async.await(client(100))
Async.await(server)
19 changes: 19 additions & 0 deletions src/main/scala/PosixLikeIO/examples/readAndWriteFile.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package PosixLikeIO.examples

import gears.async.{Async, given}
import PosixLikeIO.PIOHelper

import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import java.nio.file.StandardOpenOption
import scala.concurrent.ExecutionContext


@main def readAndWriteFile(): Unit =
given ExecutionContext = ExecutionContext.global
Async.blocking:
PIOHelper.withFile("/home/julian/Desktop/x.txt", StandardOpenOption.READ, StandardOpenOption.WRITE): f =>
Async.await(f.writeString("Hello world! (1)"))
println(Async.await(f.readString(1024)).get)
Async.await(f.writeString("Hello world! (2)"))
println(Async.await(f.readString(1024)).get)
25 changes: 25 additions & 0 deletions src/main/scala/PosixLikeIO/examples/readWholeFile.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package PosixLikeIO.examples

import gears.async.{Async, given}
import PosixLikeIO.PIOHelper

import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import java.nio.file.StandardOpenOption
import scala.concurrent.ExecutionContext


@main def readWholeFile(): Unit =
given ExecutionContext = ExecutionContext.global
Async.blocking:
PIOHelper.withFile("/home/julian/Desktop/x.txt", StandardOpenOption.READ): f =>
val b = ByteBuffer.allocate(1024)
val retCode = f.read(b).result.get
assert(retCode >= 0)
val s = StandardCharsets.UTF_8.decode(b.slice(0, retCode)).toString()
println("Read size with read(): " + retCode.toString())
println("Data: " + s)


println("Read with readString():")
println(Async.await(f.readString(1000)).get)
Loading