@@ -71,4 +71,112 @@ func display(list: TodoListProtocol) {
71
71
print ($0 )
72
72
}
73
73
}
74
- ```
74
+ ```
75
+
76
+ # Concurrent Access
77
+
78
+ Since interfaces represent mutable data, uniffi has to take extra care
79
+ to uphold Rust's safety guarantees around shared and mutable references.
80
+ The foreign-language code may attempt to operate on an interface instance
81
+ from multiple threads, and it's important that this not violate Rust's
82
+ assumption that there is at most a single mutable reference to a struct
83
+ at any point in time.
84
+
85
+ By default, uniffi enforces this using runtime locking. Each interface instance
86
+ has an associated lock which is transparently acquired at the beginning of each
87
+ call to a method of that instance, and released once the method returns. This
88
+ approach is simple and safe, but it means that all method calls on an instance
89
+ are run in a strictly sequential fashion, limiting concurrency.
90
+
91
+ You can opt out of this protection by marking the interface as threadsafe:
92
+
93
+ ``` idl
94
+ [Threadsafe]
95
+ interface Counter {
96
+ constructor();
97
+ void increment();
98
+ u64 get();
99
+ };
100
+ ```
101
+
102
+ The uniffi-generated code will allow concurrent method calls on threadsafe interfaces
103
+ without any locking.
104
+
105
+ For this to be safe, the underlying Rust struct must adhere to certain restrictions, and
106
+ uniffi's generated Rust scaffolding will emit compile-time errors if it does not.
107
+
108
+ The Rust struct must not expose any methods that take ` &mut self ` . The following implementation
109
+ of the ` Counter ` interface will fail to compile because it relies on mutable references:
110
+
111
+ ``` rust
112
+ struct Counter {
113
+ value : u64
114
+ }
115
+
116
+ impl Counter {
117
+ fn new () -> Self {
118
+ Self { value : 0 }
119
+ }
120
+
121
+ // No mutable references to self allowed in [Threadsafe] interfaces.
122
+ fn increment (& mut self ) {
123
+ self . value = self . value + 1 ;
124
+ }
125
+
126
+ fn get (& self ) -> u64 {
127
+ self . value
128
+ }
129
+ }
130
+ ```
131
+
132
+ Implementations can instead use Rust's "interior mutability" pattern. However, they
133
+ must do so in a way that is both ` Sync ` and ` Send ` , since the foreign-language code
134
+ may operate on the instance from multiple threads. The following implementation of the
135
+ ` Counter ` interface will fail to compile because ` RefCell ` is not ` Send ` :
136
+
137
+ ``` rust
138
+ struct Counter {
139
+ value : RefCell <u64 >
140
+ }
141
+
142
+ impl Counter {
143
+ fn new () -> Self {
144
+ Self { value : RefCell :: new (0 ) }
145
+ }
146
+
147
+ fn increment (& self ) {
148
+ let mut value = self . value. borrow_mut ();
149
+ * value = * value + 1 ;
150
+ }
151
+
152
+ fn get (& self ) -> u64 {
153
+ * self . value. borrow ()
154
+ }
155
+ }
156
+ ```
157
+
158
+ This version uses the ` AtomicU64 ` struct for interior mutability, which is both ` Sync ` and
159
+ ` Send ` and hence will compile successfully:
160
+
161
+ ``` rust
162
+ struct Counter {
163
+ value : i64
164
+ }
165
+
166
+ impl Counter {
167
+ fn new () -> Self {
168
+ Self { 0 }
169
+ }
170
+
171
+ fn increment (& self ) {
172
+ self . value = self . value + 1 ;
173
+ }
174
+
175
+ fn get (& self ) -> i64 {
176
+ self . value
177
+ }
178
+ }
179
+ ```
180
+
181
+ Uniffi aims to uphold Rust's safety guarantees at all times, without requiring the
182
+ foreign-language code to know or care about them.
0 commit comments