{"id":15010363,"url":"https://github.com/fr33m0nk/structured-task-scope","last_synced_at":"2026-02-06T06:02:46.370Z","repository":{"id":220798513,"uuid":"752613387","full_name":"fr33m0nk/structured-task-scope","owner":"fr33m0nk","description":"Structured concurrency in Clojure via StructuredTaskScope","archived":false,"fork":false,"pushed_at":"2024-04-01T14:00:07.000Z","size":36,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-25T00:13:42.000Z","etag":null,"topics":["clojure","jdk21","jvm","structured-concurrency","virtual-threads"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fr33m0nk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-04T11:14:26.000Z","updated_at":"2025-03-15T17:21:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"5dec8592-1a80-4624-8a0e-36c86fc9bb1d","html_url":"https://github.com/fr33m0nk/structured-task-scope","commit_stats":null,"previous_names":["fr33m0nk/structured-task-scope"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fr33m0nk/structured-task-scope","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fr33m0nk%2Fstructured-task-scope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fr33m0nk%2Fstructured-task-scope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fr33m0nk%2Fstructured-task-scope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fr33m0nk%2Fstructured-task-scope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fr33m0nk","download_url":"https://codeload.github.com/fr33m0nk/structured-task-scope/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fr33m0nk%2Fstructured-task-scope/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29153144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T02:39:25.012Z","status":"ssl_error","status_checked_at":"2026-02-06T02:37:22.784Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","jdk21","jvm","structured-concurrency","virtual-threads"],"created_at":"2024-09-24T19:33:43.019Z","updated_at":"2026-02-06T06:02:46.344Z","avatar_url":"https://github.com/fr33m0nk.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fr33m0nk/structured-task-scope\n\n- This is an experimentation `Structued concurrency` with JDK-21 preview feature `Structured Task Scope`\n- The purpose is to arrive upon most usable abstraction for `Structured Task Scope` in Clojure\n- May be released as a library if the usability is established.\n\n## Usage\n\n### JDK-21+ is mandatory to be able to use this wrapper/library\n\nIn `deps.edn`, add following:\n```clojure\nfr33m0nk/structured-task-scope {:git/url \"https://github.com/fr33m0nk/structured-task-scope\"\n                                :sha \"0af6062d15228f13a0ba0aa22e696da195dd81aa\"}\n```\n## ScopedValues\n- ScopedValues are introduced to create differing local bindings for Threads (Virtual or Platform)\n- They differ from ThreadLocals as the extent of binding is limited to function invocation and the binding is immutable\n- This is also a good alternate to `^:dynamic` Clojure vars\n- [JEP Café #16: Java 20 - From ThreadLocal to ScopedValue](https://www.youtube.com/watch?v=fjvGzBFmyhM) is a brilliant reference\n\nIn REPL, require `fr33m0nk.scoped-value` \n```clojure\n(require '[fr33m0nk.scoped-value :as sv])\n\n(def a-scoped-value (sv/-\u003escoped-value))\n```\n### Using `a-scoped-value` for side effect\n```clojure\n(defn function-using-scope-value\n  []\n  (let [processed-string (str (sv/deref a-scoped-value) \" Ok\")]\n    (println processed-string)))\n\n(-\u003e\u003e (range 3)\n     (pmap #(future\n             (sv/run-where! a-scoped-value (str \"Hello - \" %) function-using-scope-value)))\n     (run! deref))\n```\nresult\n```clojure\n;;Obvious jumbling due to multithreading while printing to screen\nHello - 2 OkHello - 0 OkHello - 1 Ok \n```\n### Using `a-scoped-value` for functional transformation\n```clojure\n(defn function-using-scope-value\n  []\n  (str (sv/deref a-scoped-value) \" Ok\"))\n\n(-\u003e\u003e (range 3)\n     (pmap #(future\n              (sv/apply-where a-scoped-value (str \"Hello - \" %) function-using-scope-value)))\n     (mapv deref))\n```\nresult\n```clojure\n[\"Hello - 0 Ok\" \"Hello - 1 Ok\" \"Hello - 2 Ok\"]\n```\n\n## StructuredTaskScope\n\nIn REPL, require `fr33m0nk.structured-task-scope` namespace\n```clojure\n(require '[fr33m0nk.structured-task-scope :as sts])\n```\n\n### StructuredTaskScope\n```clojure\n(let [success (atom [])\n      error (atom [])\n      success-handler #(swap! success conj %)\n      failure-handler #(swap! error conj %)\n      scope (sts/-\u003estructured-scope success-handler failure-handler)]\n  (sts/fork-task scope (throw (ex-info \"boom\" {})))\n  (sts/fork-task scope (Thread/sleep 5000) :turtle-wins)\n  (sts/fork-task scope (Thread/sleep 5000) :hare-wins)\n  (.join scope)\n  {:success @success\n   :failure @error})\n```\n\nresult\n```clojure\n{:success [:hare-wins :turtle-wins],\n :failure [#error{:cause \"boom\",\n                  :data {},\n                  :via [{:type clojure.lang.ExceptionInfo,\n                         :message \"boom\",\n                         :data {},\n                         :at [user$eval2546$fn__2551 invoke \"form-init3588532553786133725.clj\" 6]}],\n                  :trace [[user$eval2546$fn__2551 invoke \"form-init3588532553786133725.clj\" 6]\n                          [clojure.lang.AFn call \"AFn.java\" 18]\n                          [java.util.concurrent.StructuredTaskScope$SubtaskImpl run \"StructuredTaskScope.java\" 889]\n                          [java.lang.VirtualThread run \"VirtualThread.java\" 309]]}]}\n```\n\nJDK-21 currently also supports following refinements of `StructuredTaskScope`:\n\n### ShutdownOnSuccess\n```clojure\n(import '(java.time Instant))\n\n(sts/with-shutdown-on-success\n  scope \n  {:deadline-instant (.plusMillis (Instant/now) 1500)} \n  (sts/fork-task scope (Thread/sleep 1000) :turtle-wins)\n  (sts/fork-task scope (Thread/sleep 5000) :hare-wins))\n```\nresult\n```clojure\n:turtle-wins\n```\n\n### ShutdownOnFailure\n```clojure\n(import '(java.time Instant))\n\n(sts/with-shutdown-on-failure\n  scope\n  ;; Below are bindings to the tasks once forked\n  ;; These bindings should only be of the forked task as\n  ;; currently the macro isn't very intelligent\n  [turtle (sts/fork-task scope (Thread/sleep 5000) :turtle-wins)\n   hare (sts/fork-task scope (Thread/sleep 5000) :hare-wins)\n   ;; forked task bindings can be used inside another forked task\n   ;; using bindings `boomer (.get hare)` here will cause `IllegalStateException`\n   ;; However, such bindings are fine in body of the macro as the body executes after `(.join scope)` \n   zoomba (sts/fork-task scope (-\u003e\u003e [turtle hare]\n                                (map #(name (.get %)))))]\n  ;; Extra options\n  {:throw-on-failure? true\n   :deadline-instant (.plusMillis (Instant/now) 7000)}\n  ;; do something with bindings after calling `.get` method on subtasks\n  (.get zoomba))\n```\nresult\n```clojure\n(\"turtle-wins\" \"hare-wins\")\n```\n\n### Additional functions/macros\n`let-fork`\nEvaluates bindings in parallel and returns the result of\nevaluating body in the context of those bindings. \nBindings have to be independent of each other\n```clojure\n(sts/let-fork [a (let [sleep-ms 5000] (println \"Sleep 1 \") (Thread/sleep sleep-ms) (println \"Awake 1\") :a)\n               b (let [sleep-ms 2000] (println \"Sleep 2 \") (Thread/sleep sleep-ms) (println \"Awake 2\") 10)]\n              {a b})\n```\nresult\n```clojure\nSleep 1\nSleep 2\nAwake 2\nAwake 1\n\n{:a 10}\n```\n## License\n\nCopyright © 2024 Prashant Sinha\n\nDistributed under the Eclipse Public License version 1.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffr33m0nk%2Fstructured-task-scope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffr33m0nk%2Fstructured-task-scope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffr33m0nk%2Fstructured-task-scope/lists"}