{"id":13578323,"url":"https://github.com/jbakic/Shielded","last_synced_at":"2025-04-05T16:32:15.898Z","repository":{"id":64674586,"uuid":"12182921","full_name":"jbakic/Shielded","owner":"jbakic","description":"A strict and mostly lock-free Software Transactional Memory (STM) for .NET","archived":false,"fork":false,"pushed_at":"2022-12-12T16:46:56.000Z","size":1366,"stargazers_count":249,"open_issues_count":3,"forks_count":21,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-03-11T11:53:32.836Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jbakic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-08-17T17:52:26.000Z","updated_at":"2025-02-16T22:26:10.000Z","dependencies_parsed_at":"2022-12-13T20:20:14.948Z","dependency_job_id":null,"html_url":"https://github.com/jbakic/Shielded","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbakic%2FShielded","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbakic%2FShielded/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbakic%2FShielded/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jbakic%2FShielded/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jbakic","download_url":"https://codeload.github.com/jbakic/Shielded/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247366582,"owners_count":20927543,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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-08-01T15:01:29.451Z","updated_at":"2025-04-05T16:32:13.909Z","avatar_url":"https://github.com/jbakic.png","language":"C#","readme":"Shielded\n========\n\nAvailable on [NuGet](https://www.nuget.org/packages/Shielded).\n\nShielded is a full-featured implementation of Software Transactional Memory\nin .NET. It provides a system (the Shield static class) for running in-memory\ntransactions, and data structures which are aware of transactions. It can also\ngenerate transaction-aware proxy subclasses based on a POCO class type (only\nsupported on the .NET Framework, not on .NET Standard). The implementation is\nstrict, with strong guarantees on safety. It is mostly lock-free, using only\none major lock which is held during the pre-commit check.\n\nHere is a small example:\n\n```csharp\nvar n = new Shielded\u003cint\u003e();\nint a = n;\nShield.InTransaction(() =\u003e\n    n.Value = n + 5);\n```\n\nShielded fields are thread-safe. You can read them out of transaction, but\nchanges must be done inside. While inside, the library guarantees a consistent\nview of all shielded fields.\n\nAnother example, the STM version of \"Hello world!\" - parallel addition in an\narray. Here, in a dictionary:\n\n```csharp\nvar dict = new ShieldedDict\u003cint, int\u003e();\nParallelEnumerable.Range(0, 100000)\n    .ForAll(i =\u003e Shield.InTransaction(() =\u003e\n        dict[i % 100] = dict.ContainsKey(i % 100) ? dict[i % 100] + 1 : 1));\n```\n\nShielded works with value types, and the language automatically does the needed\ncloning. For ref types, it only makes the reference itself transactional.\nThe class should then be immutable, or, if you're targetting the full .NET Framework,\nand if you have a class you want to make transactional:\n\n```csharp\npublic class TestClass {\n    public virtual Guid Id { get; set; }\n    public virtual string Name { get; set; }\n}\n```\n\nThen you create instances like this:\n\n```csharp\nusing Shielded.ProxyGen;\n...\nvar t = Factory.NewShielded\u003cTestClass\u003e();\n```\n\nThe Factory creates a proxy sub-class, using CodeDom, which will have transactional\noverrides for all virtual properties of the base class that are public or protected.\nDue to CodeDom limitations, the getter and setter must have the same accessibility!\nThe proxy objects are thread-safe (or, at least their virtual properties are), and\ncan only be changed inside transactions.\n\nSince CodeDom is not available on .NET Standard, this feature is currently not\nsupported if you're not targeting the full .NET Framework.\n\nUsage is simple:\n\n```csharp\nvar id = t.Id;\nShield.InTransaction(() =\u003e\n    t.Name = \"Test object\");\n```\n\nIt is safe to execute any number of concurrent transactions that are reading from\nor writing into the same shielded fields - each transaction will complete correctly.\nThis is accomplished by:\n* ensuring that in one transaction you read a consistent state of\nall shielded fields\n* buffering writes into storage which is local for each thread\n\nYour changes are commited and made visible to other threads only if all\nthe shielded fields you read or wrote into have not changed since you\nstarted. If any have new changes, your transaction is retried from the\nbeginning, but this time reading the new data. Though it may seem so,\nthis cannot create an infinite loop since for any conflict to occur at\nleast one transaction must successfully commit. Overall, the system must\nmake progress.\n\nThis quality would place Shielded in the lock-free class of non-blocking\nconcurrency mechanisms, according to academic classification. However,\nthis is not accurate since the commit check gets done under a lock. Hence\nthe word \"mostly\" in the short description.\n\nFeatures\n--------\n\n* **MVCC**: Each transaction reads a consistent snapshot of the state without\nthe need for locking, since updates just create new versions.\n    * Old versions are dropped soon after no one is capable of reading them\n    any more.\n* **Read-only transactions** always complete without any repetitions and\nwithout entering the global lock!\n* **Strictness**: If a write is made anywhere, the system will insist that\nall touched locations, read or written, still contain the same version\nof data that they had when the transaction opened. This means it does not\nsuffer from the Write Skew issue.\n* **Transactional collections**: Included in the library are ShieldedDict\u003c\u003e\n(dictionary), ShieldedSeq\u003c\u003e (singly linked list) and ShieldedTree\u003c\u003e (a\nred-black tree implementation).\n    * It is possible to use this library with immutable collections from\n    [System.Collections.Immutable](https://msdn.microsoft.com/en-us/library/mt452182%28v=vs.110%29.aspx).\n* **Transaction-local storage**: ShieldedLocal\u003c\u003e allows storing anything\nin the transaction context, visible only from within that transaction.\n* To perform **side-effects** (IO, and most other operations which are not\nshielded) you use the SideEffect method of the Shield class, which takes\noptional onCommit and onRollback lambdas, or the **SyncSideEffect** method which\nallows you to execute code during a commit, while the changed fields are still\nlocked.\n* **Conditional transactions**: Method Shield.Conditional allows you\nto define something similar to a database AFTER trigger. It receives a test, and\nan action to perform, both lambdas. It runs the test, makes a note of\nall shielded objects that the test had accessed, and later re-executes\nthe test when any of those objects is committed into. If test passes, the\naction is called.\n    * Implemented transactionally, so can be called from transactions, and\n    can be triggered by the transaction that created it.\n    * Returns an IDisposable for deactivating the subscription, also\n    transactionally. It may even deactivate itself, e.g. to guarantee one-time execution.\n* **Pre-commit checks**: Shield.PreCommit is very similar to Shield.Conditional,\nbut executes the test within a transaction that changes one of the fields it is\ninterested in, just before that transaction will commit.\n    * Can be used to ensure certain invariants are held, or to implement\n    thread prioritization by allowing only some threads which access a field\n    to commit into it.\n* **Custom commit operations**: You can integrate your own code into the commit process,\nto execute while the shielded fields, that are being written, are held locked.\n    * Already mentioned Shield.SyncSideEffect does this on the level of one transaction.\n    * Using Shield.WhenCommitting, you subscribe globally for any commit, or based on\n    the type of field being written. These subscriptions should never throw!\n    * Shield.RunToCommit runs a transaction just up to commit, and allows you to\n    commit/rollback later, or from another thread. This is useful for asynchronous\n    programming.\n* **Commutables**: operations which can be performed without conflict, because\nthey can be reordered in time and have the same net effect, i.e. they are\ncommutable (name borrowed from Clojure). Incrementing an int is an\nexample - if you don’t care what the int’s value is, you can increment it\nwithout conflict by simply incrementing whatever value you encounter there\nat commit time. Using commutes, when appropriate, reduces conflicts and\nimproves concurrency. Incrementing an int, conflict-free:\n\n    ```csharp\n    n.Commute((ref int a) =\u003e a++);\n    ```\n\n    * Commutes are not performed under any lock, but rather in a special\n    commute subtransaction, which reads the latest data, and tries to\n    commit with the same stamp as your main transaction. If only the commutes\n    fail, then only the commutes get retried.\n    * If, in the example above, your main transaction has already (or perhaps\n    will later) read the n field or written to it (non-commutatively), the\n    commute “degenerates” - it gets executed in place, in your transaction,\n    and you can see it’s effect. This means consistency - if you read it, it\n    will stay as read when you commit. But, it is now a potential conflict.\n    * Shield has various commutable operations defined in it. Appending to a\n    sequence is commutable - if you do not touch the seq, it never conflicts.\n    Collection Count fields are comuted over, to avoid unnecessary conflicts.\n","funding_links":[],"categories":["C# #","Misc","杂项"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbakic%2FShielded","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjbakic%2FShielded","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbakic%2FShielded/lists"}