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

Make some enhancements to atomic integers #776

Draft
wants to merge 24 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e233371
Teach nqp to falsify uint64
Kaiepi Jul 19, 2022
2e8a18f
Give NQP native refs
Kaiepi Jul 23, 2022
3a7b0d4
Introduce Counter, a bounded native atomic integer
Kaiepi Jul 24, 2022
883ddc6
Always set the blocker when dropping a predecessor of a Counter
Kaiepi Jul 24, 2022
baeae37
Give NQP support for CALL-ME
Kaiepi Jul 24, 2022
468dac2
Give Counter some HLL-ish coercers
Kaiepi Jul 24, 2022
ad248c1
Don't be so wishy-washy about int/uint in Counter's guts
Kaiepi Jul 24, 2022
ad6b9af
Counter.CALL-ME take two: minimal dispatch
Kaiepi Jul 24, 2022
24c7c7b
Give nqp its own atomicint type
Kaiepi Jul 24, 2022
4913a81
Use atomicint in Counter, fixing runtime errors on Windows
Kaiepi Jul 24, 2022
0282875
Foward Counter.CALL-ME adverbs to resolved getters
Kaiepi Jul 24, 2022
6242efc
Eliminate internal Counter &ENCODE routine
Kaiepi Jul 24, 2022
86aaa6d
Fix Counter.drop_succ upper bounds check
Kaiepi Jul 24, 2022
e81231e
Finetune MoarVM nqp::falsey uint64 handling
Kaiepi Jul 24, 2022
1847e8d
Treat Counter.need_pred more like need_succ
Kaiepi Jul 25, 2022
bad6565
Eliminate the else block in Counter.drop_pred
Kaiepi Jul 25, 2022
37a35a0
Restrict Counter to MoarVM temporarily
Kaiepi Jul 25, 2022
4987730
Fix nqp::setinvokespec call on non-MoarVM platforms
Kaiepi Jul 25, 2022
57cbcff
[JVM] Compartmentalize dependencies on the Unsafe singleton
Kaiepi Jul 25, 2022
01fff60
[JVM] Implement the barrierfull op
Kaiepi Jul 26, 2022
f3bcaa1
[JVM] Do thread-safe mutable state in precomp atomically
Kaiepi Jul 27, 2022
572d61d
[JVM] Do a modern object-oriented atomic ops (i.e. eliminate Unsafe)
Kaiepi Jul 27, 2022
d820900
[JVM] Fix all -Xlint:deprecation,unchecked warnings
Kaiepi Jul 27, 2022
a44d333
[JVM] Better synchronize the class cache + weaken its references further
Kaiepi Jul 28, 2022
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
131 changes: 131 additions & 0 deletions src/core/Counter.nqp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#?if moar
# This adds bounds to a native atomic uint, giving a few means of handling when
# those are reached via succ/pred operations:
#
# - drop_pred and drop_succ perform said operation only if valid
# - want_pred and want_succ wait for said operation to become valid
# - need_pred and need_succ permit underflows and overflows respectively
#
# This roughly translates to a semaphore's tryacquire/acquire/release
# operations, which it was originally designed to replicate, to which it
# performs comparably. We get more flavours of bounds handling and
# serializability if we ditch that structure, however.
class Counter {
my int $INT_MIN_MAX := nqp::bitneg_i(0);

has atomicint $!counter;
has atomicint $!blocker;
has str $!moniker;

method new(int $counter, str $moniker = nqp::null_s()) {
my $self := nqp::create(self);
nqp::bindattr_i($self, $?CLASS, '$!counter', $counter);
nqp::bindattr_i($self, $?CLASS, '$!blocker', $counter == 0);
nqp::bindattr_s($self, $?CLASS, '$!moniker', $moniker);
$self
}

# Gives the predecessor unless we're at the lower bound.
method drop_pred() {
my $counter := nqp::getattrref_i(self, $?CLASS, '$!counter');
if my int $pred := nqp::atomicload_i($counter) {
nqp::barrierfull();
nqp::atomicstore_i($counter, ($pred := $pred - 1));
nqp::atomicstore_i(nqp::getattrref_i(self, $?CLASS, '$!blocker'), $pred == 0);
}
my uint $this := $pred
}

# Gives the predecessor, waiting for one to become valid if need be.
method want_pred() {
my $blocker := nqp::getattrref_i(self, $?CLASS, '$!blocker');
nqp::threadyield()
while nqp::cas_i($blocker, 0, 1);
nqp::atomicstore_i($blocker, 0)
if my uint $pred := nqp::sub_i(nqp::getattrref_i(self, $?CLASS, '$!counter'), 1);
$pred
}

# Gives the predecessor immediately, ignoring underflows.
method need_pred() {
my uint $pred := nqp::sub_i(nqp::getattrref_i(self, $?CLASS, '$!counter'), 1);
nqp::atomicstore_i(nqp::getattrref_i(self, $?CLASS, '$!blocker'), $pred == 0);
$pred
}

# Gives the successor unless we're at the upper bound.
method drop_succ() {
my $counter := nqp::getattrref_i(self, $?CLASS, '$!counter');
unless (my int $succ := nqp::atomicload_i($counter)) == $INT_MIN_MAX {
nqp::barrierfull();
nqp::atomicstore_i($counter, ($succ := $succ + 1));
nqp::atomicstore_i(nqp::getattrref_i(self, $?CLASS, '$!blocker'), 0);
}
my uint $this := $succ
}

# Gives the successor, waiting for one to become valid if need be.
method want_succ() {
my $counter := nqp::getattrref_i(self, $?CLASS, '$!counter');
nqp::threadyield()
while (my int $succ := nqp::atomicload_i($counter)) == $INT_MIN_MAX;
nqp::barrierfull();
nqp::atomicstore_i($counter, ($succ := $succ + 1));
nqp::atomicstore_i(nqp::getattrref_i(self, $?CLASS, '$!blocker'), 0);
my uint $this := $succ
}

# Gives the predecessor immediately, ignoring overflows.
method need_succ() {
my uint $succ := nqp::add_i(nqp::getattrref_i(self, $?CLASS, '$!counter'), 1);
nqp::atomicstore_i(nqp::getattrref_i(self, $?CLASS, '$!blocker'), $succ == 0);
$succ
}

method Int() {
my uint $counter := nqp::atomicload_i(nqp::getattrref_i(self, $?CLASS, '$!counter'))
}

method Numeric() {
self.Int()
}

method Str() {
nqp::if(nqp::isnull_s($!moniker), ~self.Int(), $!moniker)
}

method raku() {
nqp::if(
nqp::isconcrete(self),
nqp::join('', nqp::list_s(
nqp::how_nd(self).name(self),
'.new(',
~self.Int(),
nqp::if(
nqp::isnull_s($!moniker),
'',
', "' ~ nqp::join('\"', nqp::split('"', $!moniker)) ~ '"'),
')')),
nqp::how_nd(self).name(self))
}

method COERCE($counter) {
self.new(+$counter, ~$counter)
}

proto method CALL-ME(*@xs, *%adverbs) {*}
multi method CALL-ME($a, *%adverbs) {
self."$a"(|%adverbs)
}
multi method CALL-ME($a, $b, *%adverbs) {
my str $method := $a ~ '_' ~ $b;
self."$method"(|%adverbs)
}
multi method CALL-ME($a, $b, *@xs, *%adverbs) {
my @operations := nqp::list_s($a, $b);
nqp::push_s(@operations, $_) for @xs;
my str $method := nqp::join('_', @operations);
self."$method"(|%adverbs)
}
}
#?endif
29 changes: 29 additions & 0 deletions src/core/NQPMu.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,38 @@ nqp::sethllconfig('nqp', nqp::hash(
'hllize_dispatcher', 'nqp-hllize',
'istype_dispatcher', 'nqp-istype',
'isinvokable_dispatcher', 'nqp-isinvokable',
#?endif
'int_lex_ref', IntLexRef,
'uint_lex_ref', UIntLexRef,
'num_lex_ref', NumLexRef,
'str_lex_ref', StrLexRef,
'int_attr_ref', IntAttrRef,
'uint_attr_ref', UIntAttrRef,
'num_attr_ref', NumAttrRef,
'str_attr_ref', StrAttrRef,
'int_pos_ref', IntPosRef,
'uint_pos_ref', UIntPosRef,
'num_pos_ref', NumPosRef,
'str_pos_ref', StrPosRef,
'int_multidim_ref', IntMultidimRef,
'uint_multidim_ref', UIntMultidimRef,
'num_multidim_ref', NumMultidimRef,
'str_multidim_ref', StrMultidimRef,
#?if js
'int64_lex_ref', Int64LexRef,
'int64_attr_ref', Int64AttrRef,
'int64_pos_ref', Int64PosRef,
'int64_multidim_ref', Int64MultidimRef,
#?endif
));

