Skip to content

Commit

Permalink
deduping code in ch04
Browse files Browse the repository at this point in the history
- will refactor common code into separate file / object later.
  • Loading branch information
spamegg1 committed Apr 5, 2024
1 parent e688756 commit 173b074
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 105 deletions.
12 changes: 8 additions & 4 deletions src/main/scala/ch04/01nativeFork.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@ def nativeFork(args: String*): Unit =

def doFork(task: Function0[Int]): Int =
val pid = util.fork()
if pid > 0 then pid
if pid > 0 then
println(s"forked, got pid ${pid}")
pid
else
println(s"in proc ${unistd.getpid()}")
val res = task.apply()
stdlib.exit(res)
res

def await(pid: Int): Int =
val status = stackalloc[Int]()
val status = stackalloc[Int](1)
util.waitpid(pid, status, 0)
val statusCode = !status
if statusCode != 0 then throw Exception(s"Child process returned error $statusCode")
Expand All @@ -46,6 +49,7 @@ def doAndAwait(task: Function0[Int]): Int =
def runCommand(args: Seq[String], env: Map[String, String] = Map.empty): Int =
if args.size == 0 then throw Exception("bad arguments of length 0")
Zone: // implicit z => // 0.5
println(s"proc ${unistd.getpid()}: running command ${args.head} with args ${args}")
val fname = toCString(args.head)
val argArray = makeStringArray(args) // take Scala strings, lower them to C level
val envStrings = env.map((k, v) => s"$k=$v") // convert env pairs to execve format
Expand All @@ -62,6 +66,7 @@ def runCommand(args: Seq[String], env: Map[String, String] = Map.empty): Int =
// Take Scala strings (args), turn them into a CString array, to be fed into execve.
def makeStringArray(args: Seq[String]): Ptr[CString] =
val count = args.size
val pid = unistd.getpid()
val size = sizeof[Ptr[CString]] * count.toUSize + 1.toUSize // 1 extra for null
val destArray = stdlib.malloc(size).asInstanceOf[Ptr[CString]]

Expand Down Expand Up @@ -92,7 +97,7 @@ def awaitAny(pids: Set[Int]): Set[Int] =
val finished = util.waitpid(-1, status, 0)

if running.contains(finished) then
val statusCode = !status
val statusCode = !status // TODO: checkStatus(status)
if statusCode != 0 then throw Exception(s"Child process returned error $statusCode")
else pids - finished
else throw Exception(s"error: reaped process ${finished}, expected one of $pids")
Expand All @@ -102,7 +107,6 @@ def awaitAll(pids: Set[Int]): Unit =
while running.nonEmpty do
println(s"waiting for $running")
running = awaitAny(running)

println("Done!")

// TODO
Expand Down
105 changes: 4 additions & 101 deletions src/main/scala/ch04/03nativePipeTwo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,102 +4,16 @@ import scalanative.unsigned.UnsignedRichInt
import scalanative.unsafe.*
import scalanative.libc.*
import scalanative.posix.unistd
import util.*

case class Command(path: String, args: String, env: Map[String, String])
import ch04.nativeFork.{Command, doFork, await, makeStringArray, runCommand, doAndAwait}
import ch04.nativeFork.{util, runOneAtATime, awaitAny, awaitAll, runSimultaneously}

@main
def nativePipeTwo(args: String*): Unit =
println("about to fork")
val status = runTwoAndPipe(0, 1, Seq("/bin/ls", "."), Seq("/usr/bin/sort", "-r"))
println(s"wait status ${status}")

def doFork(task: Function0[Int]): Int =
val pid = fork()
if pid > 0 then
println(s"forked, got pid ${pid}")
pid
else
println(s"in proc ${unistd.getpid()}")
val res = task.apply()
stdlib.exit(res)
res

def await(pid: Int): Int =
val status = stackalloc[Int]()
waitpid(pid, status, 0)
val statusCode = !status
if statusCode != 0 then throw new Exception(s"Child process returned error $statusCode")
!status

