{"id":16504522,"url":"https://github.com/cosmos72/stmx","last_synced_at":"2026-01-28T16:02:24.725Z","repository":{"id":7515583,"uuid":"8866014","full_name":"cosmos72/stmx","owner":"cosmos72","description":"High performance Transactional Memory for Common Lisp","archived":false,"fork":false,"pushed_at":"2024-02-18T18:57:50.000Z","size":1852,"stargazers_count":249,"open_issues_count":6,"forks_count":15,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-03-02T01:13:33.630Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://stmx.org/","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cosmos72.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":null,"funding":null,"license":null,"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":"2013-03-18T22:49:07.000Z","updated_at":"2025-02-21T14:48:35.000Z","dependencies_parsed_at":"2024-01-17T00:45:53.933Z","dependency_job_id":"22df263f-3ae4-46f8-bc62-00a441a67293","html_url":"https://github.com/cosmos72/stmx","commit_stats":{"total_commits":444,"total_committers":6,"mean_commits":74.0,"dds":"0.018018018018018056","last_synced_commit":"f71e742a50b85e3abc0af9bb5f02802f218a1705"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/cosmos72/stmx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosmos72%2Fstmx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosmos72%2Fstmx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosmos72%2Fstmx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosmos72%2Fstmx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cosmos72","download_url":"https://codeload.github.com/cosmos72/stmx/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosmos72%2Fstmx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28846763,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T15:15:36.453Z","status":"ssl_error","status_checked_at":"2026-01-28T15:15:13.020Z","response_time":57,"last_error":"SSL_read: 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":[],"created_at":"2024-10-11T15:03:41.310Z","updated_at":"2026-01-28T16:02:24.707Z","avatar_url":"https://github.com/cosmos72.png","language":"Common Lisp","readme":"STMX\n======\n\nSummary\n-------\nSTMX is a high-performance implementation of composable Transactional\nMemory (TM) for Common Lisp. TM is a concurrency control mechanism aimed \nat making concurrent programming easier to write and understand.\nInstead of traditional lock-based programming, one programs with\natomic memory transactions, which can be composed together to make\nlarger atomic memory transactions.\n\nA memory transaction gets committed if it returns normally, while it gets rolled\nback if it signals an error (and the error is propagated to the caller).\n\nFinally, memory transactions can safely run in parallel in different threads,\nare re-executed from the beginning in case of conflicts or if consistent reads\ncannot be guaranteed, and their effects are not visible from other threads\nuntil they commit.\n\nMemory transactions give freedom from deadlocks, are immune to thread-safety\nbugs and race conditions, provide automatic roll-back on failure,\nand aim at resolving the tension between granularity and concurrency.\n\n\nNews\n----\n\n### Latest news, 1st March 2020\n\nFixed STMX for internal changes in SBCL 2.0.0 and SBCL 2.0.2.\nUpdated list of Intel CPUs supporting memory transactions in hardware (Intel TSX) - see below.\n\n### News, 16th January 2015\n\nVersion 2.0.1 released.\nIt adds support for transactional structs in addition to transactional CLOS objects,\nand a faster, struct-based implementation of transactional CONS cells and lists,\nincluding several list-manipulating functions - see [util/tcons.lisp](util/tcons.lisp)\nand [util/tlist.lisp](util/tlist.lisp)\n\nUnluckily, the hardware bug that prompted Intel to disable hardware transactional memory (TSX)\nin August 2014 is still there, and *very* few new models are available without the bug.\nSo for the moment STMX will be software-only on many CPUs.\n\n### Older news\n\nSee the file [NEWS.md](NEWS.md)\n\n\nGeneral documentation\n---------------------\nAn [introduction](doc/introduction.md) is available to explain more in detail\nwhat STMX is, what it is not, and how it is implemented.\n\nFor background information,\n[Composable Memory Transactions](http://research.microsoft.com/~simonpj/papers/stm/stm.pdf)\nis a very good - though a bit technical - explanation of memory transactions and\nhow they are used and combined. For the interested reader, it also goes in\ndeep detail on how to actually implement them.\n\n\nSupported systems\n-----------------\nSTMX is currently tested on the following Common Lisp implementations:\n\n* [SBCL](http://sbcl.org/)\n  * version 2.0.9       (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 2.0.6       (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 2.0.0       (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.5.4       (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.4.16      (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.3.19      (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.2.14      (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.1.15      (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.2.6       (x86)      on Debian GNU/Linux stretch  (x86_64)\n  * version 1.2.8       (armhf)    on Debian GNU/Linux wheezy   (armhf) inside Qemu\n  * version 1.1.15      (powerpc)  on Debian GNU/Linux stretch  (powerpc) inside Qemu\n  * version 1.2.7       (x86_64)   on Windows 7                 (x86_64)\n\n  Versions \u003c 1.2 have too old builtin ASDF, and must be *manually* upgraded\n  to ASDF \u003e= 3.1 to load STMX.\n\n* [ABCL](http://www.abcl.org/)\n  * version 1.7.1 with OpenJDK 11.0.8 (x86_64) on Debian GNU/Linux bullseye (x86_64)\n  \n* [CCL](http://ccl.clozure.com/)\n  * version 1.12        (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.11        (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.10        (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n  * version 1.12        (x86)      on Debian GNU/Linux bullseye (x86_64)\n  * version 1.11        (x86)      on Debian GNU/Linux bullseye (x86_64)\n  * version 1.10        (x86)      on Debian GNU/Linux bullseye (x86_64)\n  * version 1.10        (linuxarm) on Debian GNU/Linux wheezy   (armhf) inside Qemu\n  * version 1.9-r15761  (linuxppc) on Debian GNU/Linux wheezy   (powerpc) inside Qemu\n\n* [CLISP](http://www.clisp.org/)\n  * version 2.49.92     (x86_64)   on Debian GNU/Linux bullseye (x86_64)\n\n  CLISP lacks multi-threading, and its builtin ASDF must be *manually* upgraded\n  to ASDF \u003e= 3.1 to load STMX.\n  \n### Partially supported systems\n\n* [CMUCL](http://www.cons.org/cmucl/)\n  * versions \u003c= 20e should be started with the command line options \"-fpu\" \"x87\"\n  to improve STMX reliability, see [doc/supported-systems.md](doc/supported-systems.md).\n\n  Version \u003e= 20f no longer support the command line options \"-fpu\" \"x87\"\n  and STMX runs reliably only in single-thread mode.\n\n  CMUCL has too old builtin ASDF, and must be *manually* upgraded\n  to ASDF \u003e= 3.1 to load STMX.\n\n* [ECL](http://ecls.sourceforge.net/)\n  * version 16.1.2      (x86_64)   on Debian GNU/Linux stretch (x86_64)\n  * version 16.0.0      (x86_64)   on Debian GNU/Linux stretch (x86_64)\n  * version 15.2.21     (x86_64)   on Debian GNU/Linux stretch (x86_64)\n  * version 13.5.1      (x86_64)   on Debian GNU/Linux stretch (x86_64)\n\n  There are known issues running STMX on ECL, see\n  [Github issues](https://github.com/cosmos72/stmx/issues/4)\n\n  ECL versions 16.0.0 and 16.1.2 run STMX reliably enough for lightweight use,\n  but sometimes still hang in test suite.\n\n\n### Untested systems\n\nSTMX will probably work on several other Common Lisp implementations as long as\nthey support log4cl, closer-mop, bordeaux-threads and trivial-garbage,\nbut the author gives no guarantees.\n\n\nInstallation and loading\n------------------------\n\n### Stable version - from [Quicklisp](http://www.quicklisp.org)\n\nSTMX is available from Quicklisp. The simplest way to obtain it is to first install\n[Quicklisp](http://www.quicklisp.org) then run these commands from REPL:\n\n    CL-USER\u003e (ql:quickload \"stmx\")\n    ;; lots of output...\n    CL-USER\u003e (use-package :stmx)\n     \nIf all goes well, this will automatically download and install the\n**stable** branch of STMX and its dependencies:\n\n- `log4cl`\n- `closer-mop`\n- `bordeaux-threads`\n- `trivial-garbage`\n\n### Latest version - from [GitHub](https://github.com/cosmos72/stmx)\n\nIn case you want to use the \"latest and greatest\" version directly\nfrom the author, in order to get the newest features - most notably\nhardware memory transactions - improvements, bug fixes, and\noccasionally new bugs, you need to download it into your Quicklisp\nlocal-projects folder. Open a shell and run the commands:\n\n    $ cd ~/quicklisp/local-projects\n    $ git clone git://github.com/cosmos72/stmx.git\n\nthen proceed as before - load a REPL and run:\n\n    CL-USER\u003e (ql:quickload \"stmx\")\n    ;; lots of output...\n    CL-USER\u003e (use-package :stmx)\n     \nIf all goes well, it will automatically load STMX and its dependencies.\n\nNote: unless you know what you are doing, do not try to load different\nSTMX versions one after the other from the same REPL - strange things\nmay happen.\n\n### Other versions - from [Sourceforge](http://sourceforge.net/projects/stmx/)\n\nAll the stable versions of STMX, present and past, are also available from\n[Sourceforge](http://sourceforge.net/projects/stmx/files/stmx/), including\nversion 1.9.0.\n\n### Troubleshooting\n\nIn case you get errors:\n\n- check that Quicklisp is installed correctly, for example by\n  executing at REPL:\n\n        CL-USER\u003e (ql:quickload \"closer-mop\")\n\n- if you tried to download the stable version from Quicklisp,\n  check that your quicklisp is updated and knows about STMX:\n  \n        CL-USER\u003e (ql:system-apropos \"stmx\")\n        \n  should print something like\n  \n        #\u003cSYSTEM stmx / stmx-stable-7e68763b-git / quicklisp 2013-06-15\u003e\n        #\u003cSYSTEM stmx.test / stmx-stable-7e68763b-git / quicklisp 2013-06-15\u003e\n     \n  If it doesn't, you need to update Quicklisp as described\n  [here](http://www.quicklisp.org/beta) - search for \"To get updated\n  software\" in the page.\n  \n- if you tried to download the latest version from GIT,\n  check that you downloaded STMX creating an `stmx/` folder inside\n  your Quicklisp local-projects folder, usually `~/quicklisp/local-projects`\n\n\n### Testing that it works\n\nAfter loading STMX for the first time, it is recommended to run the\ntest suite to check that everything works as expected. From the REPL,\nrun:\n\n    CL-USER\u003e (ql:quickload \"stmx.test\")\n    ;; lots of output...\n    CL-USER\u003e (fiveam:run! 'stmx.test:suite)\n    ;; even more output...\n     Did 7133 checks.\n        Pass: 7133 (100%)\n        Skip: 0 ( 0%)\n        Fail: 0 ( 0%)\n        \nNote: `(ql:quickload \"stmx.test\")` intentionally works only **after**\n`(ql:quickload \"stmx\")` has completed successfuly.\n\nThe test suite should report zero Skip and zero Fail; the number of Pass may vary.\nYou are welcome to report any failure you get while running the test suite,\nplease include in the report:\n- operating system name and version (example: Debian GNU/Linux x86_64 version 7.0)\n- Common Lisp implementation and version (example: SBCL 1.0.57.0.debian, x86_64)\n- EXACT output produced by the test suite\n- any other relevant information\n\nSee \"Contacts, help, discussion\" below for the preferred method to send the report.\n\n\nBasic usage\n-----------\n\nSTMX offers the following Lisp macros and functions, also heavily documented\nin the sources - remember `(describe 'some-symbol)` at REPL.\n\n- `TRANSACTIONAL` declares that a class or struct is transactional, i.e. that its\n  slots contain transactional data. Use it to wrap a class or a struct definition:\n  \n        (transactional\n          (defclass foo ()\n            ((value1 :type integer :initarg :value1 :initform 0)\n             (value2 :type string  :initarg :value2 :initform \"\"))))\n\n        (transactional\n          (defstruct bar ()\n            (value1 0  :type integer)\n            (value2 \"\" :type string)))\n\n  If you want to declare a slot as non-transactional, for example because it is\n  immutable, add the option `:transactional nil`:\n\n        (transactional\n          (defclass tred-black-tree ()\n            ((root           :type t)\n             (key-comparator :type function :transactional nil))))\n\n  Note: on all Common Lisp implementations listed above, CLOS slot accessors\n  have been tested to work correctly, i.e. they honour the transactional machinery\n  (implemented with MOP slot-value-using-class) and return the same values\n  as SLOT-VALUE.\n  \n  In the past, some cases were found - and fixed - where slot accessors would not\n  work correctly on transactional classes. It is *strongly* recommended to run\n  the test suite when using STMX for the first time on a system: it will also check\n  that accessors work on transactional classes.\n  \n  Support for `(TRANSACTIONAL (DEFSTRUCT ...))` was added in STMX version 2.0.1\n  on January 2015. Previously, only `(TRANSACTIONAL (DEFCLASS ...))` was supported.\n\n\n- `ATOMIC` is the main macro: it wraps Lisp forms into an atomic\n   memory transaction then executes them. For example, defining\n\n        (defun show-foo (obj)\n          (declare (type foo obj))\n          (multiple-value-bind (value1 value2)\n              (atomic\n                (values (slot-value obj 'value1)\n                        (slot-value obj 'value2)))\n            (format t \"atomic function show-foo: foo contains ~S, ~S~%\"\n                    value1 value2)))\n      \n        (defmethod set-foo ((obj foo) value1 value2)\n          (declare (type integer value1)\n                   (type string value2))\n          (atomic\n            (setf (slot-value obj 'value1) value1)\n            (setf (slot-value obj 'value2) value2))\n          (format t \"atomic method set-foo: foo now contains ~S, S~%\"\n                  value1 value2))\n      \n  SHOW-FOO will atomically read the slots VALUE1 and VALUE2 of a FOO\n  instance, then print both. Note that `(format t ...)` is **outside** the atomic\n  block - more on this later.\n\n  SET-FOO will atomically set the slots VALUE1 and VALUE2 of a FOO\n  instance.\n\n  Using these two functions, STMX guarantees that multiple threads accessing\n  the same FOO instance will always see consistent values for both slots,\n  i.e. SHOW-FOO will **never** see intermediate states of a transaction,\n  where for example one slot has been updated by SET-FOO, but the other slot\n  has not been updated yet.\n\n  This is the main feature of STMX: if an atomic block completes normally,\n  it is assumed to be successful and it gets committed: all its writes\n  to transactional memory become visible simultaneously to other threads.\n  If instead an atomic block exits with a non-local control transfer\n  (signals an error, throws, or invokes a `(go some-label)`), it is assumed\n  to be failed and it gets rolled back: all its writes to transactional memory\n  are discarded.\n\n  Warning: in order to avoid deadlocks and conflicts while still reaching good\n  performance, STMX may execute more than once the contents of an atomic block.\n  Also, some instructions as `(retry)` described below, explicitly cause an\n  atomic block to be re-executed from the beginning. For this reasons,\n  atomic blocks should **not** contain irreversible operations such as\n  input/output. More details in the paragraph INPUT/OUTPUT DURING TRANSACTIONS\n  below.\n\n  Note: new threads **must** be created with `(bordeaux-threads:make-thread)`\n  in order to establish thread-local bindings needed by STMX. A safety check\n  that detects missing thread-local bindings has been recently added to STMX.\n  \n  Note: STMX allows using transactional data both inside and outside atomic\n  blocks, but be aware that accessing transactional data from outside\n  atomic transactions is only intended for **debugging** purposes at the REPL:\n  in a program it can cause a lot of problems, due to inconsistencies and due to\n  other threads not being notified when a transactional memory location is\n  updated.\n  Future versions may remove this convenience hack and replace it with a cleaner,\n  stricter mechanism.\n  In a program, **always** make sure that all code that accesses transactional data\n  is directly or indirectly executed inside an `(atomic ...)` block.\n\n- `TRANSACTION` declares that a method or function is an atomic memory\n  transaction, and is actually just a macro that wraps the body of a function\n  or method in an `(atomic ...)` block.\n  In the past, it was suggested as a more convenient alternative to `ATOMIC`,\n  but for various stylistic reasons the current recommendation is to avoid it.\n  The main reason is that it encourages performing too many operations inside\n  an atomic block, including irreversible ones as input/output, which has\n  impredictable behaviour and should be **really** avoided. Examples:\n\n        (transaction\n          (defun get-foo-values (obj)\n            (declare (type foo obj))\n            (values\n              (value1-of obj) (value2-of obj))))\n      \n        (transaction\n          (defmethod set-foo-values ((obj foo) value1 value2)\n            (declare (type integer value1)\n                     (type string value2))\n            (setf (value1-of obj) value1)\n            (setf (value2-of obj) value2)\n            obj))\n\n- Composing transactions\n\n  A key feature of `ATOMIC` is its composability:\n  smaller transactions can be composed to create larger transactions.\n  For example, the following three program fragments are perfectly equivalent:\n\n  1) use `(atomic ...)` to wrap into a single transaction many smaller `(atomic ...)` blocks\n\n        (defmethod swap-value1-of ((x foo) (y foo))\n          (format t \"swapping value1 of ~S and ~S~%\" x y)\n          (atomic\n            (rotatef (slot-value x 'value1) (slot-value y 'value1))))\n\n        (defmethod swap-value2-of ((x foo) (y foo))\n          (format t \"swapping value2 of ~S and ~S~%\" x y)\n          (atomic\n            (rotatef (slot-value x 'value2) (slot-value y 'value2))))\n\n        (defmethod swap-contents ((x foo) (y foo))\n          (atomic\n            (swap-value1-of x y)\n            (swap-value2-of x y)))\n\n  2) write redundant `(atomic ...)` blocks\n\n        (defmethod swap-contents ((x foo) (y foo))\n          (format t \"swapping value1 and value2 of ~S and ~S~%\" x y)\n          (atomic\n            (atomic\n              (rotatef (slot-value x 'value1) (slot-value y 'value1)))\n            (atomic\n              (rotatef (slot-value x 'value2) (slot-value y 'value2)))))\n\n  3) write a single `(atomic ...)` block\n\n        (defmethod swap-contents ((x foo) (y foo))\n          (format t \"swapping value1 and value2 of ~S and ~S~%\" x y)\n          (atomic\n            (rotatef (slot-value x 'value1) (slot-value y 'value1))\n            (rotatef (slot-value x 'value2) (slot-value y 'value2))))\n\n  This composability property has an important consequence: transactional code,\n  possibly written by different people for unrelated purposes, can be combined\n  into larger transactions without modifying it - actually, without looking at\n  the source code at all - as long as it all uses the same transactional library.\n\n  The STMX machinery will guarantee that transactions intermediate status, where\n  an atomic block is half-way through its job, will **never** be visible to other\n  transactions.\n\n  For example, it becomes trivial to write some code that atomically removes\n  an object from a transactional container and adds it to another one:\n  just write something like\n\n        (defmethod move-obj-from-a-to-b ((a some-container) (b another-container))\n          (atomic\n            (let ((obj (take-obj-from-some-container a)))\n               (put-obj-into-another-container obj b))))\n\n  and it will work as long as both container are transactional and use the same\n  transaction library (in this case, STMX).\n\n  A lot of facts that in other concurrent programming paradigms can be\n  great obstacles to such a solution become completely irrelevant\n  when using transactions:\n  it is irrelevant that the two containers may be unrelated classes,\n  that the respective authors may not have anticipated such need in the APIs,\n  that the internal details of the two implementations may be unknown to the\n  author of code that combines them atomically (the `move-obj-from-a-to-b`\n  in the example),\n  that other existing code in the program uses the same containers `a` and `b`\n  but does not cooperate with `move-obj-from-a-to-b`.\n\n  Style suggestion: in order to guarantee that all transactional memory accesses\n  are performed inside an atomic block, it may be tempting to wrap each function\n  or method body inside `(atomic ...)`. While safe and correct, this approach\n  has a small performance penalty that performance-critical code may want to\n  avoid by minimizing the number of `(atomic ...)` blocks: it is enough to have\n  a top-level atomic block that corresponds to the largest transaction that one\n  wants to execute, and omit inner atomic blocks in the same or other functions\n  called directly or indirectly from the top-level atomic block. In such case,\n  it is strongly recommended to insert in the documentation of the functions\n  accessing transactional memory without a direct atomic block a sentence like\n  \"This function should be always invoked from inside an STMX atomic block.\"\n\n- `RETRY` is a function. It is more tricky to understand, but really powerful.\n  As described in the summary, transactions will commit if they return normally,\n  while they will rollback if they signal an error or condition.\n\n  The `(retry)` function call offers a third option: if invoked inside\n  a transaction, it tells STMX that the transaction cannot complete\n  immediately, for example because some necessary data is not currently\n  available, and instructs STMX to wait until the data has changed,\n  then re-execute the transaction from scratch.\n\n  How does `(retry)` know which data it should monitor for changes?\n  Simple: it will monitor *all* transactional data (including slots of\n  transactional objects) that was read since the beginning of the\n  transaction and until `(retry)` was invoked. \n\n  With `RETRY`, reliable communication among threads is (hopefully)\n  extremely simple to implement: a thread can read one (or more)\n  transactional data, checking for values that some other thread\n  will write there, and just `(retry)` if no appropriate values are\n  there yet.\n\n- `ORELSE` is a macro to execute two or more Lisp forms as alternatives\n  in separate, nested transactions: if the first retries or detects an\n  inconsistent read, the second will be executed and so on, until one\n  transaction either commits (returns normally) or rollbacks (signals\n  an error or condition). It can only be used inside a transaction.\n\n- `NONBLOCKING` is an utility macro based on `ORELSE` to convert a blocking\n  transaction into another that returns NIL instead of waiting\n  (and otherwise returns T followed by the values or the original transaction)\n  \n        (nonblocking (x) (y) (z))\n        \n  basically expands to\n  \n        (orelse (values t (progn (x) (y) (z))) nil)\n        \n  with the difference that `(nonblocking ...)` actually captures all the values\n  returned by the transaction, not just the first as in the example above.\n\n\nInput/Output during transactions\n--------------------------------\n**WARNING:** since transactions will be re-executed in case of conflicts with\nothers and can also rollback or retry, all code inside an atomic block\nmay be executed more times than expected, or may be executed when **not** expected.\n\nSome transactional memory implementations, especially for statically-typed\nlanguages, forbid performing input/output during a transaction on the ground\nthat I/O is not transactional: if a transaction sends an irreversible command\nto the outside world, there is no way to undo it in case the transaction\nrolls back, retries or conflicts.\n\nSTMX does not implement such restrictions, i.e. I/O and any other irreversible\naction can also be performed inside an atomic block.\nThis means you are free to launch missiles during a transaction, and destroy\nthe world when you shouldn't have. **You have been warned.**\n\nDespite the risk, there are at least two reasons for such a design choice:\n* Forbidding I/O operations inside transactions, if done at all, should be done\n  while compiling a program rather than while running it.\n  In Common Lisp, neither of the two seems easy to implement.\n* Common Lisp programs are often much more dynamic and flexible than programs\n  in other languages, and programmers are trusted to know what they are doing.\n  Such a prohibition does not seem to fit well with this spirit.\n\nThe typical solution for the above risk is: during a transaction, perform I/O\n**only** for debugging purposes, for example using a logging library as log4cl\n(or whatever is appropriate for your program), and queue any I/O operation\nin a transactional buffer. Then, invoke a separate function that first runs\na transaction to atomically consume the buffer and only later,\n**outside** any transaction, performs the actual I/O operation.\n\nAn alternative solution is: during a transaction, instead of performing I/O\npass to `AFTER-COMMIT` a function that will perform I/O when executed.\nNote: `AFTER-COMMIT` is described in Advanced usage below, read it carefully\nbecause functions executed by `AFTER-COMMIT` have several restrictions on what\nthey are allowed to do.\n\nAdvanced usage\n--------------\n\nFor those cases where the basic features are not sufficient, or where more\ncontrol is desired during the execution of transactional code, some advanced\nfeatures are available:\n\n- `RUN-ATOMIC` is the function version of `ATOMIC`: takes a single function\n  argument and executes it in a transaction. This means the following two code\n  snippets are equivalent:\n\n        (defvar a (make-instance 'foo))\n        (defvar b (make-instance 'foo))\n        (atomic\n          (set-foo a 1 \"abc\")\n          (set-foo b 2 \"def\"))\n\n  and\n\n        (defvar a (make-instance 'foo))\n        (defvar b (make-instance 'foo))\n\n        (defun init-foo-a-and-b ()\n          (set-foo a 1 \"abc\")\n          (set-foo b 2 \"def\"))\n\n        (run-atomic #'init-foo-a-and-b)\n\n- `RUN-ORELSE` is the function version of `ORELSE`: it accepts any number\n  of functions and executes them as alternatives in separate, nested\n  transactions:\n  if the first retries or is invalid, the second will be executed and so on,\n  until one function either commits (returns normally) or rollbacks\n  (signals an error or condition).\n\n  If X, Y and Z are no-argument functions, the following two lines are\n  equivalent:\n  \n        (orelse (x) (y) (z))\n        (run-orelse #'x #'y #'z)\n\n- `BEFORE-COMMIT` is a macro that registers Lisp forms to be executed later,\n  just before the transaction tries to commit.\n  It can be useful to normalize or simplify some transactional data, or perform\n  any kind of bookkeeping activity. \n\n  Be aware that the transaction is not yet committed when the forms registered\n  with BEFORE-COMMIT run. This means in particular:\n\n  - There is no guarantee that the commit will succeed.\n\n  - If the forms signal an error when executed, the error is propagated to the\n    caller, forms registered later with BEFORE-COMMIT are not executed, and the\n    transaction rolls back.\n\n  - The forms can read and write normally to transactional memory,\n    and in case of conflicts the whole transaction, _including_ all forms\n    registered with BEFORE-COMMIT, is re-executed from the beginning.\n\n  - The forms cannot (retry) - attempts to do so will signal an error.\n    Starting a nested transaction and retrying inside that is acceptable,\n    as long as the (retry) does not propagate outside the forms themselves.\n\n- `AFTER-COMMIT` is another macro that registers Lisp forms to be executed\n  later, but in this case they are executed immediately after the\n  transaction has been successfully committed.\n  It can be useful to notify some subsystem that for any reason cannot call\n  `(retry)` to be informed of changes in transactional memory - for example\n  because it is some existing code that one does not wish to modify.\n\n  In this case, the transaction is already committed when the forms registered\n  with AFTER-COMMIT run, and (since STMX 1.3.2) the forms are executed *outside*\n  any transaction. There are some limitations on what the forms can do:\n\n  - If the forms signal an error when executed, the error is propagated to the\n    caller, forms registered later with AFTER-COMMIT are not executed, but the\n    transaction remains committed.\n\n  - The forms are not executed inside a transaction: while it is certainly\n    possible to explicitly run an `(atomic)` block from them, doing so would\n    probably defeat the purpose of AFTER-COMMIT and it may also cause\n    a significant performance penalty.\n\n- `CALL-BEFORE-COMMIT` is the function version of `BEFORE-COMMIT`: it accepts a\n   single function and registers it to be executed before the transaction tries\n   to commit.\n\n- `CALL-AFTER-COMMIT` is the function version of `AFTER-COMMIT`: it accepts a\n   single function and registers it to be executed after the transaction\n   has been successfully committed.\n\n- `TVAR` is the class implementing transactional memory behind the scenes.\n   It is used internally by slots of transactional classes, but can also be used\n   directly. Except if specified, all its functions and methods work both inside\n   and outside transactions (remember that using transactional memory outside\n   transactions is only intended for **debugging** purposes). Functions and\n   methods:\n   - `(tvar [initial-value])` Create a new TVAR, optionally bound to a value.\n   - `($-slot var)` Get the value of VAR. Signals an error if VAR is not bound\n      to any value. Note: before STMX 1.9.0, this function was named `($ var)`.\n   - `(setf ($-slot var) value)` Store VALUE into VAR.\n      Note: before STMX 1.9.0, this function was named `(setf ($ var) value)`.\n   - `(bound-$? var)` Return true if VAR is bound to some value.\n   - `(unbind-$ var)` Unbind VAR from its value.\n   - `(value-of var)` getter method, equivalent to `($-slot var)`\n   - `(setf (value-of var) value)` setter method, equivalent to\n      `(setf ($-slot var) value)`\n\n   For programmers that want to squeeze the last CPU cycle out of STMX, there\n   are also some more specialized functions:\n   - `($ var)` Get the value of VAR. Return `+unbound-tvar+` if VAR is not\n     bound to any value.\n   - `(setf ($ var) value)` Set the value of VAR. Identical to `(setf ($-slot var) value)`\n     and provided for simmetry with `($ var)`.\n\nHardware transactions\n---------------------\n\nSTMX versions 1.9.0 or later can take advantage of hardware transactions\non Intel CPUs that support Transactional Synchronization Extensions (TSX)\nAs of February 2020, many recent consumer and server Intel CPUs support them,\nincluding at least:\n\n-  7th generation Core i5: 7500 7500T 7600  7600K 7600T 7Y57\n-  8th generation Core i5: 8500 8500T 8600  8600T 8600K\n-  9th generation Core i5: 9500 9500E 9500F 9500T 9500TE 9600 9600K 9600KF 9600T\n- 10th generation Core i7: -\n\n-  7th generation Core i7: 7600U 7660U 7700  7700K  7700T 7820EQ 7820HK 7820HQ 7920HQ 7Y75\n-  8th generation Core i7: 8086K 8650U 8665U 8665UE +8700 8700   8700B  8700K  8700T  8706G 8850H\n-  9th generation Core i7: 9700  9700E 9700F 9700K 9700KF 9700T  9700TE 9850H  9850HE 9850HL\n- 10th generation Core i7: -\n\n-  8th generation Core i9: 8950HK\n-  9th generation Core i9: 9880H 9900 9900K 9900KF 9900KS 9900T 9980HK\n\n(This list is necessarily incomplete. To check whether a specific Intel CPU\nsupports Transactional Synchronization Extensions (TSX) browse https://ark.intel.com/)\n\nTo actually use hardware transactions from STMX, there are two more requirements:\n\n- a 64-bit version of SBCL - any version \u003e= 1.2.0 should work\n- a 64-bit unix-like operating system - at the moment only Linux x86_64 is tested\n\nAlso, hardware transactions only work in compiled code - SBCL sometimes\ninterprets very short functions and simple code executed at REPL\ninstead of compiling them, which may cause hardware transactions to fail.\n\n\n### How to tell if hardware transactions are supported\n\nThere are several ways. The easiest are:\n\n- From **outside** transactions, run the macro `(HW-TRANSACTION-SUPPORTED?)`.\n  It internally calls the CPUID assembler instruction and returns T if hardware\n  transactions are supported, or NIL if they are not.\n- Try to use them, for example by executing `(ATOMIC (HW-TRANSACTION-SUPPORTED-AND-RUNNING?))`\n  in compiled code - hardware transactions typically do not work in interpreted code.\n  Thus actually execute something like\n  `(DEFUN HW-TRANSACTION-TEST ()\n     (ATOMIC (HW-TRANSACTION-SUPPORTED-AND-RUNNING?)))\n   (HW-TRANSACTION-TEST)`\n\n### How to use hardware transactions\n\nSTMX automatically uses hardware transactions if they are supported.\nThere is no need for special commands, just execute the usual `(ATOMIC ...)`\nor `(RUN-ATOMIC ...)` forms.\n\nHardware transactions have several limitations, and STMX will seamlessly switch\nto (slower) software transactions in the following cases:\n\n- hardware limits are exceeded, for example read-set or write-set are\n  larger than CPU L1 cache\n\n- executing a function or macro not supported by hardware transactions.\n  The list is subject to change, it currently includes:\n  * STMX functions and macros: RETRY, ORELSE, RUN-ORELSE, BEFORE-COMMIT, AFTER-COMMIT,\n    CALL-BEFORE-COMMIT, CALL-AFTER-COMMIT\n  * any Common Lisp function or macro that signals an error, or allocates\n    non-trivial amounts of memory, or performs any kind of system calls,\n    including input/output, sleeping and context switching.\n\n- executing a CPU instruction not allowed inside hardware transaction.\n  In particular, Intel TSX guarantees that CPU instructions\n  * CPUID, PAUSE, XABORT\n\n  will always abort a hardware transaction, but many other CPU instructions\n  typically have the same effect, including possibly:\n  \n  * Calls to the operating system and returns from it:\n    SYSENTER, SYSCALL, SYSEXIT, SYSRET.\n  * Interrupts: INT n, INTO.\n  * Input/Output: IN, INS, REP INS, OUT, OUTS, REP OUTS and their variants.\n  * All X87 and MMX instructions. On the opposite, XMM and YMM registers\n    and the MXCSR register **can** be used inside a hardware transaction.\n  * CLI, STI, POPFD, POPFQ, CLTS.\n  * Instructions that update segment registers, debug registers and/or control\n    registers such as DF (CLD and STD instructions), DS/ES/FS/GS/SS and\n    CR0/CR2/CR3/CR4/CR8.\n  * TLB and Cacheability control: CLFLUSH, INVD, WBINVD, INVLPG, INVPCID, and\n    memory instructions with a non-temporal hint (MOVNTDQA, MOVNTDQ,\n    MOVNTI, MOVNTPD, MOVNTPS, and MOVNTQ).\n  * Processor state save: XSAVE, XSAVEOPT, and XRSTOR.\n  * VMX instructions: VMPTRLD, VMPTRST, VMCLEAR, VMREAD, VMWRITE, VMCALL, VMLAUNCH,\n    VMRESUME, VMXOFF, VMXON, INVEPT, and INVVPID.\n  * SMX instructions: GETSEC.\n  * Miscellaneous: UD2, RSM, RDMSR, WRMSR, HLT, MONITOR, MWAIT, XSETBV, VZEROUPPER,\n    MASKMOVQ, and V/MASKMOVDQU.\n\n  For details and up-to-date information, see Intel Instruction Set Programming\n  Reference, Chapter \"Transactional Synchronization Extensions\".\n\n\nUtilities and examples\n---------------------\n\nSee the [example](example) and [util](util) folder, which contains several\nexamples and utilities built with STMX and should be relatively straightforward\nto understand. The folder [util](util) contains the following classes with\nrelated methods and functions, all in the STMX.UTIL package - for more details,\nuse `(describe 'some-symbol)` at REPL:\n\n- `TCELL` is the simplest transactional class. It is created with\n  `(tcell [initial-value])` and it can be empty or hold a\n  single value.\n\n  Methods: `FULL?` `EMPTY?` `EMPTY!` `PEEK` `TAKE` `PUT` `TRY-TAKE` `TRY-PUT`.\n\n  When empty, taking a value will (retry) and wait until some other thread\n  puts a value.\n  \n  When full, putting a value will (retry) and wait until some other thread\n  removes the current value.\n\n  Note: raw TVARs support exactly the same methods.\n\n- `TCONS` is a transactional cons cell. It is created with\n  `(tcons first-value second-value)`.\n\n  Functions: `TFIRST` `(SETF TFIRST)` `TREST` `(SETF TREST)`.\n\n  Seldom used directly.\n\n- `TLIST` is a transactional list, composed of `TCONS` cells. It is created with\n  `(tlist [values ...])`.\n\n  Functions: `TFIRST` `(SETF TFIRST)` `TREST` `(SETF TREST)` `TPUSH` `TPOP`\n  `TSECOND` `TTHIRD` `TFOURTH` `TNTH` `TLAST` `TLIST-LENGTH` `TLIST*` and many others.\n  See [util/tlist.lisp](util/tlist.lisp) for details.\n\n  Normal lists are perfectly suitable for transactional use as long as\n  they are not destructively modified, so TLIST is often unnecessary:\n  it becomes needed only to support transactional destructive modifications.\n\n- `TSTACK` is a transactional first-in-last-out buffer. It is created with\n  `(tstack)` and it can be empty or hold unlimited values.\n\n  Methods: `FULL?` `EMPTY?` `EMPTY!` `PEEK` `TAKE` `PUT` `TRY-TAKE` `TRY-PUT`.\n\n  All methods append or remove values from the end, and putting a value\n  always succeeds, even when other values are already present: the new\n  value is simple appended at the end.\n  For the rest, the methods behave as described for the `TCELL` class.\n\n- `TFIFO` is a transactional first-in-first-out buffer. It is created with\n  `(make-instance 'tfifo)` and it can be empty or hold unlimited values.\n\n  Methods: `FULL?` `EMPTY?` `EMPTY!` `PEEK` `TAKE` `PUT` `TRY-TAKE` `TRY-PUT`.\n\n  `PUT` and `TRY-PUT` append values at the end, `PEEK` `TAKE` and `TRY-TAKE`\n  get or remove them from the beginning, shifting the remaining values.\n  For the rest, the methods behave as described for the `TCELL` and `TSTACK`\n  classes.\n\n- `TCHANNEL` is a transactional multicast channel. It is created with\n  `(make-instance 'tchannel)`, can contain unlimited values and it is write-only.\n  To read from it, create a `TPORT` as described below.\n\n  Methods: `FULL?` `EMPTY?` `PUT` `TRY-PUT`.\n\n  `PUT` and `TRY-PUT` append values at the end, making them available to\n  connected ports.\n  `FULL?` always returns nil, since a channel can contain unlimited values.\n  `EMPTY?` always returns t, since it is not possible to get values from a\n  channel.\n\n  It is possible to write into the same channel from multiple threads: added\n  elements will be interleaved and made available to all connected ports.\n\n- `TPORT` is a transactional reader for `TCHANNEL`. It is created with\n  `(make-instance 'tport :channel some-channel)`.\n  Ports do not support putting values, they are used to retrieve values from the\n  channel they are connected to.\n\n  Methods: `FULL?` `EMPTY?` `EMPTY!` `PEEK` `TAKE` `TRY-TAKE`.\n\n  `PEEK` `TAKE` and `TRY-TAKE` get or consume values previously added to the\n  connected channel. All ports connected to the same channel receive all the\n  values in the same order, and they consume values independently: taking a\n  value from a port does not consume it from the other ports.\n\n  `FULL?` always returns t, since it is not possible to put values in a port.\n  `EMPTY?` returns t if some values are available to read or consume.\n  `EMPTY!` consumes all values currently available.\n\n  It is also possible to use the same port from multiple threads: elements\n  consumed by one thread will not be available to other threads using the same\n  port.\n\n- `THASH-TABLE` is a transactional hash table.\n  It is created with\n  `(make-instance 'thash-table [:test 'some-test-function] [:hash 'some-hash-function])`.\n\n  One difference from standard Common Lisp HASH-TABLE:\n  - a hash function can be specified explicitly with `:hash 'some-hash-function`\n  For the usual test functions, i.e. `'eq` `'eql` `'equal` and `'equalp` the hash function\n  can be omitted and a safe default (usually `'sxhash`) will be used.\n  For other test functions, the hash function becomes mandatory.\n\n  Methods: `GHASH-TABLE-COUNT` `GHASH-TABLE-COUNT\u003e` `GHASH-TABLE-COUNT\u003c=`\n           `GHASH-TABLE-EMPTY?` `CLEAR-GHASH`\n           `GET-GHASH` `(SETF GET-GHASH)` `SET-GHASH` `REM-GHASH` \n           `MAP-GHASH` `DO-GHASH` `COPY-GHASH`\n           `GHASH-KEYS` `GHASH-VALUES` `GHASH-PAIRS`\n           `GHASH-TEST` `GHASH-HASH`.\n\n  Warning: retrieving the number of elements in a transactional container is potentially expensive:\n  to maintain consistency, it inhibits concurrent insertion and removal from other threads.\n  For this reason, use `GHASH-TABLE-COUNT` sparingly.\n\n  THASH-TABLE constructor arguments `test` and `hash` changed in STMX 2.0.0.\n  They now must be function names (i.e. symbols), previously they were actual functions.\n\n- `TMAP` is a transactional sorted map, backed by a red-black tree.\n  It is created with `(make-instance 'tmap :pred compare-function)`\n  where COMPARE-FUNCTION must be the name of a function accepting two arguments,\n  KEY1 and KEY2, and returning t if KEY1 is smaller that KEY2.\n  For numeric keys, typical COMPARE-FUNCTIONs are `'\u003c` or `'\u003e` and the faster\n  `'fixnum\u003c` or `'fixnum\u003e`.\n  For string keys, typical COMPARE-FUNCTIONs are `'string\u003c` and `'string\u003e`.\n\n  Note: COMPARE-FUNCTIONs changed in STMX 2.0.0. They now must be function names\n  (i.e. symbols), previously they were actual functions.\n\n  Methods: `GMAP-PRED` `GMAP-COUNT` `GMAP-EMPTY?` `CLEAR-GMAP`\n           `GET-GMAP` `(SETF GET-GMAP)` `SET-GMAP` `REM-GMAP` \n           `MIN-GMAP` `MAX-GMAP` `MAP-GMAP` `DO-GMAP`\n           `GMAP-KEYS` `GMAP-VALUES` `GMAP-PAIRS`.\n\n  Warning: retrieving the number of elements in a transactional container is potentially expensive:\n  to maintain consistency, it inhibits concurrent insertion and removal from other threads.\n  For this reason, use `GMAP-COUNT` sparingly.\n\n- `GHASH-TABLE` is the non-transactional version of `THASH-TABLE`. Not so\n  interesting by itself, as Common Lisp offers a standard (and usually faster)\n  HASH-TABLE implementation. It supports exactly the same methods as `THASH-TABLE`.\n\n- `RBMAP` is the non-transactional version of `TMAP`. Not so interesting by\n  itself, as many other red-black trees implementations exist already on the\n  net. It supports exactly the same methods as `TMAP`.\n\n\nPerformance\n-----------\n\nSTMX automatically discovers and takes advantage of many optional,\nnon-standard features of the underlying Common Lisp compiler.\nIt also performs graceful degradation, i.e. if the fastest version\nof a feature is not available it automatically switches to a slower,\navailable alternative.\n\nDepending on the available features, STMX performance can vary up to a factor 100\nor more (!).\n\nTo reach its peak performance, several requirements need to be satisfied by the\nhardware and by the Lisp compiler being used.\nThey are listed here in order of importance:\n\nHardware requirements:\n\n- support hardware transactions (Intel TSX). Without them, STMX is at least\n  4-5 times slower. Or, if you prefer since Intel TSX is currently very rare,\n  **with** it STMX is at least 4-5 times faster. As of August 2013,\n  STMX can use hardware transactions only on 64-bit SBCL.\n\nLisp compiler requirements:\n\n1. it must have good multi-threading support. Without it, what would you need\n   a concurrency library as STMX for?\n2. it must expose atomic compare-and-swap operations, to implement fast mutexes.\n   A much slower alternative, but still better than nothing, is to expose\n   a function that returns which thread has acquired a bordeaux-threads lock.\n3. it must produce fast, highly optimized code.\n4. it must be 64-bit. 32-bit is much slower because transactional memory\n   version counters are then BIGNUMs instead of FIXNUMs.  \n5. it must expose memory barrier operations. This is less important on x86 and\n   x86-64, and more important on unordered architectures (almost all others).\n\nAmong the non-commercial Lisp compilers, SBCL is the only one known to STMX\nauthor that satisfies all the compiler requirements, and (guess why) the only\none where STMX author has implemented support for hardware transactions.\n\nActually, all the other tested free Lisp compilers (ABCL, CCL, CMUCL, ECL)\nare at least somewhat lacking in the area \"fast, highly optimized code\",\nand none of them offers atomic compare-and-swap or memory barrier operations at all.\nOne - CMUCL - produces relatively fast code, but does not support native threads.\nSTMX is not tested on any commercial Lisp compiler, so performance on them\nis simply unknown.\n\nFor these reasons, STMX will reach the highest known performance on SBCL by a\nlarge margin - possibly by a factor from 10 to 100 or more with respect to\nother tested systems.\n\nFor more performance considerations and a lot of raw numbers produced by running micro-benchmarks,\nsee the included files [doc/benchmark.md](doc/benchmark.md), [doc/benchmark-abcl.md](doc/benchmark-abcl.md),\n[doc/benchmark-ccl64.md](doc/benchmark-ccl64.md) and [doc/benchmark-cmucl.md](doc/benchmark-cmucl.md).\n\nThe short version is: as of March 2015, on a fast consumer PC (Core i7 4770 @ 3.5GHz\nor better) with 64-bit SBCL 1.1.9 or better, STMX can execute more than 35 millions\n**hardware** transactions per second per CPU core, and more than 7 millions\n**software** transactions per second per CPU core.\nThe second platform in terms of performance is CCL (x86_64),\nthat reaches 1.1 millions software transactions per second per CPU core\nusing two threads, but STMX performance quickly decreases with more threads\n(reason still needs to be investigated).\n\nA small example with very short transactions is the [dining philosophers](example/dining-philosophers.stmx.lisp),\nwith 5 reads and 5 writes to transactional memory per atomic block,\nwhere each CPU core runs approximately 4.5 millions software transactions\nper second - hyperthreading has very limited effects.\n\nObviously, performance in other usage scenarios will depend on the complexity\nof the code inside transactions, on the availability of hardware transactions,\non the number of reads and writes to transactional memory, and the rate\nof conflicts and rollbacks.\n\n### Note\nThese result are **not** absolute performance considerations of the tested\nLisp systems. They are simply the outcome of running micro-benchmarks\nof a particular library optimized for SBCL (see the hardware transactions,\natomic compare-and-swap and memory barriers considerations) on several other\nLisp systems.\nDo **not** try to construct these results as STMX author's opinions on the\nmentioned Lisp systems.\n\n### Lee-STMX\nFor a less artificial and hopefully more realistic benchmark, the author has ported\n[Lee-TM](http://apt.cs.man.ac.uk/projects/TM/LeeBenchmark/),\na non-trivial benchmark suite for transactional memory developed in 2007\nby the University of Manchester (UK). The result is\n[Lee-STMX](https://github.com/cosmos72/lee-stmx) - as of July 2013, its\nstatus is BETA.\n\nContacts, help, discussion\n--------------------------\nAs long as the traffic is low enough, [GitHub Issues](https://github.com/cosmos72/stmx/issues)\ncan be used to report test suite failures, bugs, suggestions, general discussion etc.\n\nIf the traffic becomes high, more appropriate discussion channels will be set-up.\n\nThe author will also try to answer support requests, but gives no guarantees.\n\nStatus\n------\n\nAs of July 2013, STMX is being written by Massimiliano Ghilardi\nand is considered by the author to be stable.\n\nSTMX is a full rewrite of CL-STM, which has been developed by Hoan Ton-That\nfor the Google Summer of Code 2006.\n\nDonations\n---------\n\nSTMX is a spare-time project. Donations can help the project by\nrecognizing its usefulness and covering expenses.\n\nYou can \u003ca href=\"http://stmx.org/donations\"\u003edonate with PayPal or credit card\u003c/a\u003e.\n\nLegal\n-----\n\nSTMX is released under the terms of the [Lisp Lesser General Public\nLicense](http://opensource.franz.com/preamble.html), known as the LLGPL.\n","funding_links":[],"categories":["Python ##","Interfaces to other package managers"],"sub_categories":["Third-party APIs"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcosmos72%2Fstmx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcosmos72%2Fstmx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcosmos72%2Fstmx/lists"}