1- use std:: fmt;
21use std:: os:: raw:: { c_int, c_void} ;
2+ use std:: { fmt, ptr} ;
33
44use super :: XRc ;
55use crate :: state:: { RawLua , WeakLua } ;
66
7+ use self :: ref_count:: RefCount ;
8+
79/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
8- #[ derive( Clone ) ]
910pub struct ValueRef {
1011 pub ( crate ) lua : WeakLua ,
11- // Keep index separate to avoid additional indirection when accessing it.
1212 pub ( crate ) index : c_int ,
13- // If `index_count` is `None`, the value does not need to be destroyed.
14- pub ( crate ) index_count : Option < ValueRefIndex > ,
15- }
16-
17- /// A reference to a Lua value index in the auxiliary thread.
18- /// It's cheap to clone and can be used to track the number of references to a value.
19- #[ derive( Clone ) ]
20- pub ( crate ) struct ValueRefIndex ( pub ( crate ) XRc < c_int > ) ;
21-
22- impl From < c_int > for ValueRefIndex {
23- #[ inline]
24- fn from ( index : c_int ) -> Self {
25- ValueRefIndex ( XRc :: new ( index) )
26- }
13+ count : RefCount ,
2714}
2815
2916impl ValueRef {
3017 #[ inline]
31- pub ( crate ) fn new ( lua : & RawLua , index : impl Into < ValueRefIndex > ) -> Self {
32- let index = index. into ( ) ;
18+ pub ( crate ) fn new ( lua : & RawLua , index : c_int ) -> Self {
3319 ValueRef {
3420 lua : lua. weak ( ) . clone ( ) ,
35- index : * index . 0 ,
36- index_count : Some ( index ) ,
21+ index,
22+ count : RefCount :: unique ( ) ,
3723 }
3824 }
3925
26+ #[ cfg( feature = "async" ) ]
27+ #[ inline]
28+ pub ( crate ) fn take_index ( & mut self ) -> Option < c_int > {
29+ self . count . take ( self . index )
30+ }
31+
4032 #[ inline]
4133 pub ( crate ) fn to_pointer ( & self ) -> * const c_void {
4234 let lua = self . lua . lock ( ) ;
4335 unsafe { ffi:: lua_topointer ( lua. ref_thread ( ) , self . index ) }
4436 }
4537}
4638
39+ impl Clone for ValueRef {
40+ #[ inline]
41+ fn clone ( & self ) -> Self {
42+ ValueRef {
43+ lua : self . lua . clone ( ) ,
44+ index : self . index ,
45+ count : self . count . clone_shared ( ) ,
46+ }
47+ }
48+ }
49+
4750impl fmt:: Debug for ValueRef {
4851 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
4952 write ! ( f, "Ref({:p})" , self . to_pointer( ) )
@@ -52,14 +55,10 @@ impl fmt::Debug for ValueRef {
5255
5356impl Drop for ValueRef {
5457 fn drop ( & mut self ) {
55- if let Some ( ValueRefIndex ( index) ) = self . index_count . take ( ) {
56- // It's guaranteed that the inner value returns exactly once.
57- // This means in particular that the value is not dropped.
58- if XRc :: into_inner ( index) . is_some ( )
59- && let Some ( lua) = self . lua . try_lock ( )
60- {
61- unsafe { lua. drop_ref ( self ) }
62- }
58+ if self . count . drop_is_last ( )
59+ && let Some ( lua) = self . lua . try_lock ( )
60+ {
61+ unsafe { lua. drop_ref ( self ) }
6362 }
6463 }
6564}
@@ -74,3 +73,154 @@ impl PartialEq for ValueRef {
7473 unsafe { ffi:: lua_rawequal ( lua. ref_thread ( ) , self . index , other. index ) == 1 }
7574 }
7675}
76+
77+ // The counter is a pure refcount token.
78+ type Unit = ( ) ;
79+ const UNIQUE : * mut Unit = ptr:: without_provenance_mut ( 1 ) ;
80+ const NONE : * mut Unit = ptr:: null_mut ( ) ;
81+
82+ impl RefCount {
83+ #[ inline]
84+ fn unique ( ) -> Self {
85+ Self :: from_raw ( UNIQUE )
86+ }
87+
88+ #[ inline]
89+ fn clone_shared ( & self ) -> RefCount {
90+ let mut current = self . load ( ) ;
91+ loop {
92+ if current != UNIQUE {
93+ if current != NONE {
94+ unsafe { XRc :: increment_strong_count ( current as * const Unit ) } ;
95+ }
96+ return RefCount :: from_raw ( current) ;
97+ }
98+ // Lazily allocate the shared counter
99+ let shared = XRc :: into_raw ( XRc :: new ( ( ) ) ) as * mut Unit ;
100+ match self . promote ( shared) {
101+ Ok ( ( ) ) => {
102+ unsafe { XRc :: increment_strong_count ( shared as * const Unit ) } ;
103+ return RefCount :: from_raw ( shared) ;
104+ }
105+ Err ( actual) => {
106+ unsafe { drop ( XRc :: from_raw ( shared as * const Unit ) ) } ;
107+ current = actual;
108+ }
109+ }
110+ }
111+ }
112+
113+ /// Takes the slot if it's solely owned by `self`.
114+ ///
115+ /// Returns `None` if the slot is still shared or already non-owning.
116+ #[ cfg( feature = "async" ) ]
117+ fn take ( & mut self , index : c_int ) -> Option < c_int > {
118+ match self . load ( ) {
119+ current if current == UNIQUE => {
120+ self . swap_none ( ) ;
121+ Some ( index)
122+ }
123+ current if current == NONE => None ,
124+ current => {
125+ // Shared: recyclable only if no other owner remains
126+ let rc = unsafe { XRc :: from_raw ( current as * const Unit ) } ;
127+ if XRc :: strong_count ( & rc) == 1 {
128+ drop ( rc) ;
129+ self . swap_none ( ) ;
130+ Some ( index)
131+ } else {
132+ let _ = XRc :: into_raw ( rc) ; // still shared
133+ None
134+ }
135+ }
136+ }
137+ }
138+
139+ /// Drops the reference and returns `true` if it was the last owner of the slot
140+ /// (so the slot must be freed).
141+ #[ inline]
142+ fn drop_is_last ( & mut self ) -> bool {
143+ let current = self . load ( ) ;
144+ if current == UNIQUE {
145+ true
146+ } else if current == NONE {
147+ false
148+ } else {
149+ unsafe { XRc :: into_inner ( XRc :: from_raw ( current as * const Unit ) ) . is_some ( ) }
150+ }
151+ }
152+ }
153+
154+ #[ cfg( feature = "send" ) ]
155+ mod ref_count {
156+ use std:: sync:: atomic:: { AtomicPtr , Ordering } ;
157+
158+ use super :: Unit ;
159+
160+ pub ( super ) struct RefCount ( AtomicPtr < Unit > ) ;
161+
162+ impl RefCount {
163+ #[ inline]
164+ pub ( super ) fn from_raw ( ptr : * mut Unit ) -> Self {
165+ RefCount ( AtomicPtr :: new ( ptr) )
166+ }
167+
168+ #[ inline]
169+ pub ( super ) fn load ( & self ) -> * mut Unit {
170+ self . 0 . load ( Ordering :: Acquire )
171+ }
172+
173+ /// Replaces the `UNIQUE` tag with the freshly allocated shared counter (`new`).
174+ ///
175+ /// Returns `Err(current)` if another thread promoted first.
176+ #[ inline]
177+ pub ( super ) fn promote ( & self , new : * mut Unit ) -> Result < ( ) , * mut Unit > {
178+ self . 0
179+ . compare_exchange ( super :: UNIQUE , new, Ordering :: AcqRel , Ordering :: Acquire )
180+ . map ( |_| ( ) )
181+ }
182+
183+ #[ cfg( feature = "async" ) ]
184+ #[ inline]
185+ pub ( super ) fn swap_none ( & self ) -> * mut Unit {
186+ self . 0 . swap ( super :: NONE , Ordering :: AcqRel )
187+ }
188+ }
189+ }
190+
191+ #[ cfg( not( feature = "send" ) ) ]
192+ mod ref_count {
193+ use std:: cell:: Cell ;
194+
195+ use super :: Unit ;
196+
197+ pub ( super ) struct RefCount ( Cell < * mut Unit > ) ;
198+
199+ impl RefCount {
200+ #[ inline]
201+ pub ( super ) fn from_raw ( ptr : * mut Unit ) -> Self {
202+ RefCount ( Cell :: new ( ptr) )
203+ }
204+
205+ #[ inline]
206+ pub ( super ) fn load ( & self ) -> * mut Unit {
207+ self . 0 . get ( )
208+ }
209+
210+ /// Replaces the `UNIQUE` tag with the freshly allocated shared counter `new`.
211+ ///
212+ /// Never fails.
213+ #[ inline]
214+ pub ( super ) fn promote ( & self , new : * mut Unit ) -> Result < ( ) , * mut Unit > {
215+ debug_assert_eq ! ( self . 0 . get( ) , super :: UNIQUE ) ;
216+ self . 0 . set ( new) ;
217+ Ok ( ( ) )
218+ }
219+
220+ #[ cfg( feature = "async" ) ]
221+ #[ inline]
222+ pub ( super ) fn swap_none ( & self ) -> * mut Unit {
223+ self . 0 . replace ( super :: NONE )
224+ }
225+ }
226+ }
0 commit comments