kotlin / kotlinx-atomicfu Goto Github PK
View Code? Open in Web Editor NEWThe idiomatic way to use atomic operations in Kotlin
License: Other
The idiomatic way to use atomic operations in Kotlin
License: Other
Something like this?
inline operator fun <T> AtomicRef<T>.getValue(obj: Any, property: KProperty<Any?>): T = this.value
inline operator fun <T> AtomicRef<T>.setValue(obj: Any, property: KProperty<Any?>, value: T) = run { this.value = value }
inline operator fun AtomicBoolean.getValue(obj: Any, property: KProperty<Any?>): Boolean = this.value
inline operator fun AtomicBoolean.setValue(obj: Any, property: KProperty<Any?>, value: Boolean) = run { this.value = value }
inline operator fun AtomicInt.getValue(obj: Any, property: KProperty<Any?>): Int = this.value
inline operator fun AtomicInt.setValue(obj: Any, property: KProperty<Any?>, value: Int) = run { this.value = value }
inline operator fun AtomicLong.getValue(obj: Any, property: KProperty<Any?>): Long = this.value
inline operator fun AtomicLong.setValue(obj: Any, property: KProperty<Any?>, value: Long) = run { this.value = value }
Sometimes you just need to update and retrieve the .value
property, in those cases, this would be nice since you could use the atomic as a plain property.
Adding common dependency to a native module causes Gradle plugin to complain about it for every module. In ktor, it generates a huge number of warnings so one wouldn't be able to discover actual problems.
A compileOnly dependency is used in the Kotlin/Native target 'linuxX64':
Compilation: main
Dependencies:
org.jetbrains.kotlinx:atomicfu-common:0.12.9
Such dependencies are not applicable for Kotlin/Native, consider changing the dependency type to 'implementation' or 'api'.
No size getter in AtomicArray
and array
field is private:
public class AtomicArray<T> internal constructor(size: Int) {
private val array = Array(size) { atomic<T?>(null) }
@JsName("get\$atomicfu")
public operator fun get(index: Int): AtomicRef<T?> = array[index]
}
This is a potential future enhancement.
Support custom tracing format using the following DSL:
val trace = Trace { index, text ->
// can define custom transformation to string instead of default one below:
"$index: [${Thread.currentThread().name}] $text"
}
Will this library be available on the other platforms JS and especially Native?
No atomicfu artifacts published at maven central: the latest published version is 0.11.12
. This is important since ktor users is unable to use ktor without specifying additional repositories.
Also notice that the latest release on github is in draft state
Hello! I initially couldn't figure out why I couldn't resolve the latest coroutines version of atomicfu in my project, and then I realized it was because I was only pointing at mavenCentral()
in my dependencies list. Re-adding jcenter()
fixed it, but it'd be great to see atomicfu back up on mavenCentral()
.
When will this move to latest gradle and latest kotlin
5.4.1, 1.3.31 ?
I've got an NPE inside the JS plugin for in this code.
Actual error obtained with gradle --stacktrace build
:
Caused by: java.lang.NullPointerException
at org.mozilla.javascript.ast.ParenthesizedExpression.visit(ParenthesizedExpression.java:75)
at org.mozilla.javascript.ast.FunctionCall.visit(FunctionCall.java:158)
at org.mozilla.javascript.ast.ExpressionStatement.visit(ExpressionStatement.java:120)
at org.mozilla.javascript.ast.Block.visit(Block.java:61)
at org.mozilla.javascript.ast.FunctionNode.visit(FunctionNode.java:442)
at kotlinx.atomicfu.transformer.AtomicFUTransformerJS$AtomicOperationsInliner.visit(AtomicFUTransformerJS.kt:314)
The error is thrown both on 0.12.0
and 0.12.1
. If I comment out js part, everything works fine.
Support AtomicArray<T>
class. It should be conceptually similar to Array<AtomicRef<T>>
. The following snippet provides a glimpse of proposed API:
val a = AtomicArray<T>(size) // similar to Array constructor
val x = a[i].value // read value
a[i].value = x // set value
a[i].compareAndSet(expect, update) // do atomic operations
In FU
mode it should simply map onto AtomicReferenceArray
and its operations, but in VH
mode it shall be efficiently implement via a simple array reference with VarHandle
to perform atomic operations on elements.
In addition to AtomicArray
, support for AtomicBooleanArray
, AtomicIntArray
, and AtomicLongArray
shall be provided as well.
If you enable the plugin AtomicFU, it ceases to run kotlin-runner in IDEA:
Error: Could not find or load main class demo.AppKt
demo project: https://github.com/bug-samples/kotlinx.atomicfu-27
Currently readme on Gradle says that you need to add the following dependencies to your gradle file:
dependencies {
compileOnly "org.jetbrains.kotlinx:atomicfu:$atomicfu_version"
testRuntime "org.jetbrains.kotlinx:atomicfu:$atomicfu_version"
}
This should not be needed. Plugin shall do it automatically on JVM. Moreover, plugin shall automatically detect what Kotlin platform is used (based on the Kotlin plugin that is applied) and also add appropriate dependencies for Kotlin/JS and Kotlin/Native.
Support AtomicUInt
, AtomicULong
, and similar atomic unsigned primitives.
AtomicFU js transformer is fragile. It works for kotlinx.coroutines
but it is not generally applicable to wider range of libraries. Here is a snippet of produced JS code:
(function(root, factory) {
if (typeof define === 'function' && define.amd)
define(['exports', 'kotlin'], factory);
else if (typeof exports === 'object')
factory(module.exports, require('kotlin'));
else {
if (typeof kotlin === 'undefined') {
throw new Error("Error loading module 'kotlinx-coroutines-core'. Its dependency 'kotlin' was not found. Please, check whether 'kotlin' is loaded prior to 'kotlinx-coroutines-core'.");
}
if (typeof this['kotlinx-atomicfu'] === 'undefined') {
}
root['kotlinx-coroutines-core'] = factory(typeof this['kotlinx-coroutines-core'] === 'undefined' ? {} : this['kotlinx-coroutines-core'], kotlin);
}
}(this, function(_, Kotlin, $module$kotlinx_atomicfu) {
'use strict';
...
var atomic;
var atomic_0;
....
$$importsForInline$$['kotlinx-atomicfu'] = $module$kotlinx_atomicfu;
Things that should be fixed:
if (typeof this['kotlinx-atomicfu'] === 'undefined') { ... }
should be completely removed (not just it body), while maintaining line-numbers (fill with appropriate number of blank lines or comments)function(_, Kotlin, $module$kotlinx_atomicfu) { ...
header should be corrected, its $module$kotlinx_atomicfu
parameters shall be removed at the appropriate position (note, that it may receive additional modules after kotlinx_atomicfu
)var atomic;
and var atmoic_0;
shall be removed completely (replaced with a blank like), not just their right-hand side. $$importsForInline$$['kotlinx-atomicfu'] = $module$kotlinx_atomicfu;
line shall be replaced with a blank like (if present).Hi there, first of all thanks for this library!
However I'm confused by its current status. The readme shows this project as Incubator
, which is defined on jetbrains.com as considered experimental, not mature enough, or lacking in some manner or release quality such testing and supporting documentation
.
We are writing an open source library, and worry that we shouldn't use AtomicFu in our libraries, because of the uncertainty around its maturity and safety in production. But I know that kotlin.coroutines
relies on AtomicFu, and that coroutines are no more considered experimental, having being added to the kotlin stdlib.
Incubator
? Is it just because of an incomplete supporting documentation
, or is there another reason?We should make the following change to the gradle plugin:
BOTH
(FU
variant in root classes and VH
variant in META-INF/version/9
dir)BOTH
variant is generated by plugin, it should also automatically add Multi-Release: true
attribute to the jar manifest.Execution failed for task ':kotlinx-coroutines-core:transformJvmMainAtomicfu'.
java.lang.ClassNotFoundException: kotlinx.coroutines.EventLoopImplBase$DelayedTaskQueue
Stacktrace:
Caused by: java.lang.ClassNotFoundException: kotlinx.coroutines.EventLoopImplBase$DelayedTaskQueue
at kotlinx.atomicfu.transformer.AtomicFUTransformer$CW.getCommonSuperClass(AtomicFUTransformer.kt:1165)
at org.objectweb.asm.SymbolTable.addMergedType(SymbolTable.java:1200)
at org.objectweb.asm.Frame.merge(Frame.java:1299)
at org.objectweb.asm.Frame.merge(Frame.java:1244)
at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1610)
at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1546)
at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:767)
at kotlinx.atomicfu.transformer.AtomicFUTransformer$TransformerMV.visitEnd(AtomicFUTransformer.kt:610)
at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1288)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:688)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:400)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transformFile(AtomicFUTransformer.kt:237)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transform(AtomicFUTransformer.kt:172)
at kotlinx.atomicfu.plugin.gradle.AtomicFUTransformTask.transform(AtomicFUGradlePlugin.kt:406)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:103)
As atomic document says to leak atomic values to public use:
val _pref = atomic(0L)
var pref : Long
get() = _pref.value
set(value) { _pref.value = value }
so I think it's possible to have something like:
val pref by atomicVar(0L)
val pref by atomicVal(0L)
this::pref.atomic.value = 10
We need SynchronizedObject.tryLock
function in Kotlin/Native implementation of lock.
Kotlin/Native implementation should be rewritten on top of native-specific konan.worker.AtomicReference
class so that atomicfu-based code can be compiled into multi-threaded Kotlin/Native code.
Current usage of Gradle plugin is quite verbose. As explained in README it takes a lot of steps that you need to copy-paste into your gradle project file:
def CLASSES_POST_ATOMICFU = file("$buildDir/classes-post-atomicfu/main")
atomicFU {
inputFiles = sourceSets.main.output.classesDirs
outputDir = CLASSES_POST_ATOMICFU
classPath = sourceSets.main.runtimeClasspath
variant = "FU" // "VH" to use Java 9 VarHandle, "BOTH" to produce multi-version code
}
atomicFU.dependsOn compileKotlin
testClasses.dependsOn atomicFU
jar.dependsOn atomicFU
jar {
mainSpec.sourcePaths.clear() // hack to clear existing paths
from files(CLASSES_POST_ATOMICFU, sourceSets.main.output.resourcesDir)
}
While we'd like to continue supporting this syntax with explicit specification of inputFiles
, outputDir
and classPath
for backwards compatibility, we'd like to be able to have a completely new default behavior, so that just doing apply plugin: 'kotlinx-atomicfu'
does all the magic and configures automatic transformation for all source sets.
I added the kotlinx-atomicfu
plugin to my multiplatform project. As soon as I did that I get the following error:
Source set 'commonMain' of project '<project name>' is part of several compilations [debug, release, main]
Here is my entire build.gradle.kts
:
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.multiplatform")
id("kotlinx-atomicfu")
}
android {
compileSdkVersion(AndroidConfig.compileSdkVersion)
defaultConfig {
minSdkVersion(AndroidConfig.minSdkVersion)
targetSdkVersion(AndroidConfig.targetSdkVersion)
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
implementation(Libs.kotlin.stdlib.jvm)
implementation(Libs.androidx.annotation)
implementation(Libs.androidx.browser)
implementation(Libs.rally.kinject.jvm)
implementation(Libs.rally.kinject.jvm)
implementation(Libs.kotlinx.coroutines.android)
}
kotlin {
android()
@Suppress("UNUSED_VARIABLE")
sourceSets {
val commonMain by getting {
dependencies {
implementation(Libs.kotlin.stdlib.common)
implementation(Libs.rally.kinject.common)
implementation(Libs.rally.store.common)
implementation(Libs.rally.disposable.common)
implementation(Libs.kotlinx.coroutines.common)
}
}
val commonTest by getting {
dependencies {
for (lib in Libs.kotlin.test.common) {
implementation(lib)
}
}
}
}
}
When I use Coroutine 1.2.0-alpha, after run proguard, This warming was throw out, I don't known if I can use -dontwarn
to make it disappear, Wheather it could broken Android APP later?
Warning: kotlinx.atomicfu.AtomicFU: can't find referenced method 'void setInterceptor(kotlinx.atomicfu.AtomicOperationInterceptor)' in program class kotlinx.atomicfu.InterceptorKt
How can I include the plugin not using buildscript classpath, but using plugins
section?
That looks better...
Consider the following user-defined extension: private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true)
Currently, the postprocessor retains the original method in the bytecode. While it is okay for the Kotlin compiler to do so, it is not the case for atomicfu
that should remove every trace of itself after post-processing. Otherwise, resulting classfiles are polluted with symbols that are missing from the classpath (and it may trigger warnings like Kotlin/kotlinx.coroutines#1155).
Also, note that not only extensions but their access-bridges should be removed.
Need to move from regexps to actual tree analysis.
The following example code:
import kotlinx.atomicfu.*
private val a = atomic(0)
fun main(args: Array<String>) {
a.incrementAndGet()
println(a.value)
}
Produces the following errors during transformation:
Main.kt:6: MainKt::main: standalone invocation of kotlinx.atomicfu.AtomicInt::incrementAndGet that was not traced to previous field load
Main.kt:7: MainKt::main: standalone invocation of kotlinx.atomicfu.AtomicInt::getValue that was not traced to previous field load
Main.kt: MainKt::<clinit>: factory kotlinx.atomicfu.AtomicFU::atomic is used outside of constructor
Main.kt: MainKt::<clinit>: Failed to transform: kotlinx.atomicfu.transformer.AbortTransform: Last instruction in <clinit> shall be RETURN
We should support the use of atomic on the top level
At least by changing this section:
https://github.com/kotlin/kotlinx.atomicfu#dos-and-donts
The best solution is adding a check, see #119
Gradle plugin repository https://plugins.gradle.org allows using the new plugin DSL in Gradle without any additional configuration, e.g.
plugins {
id("the plugin id") version "1.2.3"
}
Right now we may only use the older buildscript{..}
approach
internal val x = atomic(...)
should be supported. Currently it only works if x
is accessed only from the current package. Additional checks should be made for access outside of the current package.
The last working version is 0.13.11. Guess, something had been broken after updating Gradle to 5.6.1 (#77).
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 0
at org.objectweb.asm.Frame.a(Unknown Source)
at org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
at kotlinx.atomicfu.transformer.AtomicFUTransformer$TransformerMV.visitEnd(AtomicFUTransformer.kt:611)
at org.objectweb.asm.ClassReader.b(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transformFile(AtomicFUTransformer.kt:237)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transform(AtomicFUTransformer.kt:172)
at kotlinx.atomicfu.plugin.gradle.AtomicFUTransformTask.transform(AtomicFUGradlePlugin.kt:387)
This works fine
class Bot( ... ) {
private val count = atomic<Int>(0)
val id = count.getAndUpdate { it + 1 }
}
But this doesn't work
class Bot( ... ){
val id = count.getAndUpdate { it + 1 }
companion object {
private val count = atomic<Int>(0)
}
}
And after I removed private
, I got a new exception Unsupported return within atomic operation
It would be helpful to have a README section comparing atomicfu vs Java Atomics. The current readme states:
Code it like AtomicReference/Int/Long, but run it in production efficiently as AtomicXxxFieldUpdater on Kotlin/JVM and as plain unboxed values on Kotlin/JS.
It would be helpful to have more information on why AtomicXxxFieldUpdater is more efficient then Java Atomics on Kotlin/JVM. Also any other differences to consider when choosing to use atomicfu over Java Atomics when building a Kotlin/JVM project
Hi,
While trying to migrate a project to the new multiplatform plugin I came across a bug in the gradle plugin that makes the compiling/linking of K/N tests impossible. I fiddled with it a bit and found that the plugin modify the linker options and remove the .klib of the project, making the compilation fail with a lot of unresolved reference
This bug is fully reproducible, simply create a new multiplatform project with IDEA, it works, add the atomicfu
plugin, now it fails to compile K/N tests. I also found that the artifact published on NPM is now empty, is it intentional (as the transformation is supposed to remove any reference to atomicFU)?
I attached a sample project that triggers the bug
Thank you very much for your time!
Need an update of ASM version and tests for different class versions.
getAndUpdate
Seems to fail when the parent class is an object:
object Example {
private val atomic = atomic("test")
fun update() {
atomic.getAndUpdate { "$it !" }
}
}
Results in the following compilation error:
Failed to transform: java.lang.ArrayIndexOutOfBoundsException: -1
java.lang.ArrayIndexOutOfBoundsException: -1
at org.objectweb.asm.Frame.getConcreteOutputType(Frame.java:1139)
at org.objectweb.asm.Frame.merge(Frame.java:1184)
at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1610)
at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1546)
at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:767)
at kotlinx.atomicfu.transformer.AtomicFUTransformer$TransformerMV.visitEnd(AtomicFUTransformer.kt:616)
at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1288)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:688)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:400)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transformFile(AtomicFUTransformer.kt:238)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transform(AtomicFUTransformer.kt:172)
at kotlinx.atomicfu.plugin.gradle.AtomicFUTransformTask.transform(AtomicFUGradlePlugin.kt:406)
Changing the declaration to class
will compile successfully.
This seems to have been reported before in #94 as a problem with companion objects, which was closed but never resolved.
Kotlin Version: 1.3.61
atomicfu version: 0.14.1
I have a classic consumer-producer problem with many read and few, but crucial, writes.
What fairness policies does this library uses? Can you tweak them?
My usecase is an MutableMap
used as cache. Recovering stuff from server is expensive so I'd like to minimize it as much as possible.
Of course if there is a write pending, I want no new reads to happens because I may save some from calling the server!
Steps-to-reproduce:
class Foo {
private val foo = atomic(0L).also {
println(it)
}
}
Priority: low
We need common SynchronizedObject
with the following common API:
public expect open class SynchronizedObject()
public expect inline fun <T> synchronized(lock: SynchronizedObject, block: () -> T): T
On both JVM and JS this SynchronizedObject
typealises to Any
. On JVM synchronized
maps to JVM synchronized
, while on JS
it simply calls block()
.
On native it need to be implemented carefully, so that it reused a pool of pthread mutexes for heavy-weight synchronization in a way that requires no resource management for uses of SynchronizedObject
.
Note, that on Native this primitive will by thread-safe only if all the fields that are protected by SynchronizedObject
are also atomic
. This limitation will be further addressed by a separate issue.
Apparently all versions from version 0.12.0 and later has broken publishing to npm:
only package.json
and README.md
are present in kotlinx-atomicfu subdirectory in node_modules, after installing it with
npm i kotlinx-atomicfu
class Example {
private val a = atomic<Any?>(null)
private val something: Any? = "here it is"
fun f() {
a.compareAndSet(something!!, null)
}
Example::f: Unsupported branching/control within atomic operation
Example::f: standalone invocation of kotlinx.atomicfu.AtomicRef::compareAndSet that was not traced to previous field load
Workarounds:
!!
assertion!!
assertion)Version: 0.11.10-eap13
Kotlin: 1.3.0-rc-131
The ideal solution is providing custom checks for frontend.
Harder to implement since it requires a new integration, but will provide the best DX -- early error, including inside IDE.
Acceptable solution -- report diagnostic from the backend.
Simpler to implement, but likely still requires to extend current API, anyway it's better than crash the compiler or a user application.
It seems that the following code does not work:
val a = atomicArrayOfNulls<Any?>(size)
a[i].getAndUpdate{ ... }
It fails with an exception like this:
Semaphore.kt: kotlinx.coroutines.sync.SemaphoreSegment::getAndUpdate: Failed to transform: java.lang.ClassCastException: org.objectweb.asm.tree.VarInsnNode cannot be cast to org.objectweb.asm.tree.MethodInsnNode
java.lang.ClassCastException: org.objectweb.asm.tree.VarInsnNode cannot be cast to org.objectweb.asm.tree.MethodInsnNode
at kotlinx.atomicfu.transformer.AtomicFUTransformer$TransformerMV.fixupInvokeVirtual(AtomicFUTransformer.kt:630)
at kotlinx.atomicfu.transformer.AtomicFUTransformer$TransformerMV.fixupLoadedAtomicVar(AtomicFUTransformer.kt:752)
at kotlinx.atomicfu.transformer.AtomicFUTransformer$TransformerMV.transform(AtomicFUTransformer.kt:944)
at kotlinx.atomicfu.transformer.AtomicFUTransformer$TransformerMV.visitEnd(AtomicFUTransformer.kt:551)
at org.objectweb.asm.ClassReader.b(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transformFile(AtomicFUTransformer.kt:204)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transform(AtomicFUTransformer.kt:160)
at kotlinx.atomicfu.plugin.gradle.AtomicFUTransformTask.transform(AtomicFUGradlePlugin.kt:388)
. . .
val x = atomic(false)
should be efficiently supported with native Boolean
type support.
Atomicfu creates synthetic classes to support static top-level properties. They should be package-private when possible (when there's no access from a different package) and their naming scheme shall be similar to inner-class naming (using '$' separator).
It seems that in some cases JS transformation may lead to the deadlock, or to be specific infinite while(true)
when using kotlin.coroutines.sync.Mutex
(both 0.26.0 version with Kotlin 1.2 and EAP version with Kotlin 1.3 in JavaScript).
Example project here: https://github.com/ptmt/kotlinjsplayground/blob/master/src/main/kotlin/MutexCase.kt)
In particular, this line of code doesn't work as expected:
https://github.com/Kotlin/kotlinx.coroutines/blob/4cc0e294b39ae0f38cafd49187f399b0ecc2e615/common/kotlinx-coroutines-core-common/src/sync/Mutex.kt#L472
(affected as MutexImpl)._state.compareAndSet(this@UnlockOp, update)
It gets compiled into following JavaScript:
(somewhere inside kotlinx-coroutines-core.js
)
(function(scope) {
return (Kotlin.isType(tmp$ = affected, MutexImpl) ? tmp$ : throwCCE())._state_0 === this ? function() {
(Kotlin.isType(tmp$ = affected, MutexImpl) ? tmp$ : throwCCE())._state_0 = update;
return true;
}() : false
})(this);
(there is also some unnecessary double-assignment but that's not the point)
As we can see affected
is being compared with the wrong this
, because we have a closure here. I believe it should be replaced with scope
. As far as I understand it's happening here:
I'd contribute a PR by myself, but I'm afraid that writing proper tests would take much longer than fix itself.
Failed to compile a project with compileOnly
dependency:
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.0-b07'
.
As workaround configurate atomicfu in afterEvaluate
section.
We need to implement a new transformation variant AR
(in addition to existing FU
and VH
variants) that would transform classes using atomic
objects to java.util.concurrent.Atomic*
classes without using *FieldUpdater
classes. The behavior of the (default) BOTH
variant is to be decided later. See discussion in Kotlin/kotlinx.coroutines#490 on why this is needed and what should be the default.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.