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

Add bindings and helpers for C atomics #3115

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
254 changes: 254 additions & 0 deletions clib/src/main/resources/scala-native/atomic.c

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions clib/src/main/resources/scala-native/atomic.c.gyb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// clang-format off

#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>

memory_order scalanative_atomic_memory_order_relaxed() { return memory_order_relaxed;}
memory_order scalanative_atomic_memory_order_consume() { return memory_order_consume;}
memory_order scalanative_atomic_memory_order_acquire() { return memory_order_acquire;}
memory_order scalanative_atomic_memory_order_release() { return memory_order_release;}
memory_order scalanative_atomic_memory_order_acq_rel() { return memory_order_acq_rel;}
memory_order scalanative_atomic_memory_order_seq_cst() { return memory_order_seq_cst;}
%{
atomics = [
('char','byte'),
('unsigned char','ubyte'),
('short','short'),
('unsigned short','ushort'),
('int','int'),
('unsigned int','uint'),
('long','long'),
('unsigned long','ulong'),
('long long','llong'),
('unsigned long long','ullong'),
('intptr_t', 'intptr'),
]

}%
% for (T, N) in atomics:

void scalanative_atomic_init_${N}(_Atomic(${T})* atm, ${T} init_value) { atomic_init(atm, init_value);}
${T} scalanative_atomic_load_${N}(_Atomic(${T})* atm) { return atomic_load(atm);}
${T} scalanative_atomic_load_explicit_${N}(_Atomic(${T})* atm, memory_order memoryOrder) { return atomic_load_explicit(atm, memoryOrder);}
void scalanative_atomic_store_${N}(_Atomic(${T})* atm, ${T} val) {atomic_store(atm, val);}
void scalanative_atomic_store_explicit_${N}(_Atomic(${T})* atm, ${T} val, memory_order memoryOrder) { atomic_store_explicit(atm, val, memoryOrder);}
${T} scalanative_atomic_exchange_${N}(_Atomic(${T})* atm, ${T} val) { return atomic_exchange(atm, val);}
${T} scalanative_atomic_exchange_explicit_${N}(_Atomic(${T})* atm, ${T} val, memory_order memoryOrder) { return atomic_exchange_explicit(atm, val, memoryOrder);}
% for cmp in ['strong', 'weak']:
bool scalanative_atomic_compare_exchange_${cmp}_${N}(_Atomic(${T})* atm, ${T}* expected, ${T} desired) { return atomic_compare_exchange_${cmp}(atm, expected, desired);}
bool scalanative_atomic_compare_exchange_${cmp}_explicit_${N}(_Atomic(${T})* atm, ${T}* expected, ${T} desired, memory_order onSucc, memory_order onFail) { return atomic_compare_exchange_${cmp}_explicit(atm, expected, desired, onSucc, onFail);}
% end
% for op in ['add', 'sub', 'and', 'or', 'xor']:
${T} scalanative_atomic_fetch_${op}_${N}(_Atomic(${T})* atm, ${T} val) { return atomic_fetch_${op}(atm, val);}
${T} scalanative_atomic_fetch_${op}_explicit_${N}(_Atomic(${T})* atm, ${T} val, memory_order memoryOrder) { return atomic_fetch_${op}_explicit(atm, val, memoryOrder);}
% end
% end
13 changes: 13 additions & 0 deletions clib/src/main/resources/scala-native/atomic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <atomic>

// Due to some bug (in GCC?) it might not be possible to access
WojciechMazur marked this conversation as resolved.
Show resolved Hide resolved
// atomic_thread_fence using C (would fail with undefined reference to function)
// For some strange reason it works in C++
extern "C" {
void scalanative_atomic_thread_fence(std::memory_order order) {
return std::atomic_thread_fence(order);
}
void scalanative_atomic_signal_fence(std::memory_order order) {
return std::atomic_signal_fence(order);
}
}
1,472 changes: 1,472 additions & 0 deletions clib/src/main/scala/scala/scalanative/libc/atomic.scala

Large diffs are not rendered by default.

352 changes: 352 additions & 0 deletions clib/src/main/scala/scala/scalanative/libc/atomic.scala.gyb

Large diffs are not rendered by default.

22 changes: 9 additions & 13 deletions javalib/src/main/scala/java/lang/Thread.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import scala.scalanative.runtime.Intrinsics._
import scala.scalanative.runtime.{fromRawPtr, NativeThread}
import scala.scalanative.runtime.NativeThread.{State => _, _}
import scala.scalanative.runtime.NativeThread.State._
import scala.scalanative.libc.atomic.{CAtomicLongLong, atomic_thread_fence}
import scala.scalanative.libc.atomic.memory_order._

import scala.scalanative.runtime.JoinNonDeamonThreads

Expand Down Expand Up @@ -178,13 +180,11 @@ class Thread private[lang] (
throw new IllegalStateException(
"ScalaNative application linked with disabled multithreading support"
)

// TODO: atomics
// atomic_thread_fence(memory_order_acquire)
atomic_thread_fence(memory_order_seq_cst)
nativeThread = Thread.nativeCompanion.create(this, stackSize)
// atomic_thread_fence(memory_order_release)
atomic_thread_fence(memory_order_release)
while (nativeThread.state == New) Thread.onSpinWait()
// atomic_thread_fence(memory_order_acquire)
atomic_thread_fence(memory_order_acquire)
nativeThread.setPriority(priority)
}