#?if !moar
nqp::setinvokespec(NQPMu, nqp::null(), nqp::null_s(),
nqp::getstaticcode(anon sub INVOKE(*@pos, *%named) {
nqp::invokewithcapture(nqp::findmethod($?CLASS, 'CALL-ME'), nqp::usecapture())
}));
#?endif

#?if moar
nqp::dispatch('boot-syscall', 'dispatcher-register', 'nqp-hllize', -> $capture {
nqp::dispatch('boot-syscall', 'dispatcher-guard-type',
Expand Down
86 changes: 86 additions & 0 deletions src/core/NativeTypes.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,89 @@ my native num64 is repr('P6num') is nativesize(64) { }
my native num32 is repr('P6num') is nativesize(32) { }

my native str is repr('P6str') { }

my stub atomicint metaclass NQPNativeHOW is repr('P6int') { ... };

my stub IntLexRef metaclass NQPNativeRefHOW { ... };
my stub UIntLexRef metaclass NQPNativeRefHOW { ... };
my stub NumLexRef metaclass NQPNativeRefHOW { ... };
my stub StrLexRef metaclass NQPNativeRefHOW { ... };
my stub IntAttrRef metaclass NQPNativeRefHOW { ... };
my stub UIntAttrRef metaclass NQPNativeRefHOW { ... };
my stub NumAttrRef metaclass NQPNativeRefHOW { ... };
my stub StrAttrRef metaclass NQPNativeRefHOW { ... };
my stub IntPosRef metaclass NQPNativeRefHOW { ... };
my stub UIntPosRef metaclass NQPNativeRefHOW { ... };
my stub NumPosRef metaclass NQPNativeRefHOW { ... };
my stub StrPosRef metaclass NQPNativeRefHOW { ... };
my stub IntMultidimRef metaclass NQPNativeRefHOW { ... };
my stub UIntMultidimRef metaclass NQPNativeRefHOW { ... };
my stub NumMultidimRef metaclass NQPNativeRefHOW { ... };
my stub StrMultidimRef metaclass NQPNativeRefHOW { ... };

#?if js
my stub Int64LexRef metaclass NQPNativeRefHOW { ... };
my stub Int64AttrRef metaclass NQPNativeRefHOW { ... };
my stub Int64PosRef metaclass NQPNativeRefHOW { ... };
my stub Int64MultidimRef metaclass NQPNativeRefHOW { ... };
#?endif

# Set up various native reference types.
sub setup_native_ref_type($type, $primitive, $ref_kind) {
$type.HOW.set_native_type($type, $primitive);
$type.HOW.set_ref_kind($type, $ref_kind);
$type.HOW.compose_repr($type);
nqp::setcontspec($type, 'native_ref', nqp::null());
}

nqp::scwbenable();
atomicint.HOW.set_nativesize(atomicint, nqp::const::C_TYPE_ATOMIC_INT);
atomicint.HOW.compose(atomicint);

setup_native_ref_type(IntLexRef, int, 'lexical');
setup_native_ref_type(UIntLexRef, uint, 'lexical');
setup_native_ref_type(NumLexRef, num, 'lexical');
setup_native_ref_type(StrLexRef, str, 'lexical');
setup_native_ref_type(IntAttrRef, int, 'attribute');
setup_native_ref_type(UIntAttrRef, uint, 'attribute');
setup_native_ref_type(NumAttrRef, num, 'attribute');
setup_native_ref_type(StrAttrRef, str, 'attribute');
setup_native_ref_type(IntPosRef, int, 'positional');
setup_native_ref_type(UIntPosRef, uint, 'positional');
setup_native_ref_type(NumPosRef, num, 'positional');
setup_native_ref_type(StrPosRef, str, 'positional');
setup_native_ref_type(IntMultidimRef, int, 'multidim');
setup_native_ref_type(UIntMultidimRef, uint, 'multidim');
setup_native_ref_type(NumMultidimRef, num, 'multidim');
setup_native_ref_type(StrMultidimRef, str, 'multidim');
#?if js
setup_native_ref_type(Int64LexRef, int64, 'lexical');
setup_native_ref_type(Int64AttrRef, int64, 'attribute');
setup_native_ref_type(Int64PosRef, int64, 'positional');
setup_native_ref_type(Int64MultidimRef, int64, 'multidim');
#?endif

IntLexRef.HOW.compose(IntLexRef);
UIntLexRef.HOW.compose(UIntLexRef);
NumLexRef.HOW.compose(NumLexRef);
StrLexRef.HOW.compose(StrLexRef);
IntAttrRef.HOW.compose(IntAttrRef);
UIntAttrRef.HOW.compose(UIntAttrRef);
NumAttrRef.HOW.compose(NumAttrRef);
StrAttrRef.HOW.compose(StrAttrRef);
IntPosRef.HOW.compose(IntPosRef);
UIntPosRef.HOW.compose(UIntPosRef);
NumPosRef.HOW.compose(NumPosRef);
StrPosRef.HOW.compose(StrPosRef);
IntMultidimRef.HOW.compose(IntMultidimRef);
UIntMultidimRef.HOW.compose(UIntMultidimRef);
NumMultidimRef.HOW.compose(NumMultidimRef);
StrMultidimRef.HOW.compose(StrMultidimRef);

#?if js
Int64LexRef.HOW.compose(Int64LexRef);
Int64AttrRef.HOW.compose(Int64AttrRef);
Int64PosRef.HOW.compose(Int64PosRef);
Int64MultidimRef.HOW.compose(Int64MultidimRef);
#?endif
nqp::scwbdisable();
7 changes: 7 additions & 0 deletions src/core/dispatchers.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,13 @@ nqp::dispatch('boot-syscall', 'dispatcher-register', 'nqp-call', -> $capture {
$delegate);
}
}
elsif nqp::can($callee, 'CALL-ME') {
my $call-me := $callee.HOW.find_method($callee, 'CALL-ME');
my $delegate := nqp::dispatch('boot-syscall', 'dispatcher-insert-arg-literal-obj',
$capture, 0, $call-me);
nqp::dispatch('boot-syscall', 'dispatcher-delegate', 'lang-call',
$delegate);
}
else {
nqp::die("Cannot invoke object of type '{$callee.HOW.name($callee)}'");
}
Expand Down
72 changes: 72 additions & 0 deletions src/how/NQPNativeRefHOW.nqp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
knowhow NQPNativeRefHOW {
has $!name;
has $!type;
has $!refkind;
has $!composed;
has $!repr_composed;

my $archetypes := Archetypes.new(:nominal);
method archetypes() {
$archetypes
}

method new(:$name) {
my $self := nqp::create(self);
nqp::bindattr($self, $?CLASS, '$!name', $name);
$self
}

method new_type(:$name) {
my $how := self.new();
my $obj := nqp::newtype($how, 'NativeRef');
nqp::settypehll($obj, 'nqp');
nqp::setdebugtypename($obj, $name)
}

method set_native_type($obj, $type) {
$!type := $type
}

method native_type($obj) {
$!type
}

method set_ref_kind($obj, $refkind) {
$!refkind := $refkind
}

method ref_kind($obj) {
$!refkind
}

method compose($obj, *%named) {
$obj := nqp::decont($obj);
self.compose_repr($obj);
self.publish_type_cache($obj);
$!composed := 1;
$obj
}

method is_composed($obj) {
$!composed
}

method compose_repr($obj) {
unless $!repr_composed {
my $info := nqp::hash();
$info<nativeref> := nqp::hash();
$info<nativeref><type> := nqp::decont($!type);
$info<nativeref><refkind> := $!refkind // 'unknown';
nqp::composetype(nqp::decont($obj), $info);
$!repr_composed := 1;
}
}

method repr_composed($obj) {
$!repr_composed
}

method publish_type_cache($obj) {
nqp::settypecache($obj, nqp::list($obj, $!type))
}
}
1 change: 1 addition & 0 deletions src/vm/jvm/QAST/Compiler.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -3012,6 +3012,7 @@ QAST::OperationsJAST.map_classlib_core_op('killprocasync', $TYPE_IO_OPS, 'killpr
QAST::OperationsJAST.map_classlib_core_op('cas', $TYPE_OPS, 'cas', [$RT_OBJ, $RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('atomicload', $TYPE_OPS, 'atomicload', [$RT_OBJ], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('atomicstore', $TYPE_OPS, 'atomicstore', [$RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('barrierfull', $TYPE_OPS, 'barrierfull', [], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('casattr', $TYPE_OPS, 'casattr', [$RT_OBJ, $RT_OBJ, $RT_STR, $RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('atomicbindattr', $TYPE_OPS, 'atomicbindattr', [$RT_OBJ, $RT_OBJ, $RT_STR, $RT_OBJ], $RT_OBJ, :tc);

Expand Down
6 changes: 3 additions & 3 deletions src/vm/jvm/runtime/org/raku/nqp/runtime/BootJavaInterop.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,9 @@ protected SixModelObject computeInterop(ThreadContext tc, Class<?> klass) {

CompilationUnit adaptorUnit;
try {
adaptorUnit = (CompilationUnit) adaptor.constructed.newInstance();
} catch (ReflectiveOperationException roe) {
throw new RuntimeException(roe);
adaptorUnit = (CompilationUnit) adaptor.constructed.getDeclaredConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw ExceptionHandling.dieInternal(tc, e);
}
adaptorUnit.initializeCompilationUnit(tc);

Expand Down
12 changes: 9 additions & 3 deletions src/vm/jvm/runtime/org/raku/nqp/runtime/CompilationUnit.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,15 @@ public static void enterFromMain(Class<?> cuType, int entryCodeRefIdx, String[]
*/
public static CompilationUnit setupCompilationUnit(ThreadContext tc, Class<?> cuType, boolean shared)
throws InstantiationException, IllegalAccessException {
CompilationUnit cu = (CompilationUnit)cuType.newInstance();
cu.shared = shared;
cu.initializeCompilationUnit(tc);
CompilationUnit cu = null;
try {
cu = (CompilationUnit)cuType.getDeclaredConstructor().newInstance();
cu.shared = shared;
cu.initializeCompilationUnit(tc);
}
catch (ReflectiveOperationException e) {
throw ExceptionHandling.dieInternal(tc, e);
}
return cu;
}

Expand Down
Loading