Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/tolitius/lasync
making executor service tougher
https://github.com/tolitius/lasync
back-pressure clojure concurrency java
Last synced: 3 months ago
JSON representation
making executor service tougher
- Host: GitHub
- URL: https://github.com/tolitius/lasync
- Owner: tolitius
- Created: 2013-07-23T21:39:43.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2023-09-13T21:22:59.000Z (over 1 year ago)
- Last Synced: 2024-10-12T22:28:34.037Z (4 months ago)
- Topics: back-pressure, clojure, concurrency, java
- Language: Clojure
- Homepage:
- Size: 44.9 KB
- Stars: 43
- Watchers: 2
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
## limited async
an executor service (a.k.a. smart pool of threads) that is backed by an [ArrayLimitedQueue](src/java/lasync/limitq/ArrayLimitedQueue.java) or a [LinkedLimitedQueue](src/java/lasync/limitq/LinkedLimitedQueue.java).
[![ (lasync/submit pool #(+ 41 1))
#object[java.util.concurrent.FutureTask 0x6d1ce6d3 "java.util.concurrent.FutureTask@6d1ce6d3"]
```as well as an `execute` function that _does not return a future_, hence exeptions will be caught and reported by the default exception handler.
### number of threads
by default lasync will create `available cores * 2 + 42` number of threads:
```clojure
(defn- number-of-threads []
(+ (* 2 available-cores) 42))
```but the number can be changed by:
```clojure
user=> (def pool (lasync/pool {:threads 42}))
#'user/pool
```### queue size
the default queue that is backing lasync's pool is `ArrayLimitedQueue` with a default capacity of `1024` items. But all defaults are there to customize.
A queue size is what limits the pool _enabling the back pressure_. Use `:limit` to tune that knob:```clojure
(def pool (lasync/pool {:limit 65535}))
```### stats
pool is no good when it is a black box. lasync let's you unbox those stats whenever you need it:
```clojure
user=> (lasync/stats pool){:largestPoolSize 0,
:queueCurrentSize 0,
:activeCount 0,
:terminating false,
:poolSize 0,
:taskCount 0,
:completedTaskCount 0,
:class java.util.concurrent.ThreadPoolExecutor,
:terminated false,
:keepAliveTimeMs 60000,
:allowsCoreThreadTimeOut false,
:corePoolSize 66,
:maximumPoolSize 66,
:shutdown false}
```## show me
to see lasync in action:
```clojure
lein repl
``````clojure
user=> (require '[show :refer [rock-on]])
``````clojure
user=> (rock-on 69) ;; Woodstock'69
``````
INFO: pool q-size: 4, submitted: 1
INFO: pool q-size: 4, submitted: 3
INFO: pool q-size: 4, submitted: 2
INFO: pool q-size: 4, submitted: 0
INFO: pool q-size: 4, submitted: 4
INFO: pool q-size: 4, submitted: 5
INFO: pool q-size: 4, submitted: 6
INFO: pool q-size: 4, submitted: 7
...
...
INFO: pool q-size: 4, submitted: 62
INFO: pool q-size: 3, submitted: 60
INFO: pool q-size: 4, submitted: 63
INFO: pool q-size: 3, submitted: 65
INFO: pool q-size: 3, submitted: 64
INFO: pool q-size: 2, submitted: 66
INFO: pool q-size: 1, submitted: 67
INFO: pool q-size: 0, submitted: 68
```here lasync show was rocking on 4 core box (which it picked up on), so regardless of how many tasks are being pushed to it,
the queue max size always stays at 4, and lasync creates that back pressure in case the task q limit is reached.
In fact the "blocking" can be seen in action, as each task is sleeping for a second,
so the whole thing can be visually seen being processed by 4, pause, next 4, pause, etc..here is [the code](dev/show.clj) behind the show
do check out the `(expire-core-threads 69)` and `(use-max-threads 69)` from the examples as well
## tweaking other knobs
#### queue implementation
while `ArrayLimitedQueue` fits most of the use cases, a custom, or a different queue can be configured via `:queue`:
```clojure
(def pool (lasync/pool {:queue (LinkedLimitedQueue. 128)}))
```#### thread factory
by default lasync's thread factory tries to have reasonable defaults but if you want to make your it's simply a matter
of reify'ing an interface.```clojure
(def tpool (reify
ThreadFactory
(newThread [_ runnable] ...)))(def pool (lasync/pool {:threads 10 :thread-factory tpool}))
```#### rejected execution handler
lasync takes an optional `rejected-fn` that will be called on every `RejectedExecutionException`. The default function is:
```clojure
(defn default-rejected-fn [runnable _]
(throw (RejectedExecutionException.
(str "rejected execution: " runnable))))
```but it can be replaced with a custom one (the second param is an `executor`, it is ignored in this case):
```clojure
(defn log-rejected [runnable _]
(error runnable "was rejected"))(def pool (lasync/pool {:threads 10 :rejected-fn log-rejected}))
```#### unDefault it
```clojure
(def tpool (reify ThreadFactory
(newThread [_ runnable] ...)))(defn log-rejected [runnable _]
(error runnable "was rejected"))(def lp (lasync/pool {:threads 42
:thread-factory tpool
:limit 101010101
:rejected-fn log-rejected}))
```### shut it down
when you done with a pool it is a good idea to shut it down:
```clojure
(lasync/shutdown pool)
```also wait for completion of tasks (timeout in ms):
```clojure
(lasync/await-termination pool 5000)
```## license
copyright © 2021 tolitius
distributed under the Eclipse Public License, the same as Clojure.