def doAndAwait(task: Function0[Int]): Int =
val pid = doFork(task)
await(pid)

def runCommand(args: Seq[String], env: Map[String, String] = Map.empty): Int =
if args.size == 0 then throw Exception("bad arguments of length 0")

Zone { // implicit z => // 0.5
println(s"proc ${unistd.getpid()}: running command ${args.head} with args ${args}")
val fname = toCString(args.head)
val argArray = stringSeqToStringArray(args)
val envStrings = env.map((k, v) => s"$k=$v")
val envArray = stringSeqToStringArray(envStrings.toSeq)

val r = execve(fname, argArray, envArray)
if r != 0 then
val err = errno.errno
stdio.printf(c"error: %d %d\n", err, string.strerror(err))
throw Exception(s"bad execve: returned $r")
}
0 // This will never be reached.

def stringSeqToStringArray(args: Seq[String]): Ptr[CString] =
val pid = unistd.getpid()
val destArray = stdlib
.malloc(sizeof[Ptr[CString]] * args.size.toUSize) // 0.5
.asInstanceOf[Ptr[CString]]
val count = args.size
Zone { // implicit z => // 0.5
for (arg, i) <- args.zipWithIndex
do
val stringPtr = toCString(arg)
val stringLen = string.strlen(stringPtr)
val destStr = stdlib.malloc(stringLen) // 0.5
string.strncpy(destStr, stringPtr, arg.size.toUSize) // 0.5
destStr(stringLen) = 0.toByte // 0.5
destArray(i) = destStr
}
// destArray(count) = null
// for j <- 0 to count do {}
destArray

def runOneAtATime(commands: Seq[Seq[String]]) =
for (command <- commands) do doAndAwait(() => runCommand(command))

def runSimultaneously(commands: Seq[Seq[String]]) =
val pids = for command <- commands yield doFork(() => runCommand(command))
for pid <- pids do await(pid)

def awaitAny(pids: Set[Int]): Set[Int] =
val status = stackalloc[Int](sizeof[Int])
var running = pids
!status = 0

val finished = waitpid(-1, status, 0)
if running.contains(finished) then
val statusCode = !status // TODO: check_status(status)
if statusCode != 0 then throw Exception(s"Child process returned error $statusCode")
else pids - finished
else throw Exception(s"error: reaped process ${finished}, expected one of $pids")

def awaitAll(pids: Set[Int]): Unit =
var running = pids
while running.nonEmpty do
println(s"waiting for $running")
running = awaitAny(running)
println("Done!")

// def badThrottle(commands: Seq[Seq[String]], maxParallel: Int) = ???
// def goodThrottle(commands: Seq[Seq[String]], maxParallel: Int) = ???

Expand Down Expand Up @@ -136,20 +50,9 @@ def runTwoAndPipe(input: Int, output: Int, proc1: Seq[String], proc2: Seq[String
val waitingFor = Seq(proc1Pid, proc2Pid)
println(s"waiting for procs: ${waitingFor}")

val r1 = waitpid(-1, null, 0)
val r1 = util.waitpid(-1, null, 0)
println(s"proc $r1 returned")

val r2 = waitpid(-1, null, 0)
val r2 = util.waitpid(-1, null, 0)
println(s"proc $r2 returned")
r2

@extern
object util:
def execve(filename: CString, args: Ptr[CString], env: Ptr[CString]): Int = extern
def execvp(path: CString, args: Ptr[CString]): Int = extern
def fork(): Int = extern
def getpid(): Int = extern
def waitpid(pid: Int, status: Ptr[Int], options: Int): Int = extern
def strerror(errno: Int): CString = extern
def pipe(pipes: Ptr[Int]): Int = extern
def dup2(oldfd: Int, newfd: Int): Int = extern

0 comments on commit 173b074

Please sign in to comment.