Expand Down Expand Up @@ -365,14 +365,10 @@ object Thread {
// Counter used to generate thread's ID, 0 resevered for main
final protected var threadId = 1L
private def getNextThreadId(): scala.Long = {
val id = threadId
threadId += 1
id
// TODO: atomics
// val threadIdRef = new CAtomicLongLong(
// fromRawPtr(classFieldRawPtr(this, "threadId"))
// )
// threadIdRef.fetchAdd(1L)
val threadIdRef = new CAtomicLongLong(
fromRawPtr(classFieldRawPtr(this, "threadId"))
)
threadIdRef.fetchAdd(1L)
}

}
17 changes: 7 additions & 10 deletions javalib/src/main/scala/java/lang/impl/PosixThread.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import scala.scalanative.posix.signal._
import scala.scalanative.posix.errno._
import scala.scalanative.posix.poll._
import scala.scalanative.posix.unistd._
import scala.scalanative.libc.atomic._
import scala.scalanative.libc.atomic.memory_order.memory_order_seq_cst

private[java] class PosixThread(val thread: Thread, stackSize: Long)
extends NativeThread {
Expand Down Expand Up @@ -116,11 +118,7 @@ private[java] class PosixThread(val thread: Thread, stackSize: Long)
isAbsolute: Boolean
): Unit = if (isMultithreadingEnabled) {
// fast-path check, return if can skip parking
// TODO: atomics
val prev = counter
counter = 0
if (prev > 0) return
// if (counterAtomic.exchange(0) > 0) return
if (counterAtromic.exchange(0) > 0) return
WojciechMazur marked this conversation as resolved.
Show resolved Hide resolved
// Avoid parking if there's an interrupt pending
if (thread.isInterrupted()) return
// Don't wait at all
Expand Down Expand Up @@ -161,7 +159,7 @@ private[java] class PosixThread(val thread: Thread, stackSize: Long)
state = NativeThread.State.Running
val status = pthread_mutex_unlock(lock)
assert(status == 0, "park, unlock")
// TODO: atomics: atomic_thread_fence(memory_order.memory_order_seq_cst)
atomic_thread_fence(memory_order_seq_cst)
}
}

Expand Down Expand Up @@ -263,10 +261,9 @@ private[java] class PosixThread(val thread: Thread, stackSize: Long)
fromRawPtr(elemRawPtr(base, offset))
}

// TODO: atomics
// @alwaysinline private def counterAtomic = new CAtomicInt(
// fromRawPtr(classFieldRawPtr(this, "counter"))
// )
@alwaysinline private def counterAtromic = new CAtomicInt(
WojciechMazur marked this conversation as resolved.
Show resolved Hide resolved
fromRawPtr(classFieldRawPtr(this, "counter"))
)

@inline private def priorityMapping(
threadPriority: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import scala.scalanative.runtime.GC.{ThreadRoutineArg, ThreadStartRoutine}
import scala.scalanative.annotation.alwaysinline
import scala.scalanative.unsafe._
import scala.scalanative.meta.LinktimeInfo.isMultithreadingEnabled
// TODO: atomics
//import scala.scalanative.runtime.libc.atomic_thread_fence
//import scala.scalanative.runtime.libc.memory_order._
// TODO: implement actual ConcurrentHashMap
import scala.scalanative.runtime.libc.atomic_thread_fence
import scala.scalanative.runtime.libc.memory_order._
import java.util.concurrent.ConcurrentHashMap

trait NativeThread {
Expand Down Expand Up @@ -128,7 +126,7 @@ object NativeThread {
import nativeThread.thread
TLS.assignCurrentThread(thread, nativeThread)
nativeThread.state = State.Running
// TODO: atomics: atomic_thread_fence(memory_order_seq_cst)
atomic_thread_fence(memory_order_seq_cst)
// Ensure Java Thread already assigned the Native Thread instance
// Otherwise park/unpark events might be lost
while (thread.getState() == Thread.State.NEW) onSpinWait()
Expand Down
12 changes: 12 additions & 0 deletions nativelib/src/main/scala/scala/scalanative/runtime/libc.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,16 @@ object libc {
def memset(dest: RawPtr, ch: CInt, count: CSize): RawPtr = extern
def memmove(dest: RawPtr, src: RawPtr, count: CSize): RawPtr = extern
def remove(fname: CString): CInt = extern

// Glue layer defined in libc
@name("scalanative_atomic_thread_fence")
private[runtime] final def atomic_thread_fence(order: memory_order): Unit =
extern

private[runtime] type memory_order = Int
@extern
private[runtime] object memory_order {
@name("scalanative_atomic_memory_order_seq_cst")
final def memory_order_seq_cst: memory_order = extern
}
}
10 changes: 7 additions & 3 deletions scripts/gyb_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ scalanativeUnsafe=nativelib/src/main/scala/scala/scalanative/unsafe
scalanativeUnsigned=nativelib/src/main/scala/scala/scalanative/unsigned
scalanativeRuntime=nativelib/src/main/scala/scala/scalanative/runtime

function gyb {
function gyb() {
file=$1
if [ ${file: -4} == ".gyb" ]; then
scripts/gyb.py --line-directive '' -o "${file%.gyb}" "$file"
outputFile="${file%.gyb}"
echo "Generate $outputFile"
scripts/gyb.py --line-directive '' -o "$outputFile" "$file"
else
echo "$file is not a .gyb file"
exit 1
Expand All @@ -21,9 +23,11 @@ gyb $scalanativeUnsafe/Nat.scala.gyb
gyb $scalanativeUnsafe/CStruct.scala.gyb
gyb $scalanativeUnsafe/CFuncPtr.scala.gyb
gyb $scalanativeUnsafe/Size.scala.gyb

gyb $scalanativeUnsigned/USize.scala.gyb

gyb $scalanativeRuntime/Arrays.scala.gyb
gyb $scalanativeRuntime/Boxes.scala.gyb
gyb $scalanativeRuntime/Primitives.scala.gyb

gyb clib/src/main/scala/scala/scalanative/libc/atomic.scala.gyb
gyb clib/src/main/resources/scala-native/atomic.c.gyb