├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .haxerc ├── .travis.yml.disabled ├── .vscode └── tasks.json ├── LICENSE ├── README.md ├── dev.hxml ├── haxe_libraries ├── hx3compat.hxml ├── hxcpp.hxml ├── hxcs.hxml ├── hxjava.hxml ├── hxnodejs.hxml ├── tink_chunk.hxml ├── tink_cli.hxml ├── tink_concurrent.hxml ├── tink_core.hxml ├── tink_io.hxml ├── tink_macro.hxml ├── tink_streams.hxml ├── tink_stringly.hxml └── travix.hxml ├── haxelib.json ├── src └── tink │ └── concurrent │ ├── Mutex.hx │ ├── Queue.hx │ ├── Thread.hx │ └── Tls.hx ├── tests.hxml └── tests ├── RunTests.hx ├── TestMutex.hx ├── TestQueue.hx ├── TestThread.hx └── TestTls.hx /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | test: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | haxe-version: 16 | - stable 17 | - nightly 18 | target: 19 | - node 20 | - interp 21 | - neko 22 | - python 23 | - php 24 | - cpp 25 | - js 26 | - jvm 27 | - cs -D erase-generics 28 | extra: 29 | - "" 30 | - -D concurrent 31 | exclude: 32 | - target: interp 33 | extra: -D concurrent # for some reason this combination will get stuck 34 | 35 | steps: 36 | - name: Check out repo 37 | uses: actions/checkout@v2 38 | 39 | - name: Get yarn cache directory path 40 | id: yarn-cache-dir-path 41 | run: echo "::set-output name=dir::$(yarn cache dir)" 42 | 43 | - name: Cache Yarn 44 | uses: actions/cache@v1 45 | with: 46 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 47 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 48 | restore-keys: | 49 | ${{ runner.os }}-yarn- 50 | 51 | - name: Cache Haxe 52 | uses: actions/cache@v1 53 | with: 54 | path: ${{ startsWith(runner.os, 'windows') && '%AppData%' || '~/haxe' }} 55 | key: ${{ runner.os }}-haxe 56 | 57 | - name: Install Lix 58 | uses: lix-pm/setup-lix@master 59 | 60 | - name: Install Haxe 61 | run: lix install haxe ${{ matrix.haxe-version }} 62 | 63 | - name: Install Haxe Libraries 64 | run: lix download 65 | 66 | - name: Run Test 67 | run: lix run travix ${{ matrix.target }} ${{ matrix.extra }} 68 | 69 | release: 70 | runs-on: ubuntu-latest 71 | needs: test 72 | if: startsWith(github.ref, 'refs/tags/') # consider using the "release" event. see: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#release 73 | 74 | steps: 75 | - name: Check out repo 76 | uses: actions/checkout@v2 77 | 78 | - name: Get yarn cache directory path 79 | id: yarn-cache-dir-path 80 | run: echo "::set-output name=dir::$(yarn cache dir)" 81 | 82 | - name: Cache Yarn 83 | uses: actions/cache@v1 84 | with: 85 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 86 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 87 | restore-keys: | 88 | ${{ runner.os }}-yarn- 89 | 90 | - name: Cache Haxe 91 | uses: actions/cache@v1 92 | with: 93 | path: ${{ startsWith(runner.os, 'windows') && '%AppData%' || '~/haxe' }} 94 | key: ${{ runner.os }}-haxe 95 | 96 | - name: Install Lix 97 | uses: lix-pm/setup-lix@master 98 | 99 | - name: Install Haxe 100 | run: lix install haxe stable 101 | 102 | - name: Install Haxe Libraries 103 | run: lix download 104 | 105 | - name: Release to Haxelib 106 | run: lix run travix release 107 | env: 108 | HAXELIB_AUTH: ${{ secrets.HAXELIB_AUTH }} 109 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin -------------------------------------------------------------------------------- /.haxerc: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.2.3", 3 | "resolveLibs": "scoped" 4 | } -------------------------------------------------------------------------------- /.travis.yml.disabled: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: xenial 3 | 4 | stages: 5 | - test 6 | - deploy 7 | 8 | language: node_js 9 | node_js: 8 10 | 11 | env: 12 | - HAXE_VERSION=3.4.7 13 | # - HAXE_VERSION=stable 14 | - HAXE_VERSION=latest 15 | - HAXE_VERSION=nightly 16 | 17 | install: 18 | - npm i -g lix 19 | - lix install haxe $HAXE_VERSION 20 | - lix download 21 | 22 | script: 23 | - if [ "$HAXE_VERSION" != "3.4.7" ]; then lix run travix java -D jvm; fi 24 | - if [ "$HAXE_VERSION" != "3.4.7" ]; then lix run travix java -D concurrent -D jvm; fi 25 | - lix run travix interp 26 | - lix run travix neko 27 | - lix run travix neko -D concurrent 28 | - lix run travix python 29 | - lix run travix node 30 | # - lix run travix flash 31 | - lix run travix cpp 32 | - lix run travix cpp -D concurrent 33 | - lix run travix cs 34 | - lix run travix php 35 | 36 | jobs: 37 | include: 38 | # - stage: test # should uncomment this when there is no matrix above (e.g. only one os, one env, etc) 39 | - stage: deploy 40 | os: linux 41 | install: 42 | - npm i -g lix 43 | - lix download 44 | script: skip 45 | env: 46 | secure: "boN+Z6E2oCVQn8o0P89i3D7RMuNQpzizc34HH7lCnAqO89U0cID0vaHqhLy32s3X3VKJ0VGtMHutNfpXM9NbLaPL//ZNFw5kkrmlVHGBdlYH1Jr3it8UpWq/fie/POhXmYM3Li6uUb1zfrVKCp6w7UaJck7uDLHsyYm+QSVoKpw=" 47 | after_success: 48 | - lix run travix install 49 | - lix run travix release -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "shell", 6 | "command": "lix run travix jvm", 7 | "problemMatcher": [ 8 | "$haxe-absolute", 9 | "$haxe", 10 | "$haxe-error", 11 | "$haxe-trace" 12 | ], 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | }, 17 | "label": "run tests" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 haxetink 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/haxetink/tink_core/actions/workflows/ci.yml/badge.svg)](https://github.com/haxetink/tink_concurrent/actions) 2 | [![Gitter](https://img.shields.io/gitter/room/haxetink/public?logo=gitter&)](https://gitter.im/haxetink/public) 3 | [![Discord](https://img.shields.io/discord/162395145352904705.svg?logo=discord&logoColor=white&label=discord)](https://discord.com/channels/162395145352904705/579634919576436736) 4 | 5 | # Tink Concurrent Primitives 6 | 7 | This library provides an abstraction layer over the target-specific concurrency APIs, namely: 8 | 9 | - `Thread` 10 | - `Queue` 11 | - `Tls` 12 | - `Mutex` 13 | 14 | Being largely based on abstracts, it avoids allocation of wrapper objects, although in a multi-threaded environment that will probably hardly matter on a performance level. But if you compare java.vm.Thread and tink.concurrent.Thread, you will see that it also decreases complexity. 15 | 16 | # Portability and `-D concurrent` 17 | 18 | This library runs on all platforms out of the box, like so: 19 | 20 | - `Thread` is implemented as dummy 21 | - `Queue` is implemented as a `List` 22 | - `Tls` is implemented as a `tink.core.Ref` 23 | - `Mutex` is implemented with mere noops 24 | 25 | There's exactly two things that will not compile without `-D concurrent` 26 | 27 | - creation of new threads (with `new Thread`) 28 | - blocking reads from a `Queue` (with `await`) 29 | 30 | In those cases the library will actually tell you explicitly that `concurrent` is required (instead of the compiler complaining that a class or method is not found). 31 | 32 | Support for `-D concurrent` exists on neko, java and cpp. On all other targets you will get compiler errors. 33 | 34 | ## Handling the differences introduced with `-D concurrent` 35 | 36 | For full portability, you will have to work around differences in the API that come from the `concurrent` flag. There are so many approaches, that this library cannot prescribe you how to go about it. This library is in fact not really intended for direct use, but rather with [tink_lang](http://haxetink.org/tink_lang) and/or [tink_runloop](http://haxetink.org/tink_runloop). 37 | 38 | Ensuring code to run both with and without `-D concurrent` is a worthwhile endeavor. 39 | 40 | ## Merits of not using `-D concurrent` even when available 41 | 42 | 1. In some cases you execute code in an environment where you cannot create threads, because either the OS itself or other constraints don't allow it. In those cases all abstractions are replaced by cheap non-concurrent implementations, so there will be no performance penalty. 43 | 2. It is also useful for debugging purposes. 44 | 45 | ## API difference with `neko.vm.*` and the likes 46 | 47 | This library omits reading and writing messages to threads directly. Surprisingly just using a `Queue` showed equal performance, and was thus chosen as the prefered way to pass messages, to increase type safety and keep the API lean. 48 | 49 | The `Queue` API itself is slightly different from the corresponding `Deque` API. This concerns dissecting `pop` into two calls, because they are then more easily mapped to the type system and allow for more intelligible compiler errors. 50 | -------------------------------------------------------------------------------- /dev.hxml: -------------------------------------------------------------------------------- 1 | tests.hxml 2 | -lib tink_concurrent 3 | -lib travix 4 | -neko bin/dev.n -------------------------------------------------------------------------------- /haxe_libraries/hx3compat.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/HaxeFoundation/hx3compat#ae0d8a85666199b52aed8987c9feaf297c3f66d4" into hx3compat/1.0.3/github/ae0d8a85666199b52aed8987c9feaf297c3f66d4 2 | -D hx3compat=1.0.3 3 | -cp ${HAXE_LIBCACHE}/hx3compat/1.0.3/github/ae0d8a85666199b52aed8987c9feaf297c3f66d4/std 4 | -------------------------------------------------------------------------------- /haxe_libraries/hxcpp.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "haxelib:/hxcpp#4.2.1" into hxcpp/4.2.1/haxelib 2 | # @run: haxelib run-dir hxcpp ${HAXE_LIBCACHE}/hxcpp/4.2.1/haxelib 3 | -cp ${HAXE_LIBCACHE}/hxcpp/4.2.1/haxelib/ 4 | -D hxcpp=4.2.1 -------------------------------------------------------------------------------- /haxe_libraries/hxcs.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "haxelib:/hxcs#4.2.0" into hxcs/4.2.0/haxelib 2 | # @run: haxelib run-dir hxcs ${HAXE_LIBCACHE}/hxcs/4.2.0/haxelib 3 | -cp ${HAXE_LIBCACHE}/hxcs/4.2.0/haxelib/ 4 | -D hxcs=4.2.0 -------------------------------------------------------------------------------- /haxe_libraries/hxjava.hxml: -------------------------------------------------------------------------------- 1 | -D hxjava=4.0.0-alpha 2 | # @install: lix --silent download "haxelib:/hxjava#4.0.0-alpha" into hxjava/4.0.0-alpha/haxelib 3 | # @run: haxelib run-dir hxjava ${HAXE_LIBCACHE}/hxjava/4.0.0-alpha/haxelib 4 | -cp ${HAXE_LIBCACHE}/hxjava/4.0.0-alpha/haxelib/ 5 | -java-lib lib/hxjava-std.jar 6 | -------------------------------------------------------------------------------- /haxe_libraries/hxnodejs.hxml: -------------------------------------------------------------------------------- 1 | -D hxnodejs=10.0.0 2 | # @install: lix --silent download "haxelib:/hxnodejs#10.0.0" into hxnodejs/10.0.0/haxelib 3 | -cp ${HAXE_LIBCACHE}/hxnodejs/10.0.0/haxelib/src 4 | --macro allowPackage('sys') 5 | # should behave like other target defines and not be defined in macro context 6 | --macro define('nodejs') 7 | -------------------------------------------------------------------------------- /haxe_libraries/tink_chunk.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/haxetink/tink_chunk#ac3430b9720765d2d842d82652a238ed20c2b52d" into tink_chunk/0.3.1/github/ac3430b9720765d2d842d82652a238ed20c2b52d 2 | -cp ${HAXE_LIBCACHE}/tink_chunk/0.3.1/github/ac3430b9720765d2d842d82652a238ed20c2b52d/src 3 | -D tink_chunk=0.3.1 -------------------------------------------------------------------------------- /haxe_libraries/tink_cli.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/haxetink/tink_cli#1278ad2a34fd5e2403e414aefe09bb938d0c8825" into tink_cli/0.5.1/github/1278ad2a34fd5e2403e414aefe09bb938d0c8825 2 | -lib tink_io 3 | -lib tink_macro 4 | -lib tink_stringly 5 | -cp ${HAXE_LIBCACHE}/tink_cli/0.5.1/github/1278ad2a34fd5e2403e414aefe09bb938d0c8825/src 6 | -D tink_cli=0.5.1 7 | # Make sure docs are generated 8 | -D use-rtti-doc -------------------------------------------------------------------------------- /haxe_libraries/tink_concurrent.hxml: -------------------------------------------------------------------------------- 1 | -D tink_concurrent 2 | -lib tink_core 3 | -cp src -------------------------------------------------------------------------------- /haxe_libraries/tink_core.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/haxetink/tink_core#51a4f14b6f66e945966af23dc794dd1d8dea6e61" into tink_core/2.0.2/github/51a4f14b6f66e945966af23dc794dd1d8dea6e61 2 | -cp ${HAXE_LIBCACHE}/tink_core/2.0.2/github/51a4f14b6f66e945966af23dc794dd1d8dea6e61/src 3 | -D tink_core=2.0.2 -------------------------------------------------------------------------------- /haxe_libraries/tink_io.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/haxetink/tink_io#2a7bae6747d31eb020e265b25a4fdb0d8c8f3aca" into tink_io/0.8.0/github/2a7bae6747d31eb020e265b25a4fdb0d8c8f3aca 2 | -lib tink_chunk 3 | -lib tink_streams 4 | -cp ${HAXE_LIBCACHE}/tink_io/0.8.0/github/2a7bae6747d31eb020e265b25a4fdb0d8c8f3aca/src 5 | -D tink_io=0.8.0 -------------------------------------------------------------------------------- /haxe_libraries/tink_macro.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/haxetink/tink_macro#c400f5af51773a694942631ab55026956f021562" into tink_macro/1.0.0/github/c400f5af51773a694942631ab55026956f021562 2 | -lib tink_core 3 | -cp ${HAXE_LIBCACHE}/tink_macro/1.0.0/github/c400f5af51773a694942631ab55026956f021562/src 4 | -D tink_macro=1.0.0 -------------------------------------------------------------------------------- /haxe_libraries/tink_streams.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/haxetink/tink_streams#aa006c354e742164c4f7bc8ec4532bc9154fb0d0" into tink_streams/0.3.3/github/aa006c354e742164c4f7bc8ec4532bc9154fb0d0 2 | -lib tink_core 3 | -cp ${HAXE_LIBCACHE}/tink_streams/0.3.3/github/aa006c354e742164c4f7bc8ec4532bc9154fb0d0/src 4 | -D tink_streams=0.3.3 5 | # temp for development, delete this file when pure branch merged 6 | -D pure -------------------------------------------------------------------------------- /haxe_libraries/tink_stringly.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/haxetink/tink_stringly#8ebc7897b5d08db734997f47e1ad56b3359ef30c" into tink_stringly/0.4.0/github/8ebc7897b5d08db734997f47e1ad56b3359ef30c 2 | -lib tink_core 3 | -cp ${HAXE_LIBCACHE}/tink_stringly/0.4.0/github/8ebc7897b5d08db734997f47e1ad56b3359ef30c/src 4 | -D tink_stringly=0.4.0 -------------------------------------------------------------------------------- /haxe_libraries/travix.hxml: -------------------------------------------------------------------------------- 1 | # @install: lix --silent download "gh://github.com/back2dos/travix#b680fcfb69da7151a6a818e14e7fc82bb966a8b1" into travix/0.15.0/github/b680fcfb69da7151a6a818e14e7fc82bb966a8b1 2 | # @post-install: cd ${HAXE_LIBCACHE}/travix/0.15.0/github/b680fcfb69da7151a6a818e14e7fc82bb966a8b1 && haxe -cp src --run travix.PostDownload 3 | # @run: haxelib run-dir travix ${HAXE_LIBCACHE}/travix/0.15.0/github/b680fcfb69da7151a6a818e14e7fc82bb966a8b1 4 | -lib tink_cli 5 | -cp ${HAXE_LIBCACHE}/travix/0.15.0/github/b680fcfb69da7151a6a818e14e7fc82bb966a8b1/src 6 | -D travix=0.15.0 7 | --macro travix.Macro.setup() -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tink_concurrent", 3 | "license": "MIT", 4 | "tags": [ 5 | "tink", 6 | "cross", 7 | "utility", 8 | "concurrency" 9 | ], 10 | "classPath": "src", 11 | "description": "An abstraction layer over the target-specific concurrency APIs", 12 | "contributors": [ 13 | "back2dos" 14 | ], 15 | "releasenote": "Correct java behavior.", 16 | "version": "0.1.3", 17 | "url": "http://haxetink.org/tink_concurrent", 18 | "dependencies": { 19 | "tink_core": "" 20 | } 21 | } -------------------------------------------------------------------------------- /src/tink/concurrent/Mutex.hx: -------------------------------------------------------------------------------- 1 | package tink.concurrent; 2 | 3 | using tink.CoreApi; 4 | 5 | @:forward 6 | abstract Mutex(Impl) { 7 | public inline function new() 8 | this = new Impl(); 9 | 10 | #if (!target.threaded || !concurrent) inline #end 11 | public function synchronized(f:Void->A):A { 12 | #if (target.threaded && concurrent) 13 | this.acquire(); 14 | return try { 15 | var ret:A = f(); 16 | this.release(); 17 | return ret; 18 | } 19 | catch (e:Dynamic) { 20 | this.release(); 21 | Error.rethrow(e); 22 | } 23 | #else 24 | return f(); 25 | #end 26 | } 27 | } 28 | 29 | #if (target.threaded && concurrent) 30 | @:forward 31 | private abstract Impl(sys.thread.Mutex) { 32 | public inline function new() 33 | this = new sys.thread.Mutex(); 34 | #if (java || cs || python) // on these platforms releasing a non-owned lock will throw 35 | public function release() 36 | try this.release() catch(_) {} 37 | #end 38 | } 39 | #else 40 | private abstract Impl(Bool) { 41 | public inline function new() this = false; 42 | public inline function tryAcquire():Bool return true; 43 | public inline function acquire():Void {} 44 | public inline function release():Void {} 45 | } 46 | #end 47 | -------------------------------------------------------------------------------- /src/tink/concurrent/Queue.hx: -------------------------------------------------------------------------------- 1 | package tink.concurrent; 2 | import tink.core.Any; 3 | 4 | @:forward(add, push) 5 | abstract Queue(Impl) { 6 | public inline function new() 7 | this = new Impl(); 8 | 9 | public inline function pop():Null 10 | return this.pop(false); 11 | 12 | @:requires(target.threaded) 13 | @:requires(concurrent) 14 | public inline function await():T 15 | return this.pop(true); 16 | } 17 | 18 | 19 | 20 | #if (target.threaded && concurrent) 21 | private typedef Impl = sys.thread.Deque; 22 | #else 23 | @:forward(add, push) 24 | private abstract Impl(List) { 25 | public inline function new() this = new List(); 26 | public inline function pop(block:Bool) return this.pop(); 27 | } 28 | #end -------------------------------------------------------------------------------- /src/tink/concurrent/Thread.hx: -------------------------------------------------------------------------------- 1 | package tink.concurrent; 2 | 3 | using tink.CoreApi; 4 | 5 | abstract Thread(Impl) from Impl to Impl { 6 | @:require(target.threaded) 7 | @:require(concurrent) 8 | public inline function new(f:Void->Void) 9 | #if (target.threaded && concurrent) 10 | this = Impl.create(f); 11 | #else 12 | throw 'Not Implemented'; 13 | #end 14 | 15 | static public var current(get, never):Thread; 16 | 17 | static inline function get_current():Thread 18 | return Impl.getCurrent(); 19 | 20 | @:op(A==B) 21 | public static inline function eq(a:Thread, b:Thread):Bool 22 | return Impl.eq(a, b); 23 | 24 | static public var MAIN(default, null) = current; 25 | } 26 | 27 | 28 | #if (target.threaded && concurrent) 29 | private abstract Impl(sys.thread.Thread) from sys.thread.Thread to sys.thread.Thread { 30 | public static inline function create(f):Impl 31 | return sys.thread.Thread.create(f); 32 | public static inline function getCurrent():Impl 33 | return sys.thread.Thread.current(); 34 | public static inline function eq(a:Impl, b:Impl):Bool 35 | return (a:sys.thread.Thread) == (b:sys.thread.Thread); 36 | 37 | } 38 | 39 | #else 40 | private abstract Impl(String) { 41 | inline function new(s) this = s; 42 | static public inline function getCurrent():Impl 43 | return new Impl('Fake Main Thread'); 44 | 45 | public static inline function eq(a:Impl, b:Impl):Bool 46 | return a == b; 47 | } 48 | #end -------------------------------------------------------------------------------- /src/tink/concurrent/Tls.hx: -------------------------------------------------------------------------------- 1 | package tink.concurrent; 2 | import haxe.ds.GenericStack; 3 | import tink.core.Pair; 4 | 5 | @:forward(value) 6 | abstract Tls(Impl) from Impl { 7 | public inline function new() 8 | this = new Impl(); 9 | } 10 | 11 | 12 | #if (target.threaded && concurrent) 13 | private typedef Impl = sys.thread.Tls; 14 | #else 15 | @:forward(value) 16 | private abstract Impl(tink.core.Ref) { 17 | public inline function new() 18 | this = tink.core.Ref.to(cast null); 19 | } 20 | #end -------------------------------------------------------------------------------- /tests.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -cp tests 3 | -dce full 4 | -lib hx3compat 5 | -main RunTests -------------------------------------------------------------------------------- /tests/RunTests.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import haxe.unit.TestCase; 4 | import haxe.unit.TestRunner; 5 | 6 | using tink.CoreApi; 7 | 8 | class RunTests { 9 | static var tests:Array = [ 10 | new TestThread(), 11 | new TestQueue(), 12 | new TestMutex(), 13 | new TestTls(), 14 | ]; 15 | 16 | static function main() { 17 | #if js //works for nodejs and browsers alike 18 | var buf = []; 19 | TestRunner.print = function (s:String) { 20 | var parts = s.split('\n'); 21 | if (parts.length > 1) { 22 | parts[0] = buf.join('') + parts[0]; 23 | buf = []; 24 | while (parts.length > 1) 25 | untyped console.log(parts.shift()); 26 | } 27 | buf.push(parts[0]); 28 | } 29 | #end 30 | var runner = new TestRunner(); 31 | for (test in tests) 32 | runner.add(test); 33 | 34 | travix.Logger.exit(if (runner.run()) 0 else 500); 35 | } 36 | } -------------------------------------------------------------------------------- /tests/TestMutex.hx: -------------------------------------------------------------------------------- 1 | package; 2 | import haxe.unit.TestCase; 3 | import tink.concurrent.Mutex; 4 | import tink.concurrent.Queue; 5 | import tink.concurrent.Thread; 6 | 7 | class TestMutex extends TestCase { 8 | var m:Mutex; 9 | var q:Queue; 10 | 11 | override public function setup():Void { 12 | super.setup(); 13 | m = new Mutex(); 14 | q = new Queue(); 15 | } 16 | 17 | function testSimple() { 18 | //these are basically noops on non threaded targets 19 | assertTrue(m.tryAcquire()); 20 | assertTrue(m.tryAcquire()); 21 | m.release(); 22 | m.release(); 23 | 24 | m.acquire(); 25 | m.acquire(); 26 | m.release(); 27 | m.release(); 28 | m.release(); // test release non-owned lock 29 | assertTrue(true); 30 | } 31 | 32 | #if (target.threaded && concurrent) 33 | 34 | function testAcquire() { 35 | var t = new Thread(function () { 36 | m.acquire(); 37 | m.acquire(); 38 | m.release(); 39 | Sys.sleep(.1); 40 | m.release(); 41 | }); 42 | Sys.sleep(.05); 43 | assertFalse(m.tryAcquire()); 44 | Sys.sleep(.1); 45 | assertTrue(m.tryAcquire()); 46 | } 47 | 48 | function testSynchronized() { 49 | var threads = 100, 50 | count = #if cpp 1000 #else 10000 #end,//cpps mutexes are abysmally slow 51 | counter = 0; 52 | for (i in 0...threads) 53 | new Thread(function () { 54 | for (i in 0...count) 55 | m.synchronized(function () counter++); 56 | q.add('yo'); 57 | }); 58 | for (i in 0...threads) 59 | q.await(); 60 | 61 | assertEquals(threads * count, counter); 62 | } 63 | 64 | function testExceptions() { 65 | var t = new Thread(function () { 66 | try { 67 | assertEquals(5, m.synchronized(function () return 5)); 68 | m.synchronized(function () return throw 'foo'); 69 | } 70 | catch (e:String) 71 | q.add(e); 72 | }); 73 | 74 | assertEquals(q.await(), 'foo'); 75 | assertTrue(m.tryAcquire()); 76 | } 77 | #end 78 | } -------------------------------------------------------------------------------- /tests/TestQueue.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import haxe.unit.TestCase; 4 | import tink.concurrent.Thread; 5 | 6 | import tink.concurrent.Queue; 7 | 8 | class TestQueue extends TestCase { 9 | function testBasic() { 10 | var q = new Queue(); 11 | assertEquals(null, q.pop()); 12 | q.add('5'); 13 | assertEquals('5', q.pop()); 14 | assertEquals(null, q.pop()); 15 | q.push('3'); 16 | q.add('1'); 17 | q.push('2'); 18 | assertEquals('2', q.pop()); 19 | assertEquals('3', q.pop()); 20 | assertEquals('1', q.pop()); 21 | #if (target.threaded && concurrent) 22 | q.add('5'); 23 | assertEquals('5', q.await()); 24 | assertEquals(null, q.pop()); 25 | #end 26 | } 27 | #if (target.threaded && concurrent) 28 | function testConcurrent() { 29 | 30 | var counter = 0, 31 | output = new Queue(), 32 | input = new Queue(); 33 | 34 | new Thread(function () { 35 | assertEquals(4, output.await()); 36 | counter += 1; 37 | 38 | Sys.sleep(.20); 39 | 40 | input.add(12); 41 | }); 42 | 43 | Sys.sleep(.05); 44 | 45 | assertEquals(0, counter); 46 | 47 | output.add(4); 48 | Sys.sleep(.05); 49 | 50 | assertEquals(1, counter); 51 | 52 | assertEquals(12, input.await()); 53 | } 54 | #end 55 | } -------------------------------------------------------------------------------- /tests/TestThread.hx: -------------------------------------------------------------------------------- 1 | package; 2 | import haxe.unit.TestCase; 3 | import tink.concurrent.Thread; 4 | 5 | class TestThread extends TestCase { 6 | 7 | function testMain() { 8 | assertTrue(Thread.MAIN == Thread.current); 9 | } 10 | 11 | #if (target.threaded && concurrent) 12 | function testCurrent() { 13 | var threads:Array = null; 14 | threads = [ 15 | for (i in 0...100) { 16 | new Thread(function () { 17 | Sys.sleep(.1); 18 | for (j in 0...threads.length) { 19 | if (i == j) 20 | assertTrue(threads[j] == Thread.current); 21 | else 22 | assertFalse(threads[j] == Thread.current); 23 | } 24 | }); 25 | } 26 | ]; 27 | threads.push(Thread.current); 28 | assertTrue(Thread.MAIN == Thread.current); 29 | Sys.sleep(.2); 30 | } 31 | #end 32 | } -------------------------------------------------------------------------------- /tests/TestTls.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import haxe.unit.TestCase; 4 | import tink.concurrent.*; 5 | 6 | class TestTls extends TestCase { 7 | function testSimple() { 8 | var t = new Tls(); 9 | for (i in 0...100) { 10 | t.value = i; 11 | assertEquals(i, t.value); 12 | } 13 | } 14 | #if (target.threaded && concurrent) 15 | function testConcurrent() { 16 | var l = new Tls(); 17 | l.value = -1; 18 | var q = new Queue(); 19 | var count = 50; 20 | 21 | for (i in 0...count) { 22 | 23 | new Thread(function () { 24 | var expected = i * count * 2; 25 | function next() 26 | l.value = expected = expected + 1; 27 | for (j in 0...count) { 28 | next(); 29 | Sys.sleep(((i + j) % 10) / 10000); 30 | q.add({ expected: expected, actual: l.value }); 31 | } 32 | }); 33 | } 34 | 35 | for (i in 0...count * count) 36 | switch q.await() { 37 | case { expected: e, actual: a } : 38 | assertEquals(e, a); 39 | } 40 | assertEquals(l.value, -1); 41 | } 42 | #end 43 | } --------------------------------------------------------------------------------