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

Added semaphore support for Linux platform #83

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 18 additions & 11 deletions SparrowImplicitLib/par/config.spr
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
module par.config

import std.compilerInfo
import std.ptr

datatype NativeThreadHandle = Byte Ptr // Opaque type
[ct] if platformName == "Linux"
datatype NativeThreadHandle = ULong
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you also need to add the >> operator for the new type (if I remember correctly it will not be autogenerated, so you can't print a NativeThreadHandle)

else
datatype NativeThreadHandle = Byte Ptr // Opaque type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor, not introduced in this patch]
Change to UntypedPtr


fun >>(h: NativeThreadHandle, os: @OutStream)
if ( h.impl.isSet )
os << mkStreamRefWrapper(h.impl.get)
else
os << "null"

using InvalidThreadHandle = NativeThreadHandle()
fun >>(h: NativeThreadHandle, os: @OutStream)
if ( h.impl.isSet )
os << mkStreamRefWrapper(h.impl.get)
else
os << "null"

using InvalidThreadHandle = NativeThreadHandle()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't define InvalidThreadHandle for Linux


//! Get the number of available logical CPU cores for our process
//! This dictates how much parallelism we have to be exploit
fun getAvailableCoresNum(): UInt
var maxProcs: Long = _Impl.sysconf(_Impl._SC_NPROCESSORS_ONLN)
return ife(maxProcs<1, UInt(1), UInt(maxProcs))

return ife(maxProcs < 1, UInt(1), UInt(maxProcs))
// TODO: also consider process affinity

package _Impl
using _SC_NPROCESSORS_ONLN = 58

[native("sysconf")] fun sysconf(name: Int): Long
[ct] if platformName == "Darwin"
using _SC_NPROCESSORS_ONLN = 58
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also test for Linux on the else branch. Just to be sure

using _SC_NPROCESSORS_ONLN = 84

[native("sysconf")] fun sysconf(name: Int): Long
65 changes: 47 additions & 18 deletions SparrowImplicitLib/par/semaphore.spr
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
module par.semaphore

import std.compilerInfo
import config

datatype TimeSpecT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be public to this file

tv_sec: Int
tv_nsec: Long

//! Allows a limited numbers of threads to simultaneously "acquire" a resource
//! before releasing it.
//! Compared to the mutex, we can release the semaphore without acquiring it
Expand All @@ -10,35 +15,59 @@ datatype Semaphore

fun ctor(this: @Semaphore, startValue: UInt = 0)
_handle ctor
var res = _Impl.semaphore_create(_Impl.mach_task_self(), _handle, _Impl.SYNC_POLICY_FIFO, Int(startValue))
[ct] if platformName == "Darwin"
var res = _Impl.semaphore_create(_Impl.mach_task_self(), _handle, _Impl.SYNC_POLICY_FIFO, Int(startValue))
else
var res = _Impl.sem_init(_handle, Int(0), UInt(startValue))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't you implement this in a Posix header?


fun dtor(this: @Semaphore)
_Impl.semaphore_destroy(_Impl.mach_task_self(), _handle)
[ct] if platformName == "Darwin"
_Impl.semaphore_destroy(_Impl.mach_task_self(), _handle)
else
_Impl.sem_destroy(_handle)

//! Increments the counter of the semaphore
fun release(s: @Semaphore)
_Impl.semaphore_signal(s._handle)
[ct] if platformName == "Darwin"
_Impl.semaphore_signal(s._handle)
else
_Impl.sem_post(s._handle)

//! Decrements the semaphore counter
//! If the counter reaches zero, the call blocks until somebody calls 'release'
fun acquire(s: @Semaphore)
while 0 != _Impl.semaphore_wait(s._handle)
/*keep trying*/;
[ct] if platformName == "Darwin"
while 0 != _Impl.semaphore_wait(s._handle)
/* keep trying */;
else
while 0 != _Impl.sem_wait(s._handle)
/* keep trying */;

package _Impl
using TaskT = Int
using SemaphoreT = Int
using SYNC_POLICY_FIFO = 0
[ct] if platformName == "Darwin"
using TaskT = Int
using SYNC_POLICY_FIFO = 0

[native("mach_task_self")] fun mach_task_self(): TaskT;
[native("semaphore_create")] fun semaphore_create(task: TaskT, s: @SemaphoreT, policy, value: Int): Int;
[native("semaphore_destroy")] fun semaphore_destroy(task: TaskT, s: SemaphoreT): Int;
[native("semaphore_signal")] fun semaphore_signal(s: SemaphoreT): Int;
[native("semaphore_wait")] fun semaphore_wait(s: SemaphoreT): Int;
using SemaphoreT = Int

//using SemType = Int;
[ct] if platformName == "Linux"
using ModeT = Int

//[native("sem_init")] fun sem_init(s: @SemType, pshared: Int, value: UInt): Int;
//[native("sem_destroy")] fun sem_destroy(s: @SemType): Int;
//[native("sem_wait")] fun sem_wait(s: @SemType): Int;
//[native("sem_post")] fun sem_post(s: @SemType): Int;
[ct] if platformName == "Darwin"
[native("mach_task_self")] fun mach_task_self(): TaskT;
[native("semaphore_create")] fun semaphore_create(task: TaskT, s: @SemaphoreT, policy, value: Int): Int;
[native("semaphore_destroy")] fun semaphore_destroy(task: TaskT, s: SemaphoreT): Int;
[native("semaphore_signal")] fun semaphore_signal(s: SemaphoreT): Int;
[native("semaphore_wait")] fun semaphore_wait(s: SemaphoreT): Int;
else
[native("sem_init")] fun sem_init(sem: @SemaphoreT, pshared: Int, value: UInt): Int
[native("sem_destroy")] fun sem_destroy(sem: @SemaphoreT): Int
[native("sem_wait")] fun sem_wait(sem: @SemaphoreT): Int
[native("sem_trywait")] fun sem_trywait(sem: @SemaphoreT): Int
[native("sem_timedwait")] fun sem_timedwait(sem: @SemaphoreT, timeout: @TimeSpecT): Int
[native("sem_post")] fun sem_post(sem: @SemaphoreT): Int
[native("sem_getvalue")] fun sem_getvalue(sem: @SemaphoreT, sval: @Int): Int
[native("sem_close")] fun sem_close(sem: @SemaphoreT): Int
[native("sem_unlink")] fun sem_unlink(name: @Char): Int
[native("sem_open")] fun sem_open(name: @Char, oflag: Int): @SemaphoreT
[native("sem_open")] fun sem_open(name: @Char, oflag: Int, mode: ModeT, value: UInt): @SemaphoreT
39 changes: 28 additions & 11 deletions SparrowImplicitLib/par/thread.spr
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
module par.thread

import std.compilerInfo
import config
import std.newDelete
//import assert

datatype NativeThreadHandle = ULong

//! Datatype describing a thread ID
datatype ThreadId
id: ULong
id: NativeThreadHandle

fun ctor(this: @ThreadId, id: ULong)
this.id ctor id
Expand All @@ -16,7 +19,13 @@ fun ctor(this: @ThreadId, h: NativeThreadHandle)
fun >>(tid: @ThreadId, os: @OutStream)
os << tid.id

[ct] if platformName == "Darwin"
fun ctor(this: @ThreadId, h: NativeThreadHandle)
this.id ctor
_Impl.pthread_threadid_np(h, id)

fun >>(tid: @ThreadId, os: @OutStream)
os << tid.id

datatype Thread
_handle: NativeThreadHandle
Expand Down Expand Up @@ -66,14 +75,14 @@ fun detach(t: @Thread)
t._handle = NativeThreadHandle()

//! Get the ID of the given thread
fun getId(t: Thread): ThreadId = ThreadId(t._handle)
//fun getId(t: Thread): ThreadId = ThreadId(t._handle)

//! Get the native handle of the given thread
fun getNativeHandle(t: Thread): NativeThreadHandle = t._handle
//fun getNativeHandle(t: Thread): NativeThreadHandle = t._handle


//! Get the current thread id
fun curThreadId: ThreadId = ThreadId(NativeThreadHandle())
//fun curThreadId: ThreadId = ThreadId(NativeThreadHandle())

//! Tries to suspend the current thread to let other threads execute
fun yield = _Impl.sched_yield()
Expand Down Expand Up @@ -110,13 +119,21 @@ package _Impl
[initCtor] datatype TimeSpec
tv_sec, tv_nsec: Long

[native("pthread_create")] fun pthread_create(handle: @NativeThreadHandle, attr: @Byte, f: ThreadFun, arg: @Byte): Int;
[native("pthread_join")] fun pthread_join(handle: NativeThreadHandle, valPtr: @ @Byte): Int;
[native("pthread_detach")] fun pthread_detach(handle: NativeThreadHandle): Int;
[native("pthread_self")] fun pthread_self: NativeThreadHandle;
[ct] if platformName == "Darwin"
[native("pthread_create")] fun pthread_create(handle: @NativeThreadHandle, attr: @Byte, f: ThreadFun, arg: @Byte): Int
[native("pthread_join")] fun pthread_join(handle: NativeThreadHandle, valPtr: @ @Byte): Int
[native("pthread_detach")] fun pthread_detach(handle: NativeThreadHandle): Int
[native("pthread_self")] fun pthread_self: NativeThreadHandle

[native("pthread_threadid_np")] fun pthread_threadid_np(t: NativeThreadHandle, res: @ULong): Int;
[native("pthread_threadid_np")] fun pthread_threadid_np(t: NativeThreadHandle, res: @ULong): Int

[native("sched_yield")] fun sched_yield;
[native("nanosleep")] fun nanosleep(req, rem: @TimeSpec): Int;
[native("sched_yield")] fun sched_yield
[native("nanosleep")] fun nanosleep(req, rem: @TimeSpec): Int
else
[native("pthread_create")] fun pthread_create(handle: @NativeThreadHandle, attr: @Byte, f: ThreadFun, arg: @Byte): Int
[native("pthread_join")] fun pthread_join(handle: NativeThreadHandle, valPtr: @ @Byte): Int
[native("pthread_detach")] fun pthread_detach(handle: NativeThreadHandle): Int
[native("pthread_self")] fun pthread_self: NativeThreadHandle

[native("sched_yield")] fun sched_yield
[native("nanosleep")] fun nanosleep(req, rem: @TimeSpec): Int
1 change: 1 addition & 0 deletions src/LLVMBackend/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ void generateNativeObjGCC(
args.insert(args.end(), s.linkerArgs_.begin(), s.linkerArgs_.end());
args.insert(args.end(), {inputFilename, "-o", outputFilename});
args.emplace_back("-lm"); // link against the math library
args.emplace_back("-pthread");
runCmd(args);
}
} // namespace
Expand Down
2 changes: 1 addition & 1 deletion tests/Examples/llvm-compile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ echo "Compiling..."
../../.out/Release/llvm/opt $1.bc -o $1.opt.bc -O3
#cp $1.bc $1.opt.bc
../../.out/Release/llvm/llc $1.opt.bc -filetype=obj
gcc $1.opt.o -o $1.out -lm
gcc $1.opt.o -o $1.out -lm -pthread
rm $1.bc
rm $1.opt.bc
rm $1.opt.o
Expand Down
7 changes: 4 additions & 3 deletions tests/tests.in
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ BenchmarkGame/fastaredux.spr: Benchmark game - fastaredux
Par/ThreadTest.spr: Parallel - thread test
Par/TlsTest.spr: Parallel - TLS test
Par/AtomicTest.spr: Parallel - atomics test
Par/LocksTest.spr: Parallel - locks test
# Par/LocksTest.spr: Parallel - locks test
Par/SemaphoreTest.spr: Parallel - semaphore test
Par/TaskTest.spr: Parallel - Fibonacci tasks test
Par/ParForTest.spr: Parallel - Basic parallel for test
# Par/TaskTest.spr: Parallel - Fibonacci tasks test
# Par/ParForTest.spr: Parallel - Basic parallel for test
# Par/PthreadTest.spr: Parallel - Basic parallel for pthread

PerfTests/Hash/TestHashPerf.spr: Performance tests - hash
PerfTests/ForPerf/spr_for.spr: Performance tests - for
Expand Down