PPP Exercises: Persistent Threads
Demo: Threads
Tycoon supports threads, i.e. concurrent activities. Try this script:
import runtimeCore thread fmt print int real;
let random = bind(:Fun() :Int runtimeCore.libraryName() "rtos_random" "=");
let f(n :Int) =
begin
let sleepingTime = int.abs(random()) % 20
print.string(fmt.int(n) <> ": sleeping for " <> fmt.int(sleepingTime) <> "\n")
thread.sleep(real.val(sleepingTime))
print.string(fmt.int(n) <> ": waking up\n")
end;
for i = 0 upto 10 do
thread.fork(fun(self :thread.T(Ok)) f(i))
end;
Demo: Semaphores
This is a small implementation of semaphores, extracted from a much more sophisticated implementation by Andreas Piellusch that will soon be delivered with Tycoon.
import thread queue;
Let Semaphore =
Tuple
T <:Ok
new(:Int) :T
wait(:T) :Ok
post(:T) :Ok
end;
let semaphore :Semaphore =
begin
Let T =
Tuple
var count :Int
waitqueue :queue.T(thread.T(Ok))
end
let new(count :Int) :T =
tuple
let var count = count
let waitqueue = queue.new(:thread.T(Ok))
end
let wait(sem :T) :Ok =
thread.atomic(fun()
begin
sem.count := sem.count - 1
if sem.count < 0 then
queue.push(sem.waitqueue thread.self())
thread.suspend(thread.self())
end
end)
let post(sem :T):Ok =
thread.atomic(fun()
begin
sem.count := sem.count + 1
if sem.count <= 0 then
thread.run(queue.pop(sem.waitqueue))
end
end)
let result :Semaphore =
tuple
Let T = T
let new = new
let wait = wait
let post = post
end
end;
Exercise: Producer/consumer with synchronized container
Now build a generic container 'synchronizedContainer' with a specific capacity that synchronizes via semaphores and fulfills this type:
Let SynchronizedContainer =
Tuple
T(E <:Ok) <:Ok
new(E <:Ok capacity :Int) :T(E)
put(E <:Ok :T(E) :E) :Ok
get(E <:Ok :T(E)) :E
end;
Test the container with this code:
import runtimeCore print fmt int real thread checkpoint;
let random = bind(:Fun() :Int runtimeCore.libraryName() "rtos_random" "=");
let myContainer = synchronizedContainer.new(:Int 4);
let producer(no :Int) :Ok =
for i = 1 upto 5 do
let sleepingTime = int.abs(random()) % 10
thread.sleep(real.val(sleepingTime))
let partNo = int.abs(random()) % 100
print.string("Producer no. " <> fmt.int(no) <>
" produced part no. " <> fmt.int(partNo) <> "\n")
synchronizedContainer.put(myContainer partNo)
end;
let consumer(no :Int) :Ok =
for i = 1 upto 5 do
let sleepingTime = int.abs(random()) % 10
thread.sleep(real.val(sleepingTime))
let partNo = synchronizedContainer.get(myContainer)
print.string("Consumer no. " <> fmt.int(no) <>
" consumed part no. " <> fmt.int(partNo) <> "\n")
end;
for i = 1 upto 5 do
thread.fork(fun(self :thread.T(Ok)) producer(i))
thread.fork(fun(self :thread.T(Ok)) consumer(i))
end;
Exercise: Persistent Timer Thread
Implement a thread that runs forever in parallel to the toplevel and reminds you of the actual time. The thread should be named 'timeThread', so that he can be killed by 'thread.kill'. He should depend on two mutable global variables 'showTime :Bool' (that tells the thread if he should stay silent) and 'sleepingTime :Real' that determines the interval between his messages:
let var showTime = true;
let var sleepingTime = 10.0;
let timeThread =
...
sleepingTime := 1.0;
do saveSystem;
do exit;
tycoon -restart
showTime := false;
thread.kill(timeThread);
showTime := true;
Gerald Schröder, 1996