1
+ #[ doc="
2
+ Process-wide, lazily started/stopped libuv event loop interaction.
3
+ " ] ;
4
+
5
+ import ll = uv_ll;
6
+ import hl = uv_hl;
7
+ import get_gl = get;
8
+
9
+ export get;
10
+
11
+ native mod rustrt {
12
+ fn rust_uv_get_kernel_global_chan_ptr ( ) -> * libc:: uintptr_t ;
13
+ fn rust_uv_get_kernel_global_async_handle ( ) -> * libc:: uintptr_t ;
14
+ fn rust_compare_and_swap_ptr ( address : * libc:: uintptr_t ,
15
+ oldval : libc:: uintptr_t ,
16
+ newval : libc:: uintptr_t ) -> bool ;
17
+ }
18
+
19
+ #[ doc ="
20
+ Race-free helper to get access to a global task where a libuv
21
+ loop is running.
22
+
23
+ # Return
24
+
25
+ * A `hl::high_level_loop` that encapsulates communication with the global
26
+ loop.
27
+ " ]
28
+ fn get ( ) -> hl:: high_level_loop {
29
+ let global_loop_chan_ptr = rustrt:: rust_uv_get_kernel_global_chan_ptr ( ) ;
30
+ log ( debug, #fmt ( "ENTERING global_loop::get() loop chan: %?" ,
31
+ global_loop_chan_ptr) ) ;
32
+
33
+ let builder_fn = { ||
34
+ let builder = task:: builder ( ) ;
35
+ let opts = {
36
+ supervise: false,
37
+ notify_chan: none,
38
+ sched:
39
+ some ( { mode: task:: manual_threads ( 1 u) ,
40
+ native_stack_size: none } )
41
+ } ;
42
+ task:: set_opts ( builder, opts) ;
43
+ builder
44
+ } ;
45
+ unsafe {
46
+ log ( debug, "before priv::chan_from_global_ptr" ) ;
47
+ let chan = priv:: chan_from_global_ptr :: < hl:: high_level_msg > (
48
+ global_loop_chan_ptr,
49
+ builder_fn) { |port|
50
+
51
+ // the actual body of our global loop lives here
52
+ log ( debug, "initialized global port task!" ) ;
53
+ log ( debug, "GLOBAL initialized global port task!" ) ;
54
+ outer_global_loop_body ( port) ;
55
+ } ;
56
+ log ( debug, "after priv::chan_from_global_ptr" ) ;
57
+ let handle = get_global_async_handle_native_representation ( )
58
+ as * * ll:: uv_async_t ;
59
+ ret { async_handle : handle, op_chan : chan } ;
60
+ }
61
+ }
62
+
63
+ // INTERNAL API
64
+
65
+ unsafe fn outer_global_loop_body ( msg_po : comm:: port < hl:: high_level_msg > ) {
66
+ // we're going to use a single libuv-generated loop ptr
67
+ // for the duration of the process
68
+ let loop_ptr = ll:: loop_new ( ) ;
69
+
70
+ // data structure for loop goes here..
71
+
72
+ // immediately weaken the task this is running in.
73
+ priv:: weaken_task ( ) { |weak_exit_po|
74
+ // when we first enter this loop, we're going
75
+ // to wait on stand-by to receive a request to
76
+ // fire-up the libuv loop
77
+ let mut continue = true ;
78
+ while continue {
79
+ log ( debug, "in outer_loop..." ) ;
80
+ continue = either:: either (
81
+ { |left_val|
82
+ // bail out..
83
+ // if we catch this msg at this point,
84
+ // we should just be able to exit because
85
+ // the loop isn't active
86
+ log ( debug, #fmt ( "weak_exit_po recv'd msg: %?" ,
87
+ left_val) ) ;
88
+ false
89
+ } , { |right_val|
90
+ log ( debug, "about to enter inner loop" ) ;
91
+ inner_global_loop_body ( weak_exit_po, msg_po, loop_ptr,
92
+ copy ( right_val) )
93
+ } , comm:: select2 ( weak_exit_po, msg_po) ) ;
94
+ log ( debug, #fmt ( "GLOBAL LOOP EXITED, WAITING TO RESTART? %?" ,
95
+ continue ) ) ;
96
+ }
97
+ } ;
98
+
99
+ ll:: loop_delete ( loop_ptr) ;
100
+ }
101
+
102
+ unsafe fn inner_global_loop_body ( weak_exit_po_in : comm:: port < ( ) > ,
103
+ msg_po_in : comm:: port < hl:: high_level_msg > ,
104
+ loop_ptr : * libc:: c_void ,
105
+ -first_interaction : hl:: high_level_msg ) -> bool {
106
+ // resend the msg
107
+ comm:: send ( comm:: chan ( msg_po_in) , first_interaction) ;
108
+
109
+ // black magic
110
+ let weak_exit_po_ptr = ptr:: addr_of ( weak_exit_po_in) ;
111
+ hl:: run_high_level_loop (
112
+ loop_ptr,
113
+ msg_po_in,
114
+ // before_run
115
+ { |async_handle|
116
+ log ( debug, #fmt ( "global_loop before_run: async_handle %?" ,
117
+ async_handle) ) ;
118
+ // set the handle as the global
119
+ set_global_async_handle ( 0 u as * ll:: uv_async_t ,
120
+ async_handle) ;
121
+ // when this is ran, our async_handle is set up, so let's
122
+ // do an async_send with it
123
+ ll:: async_send ( async_handle) ;
124
+ } ,
125
+ // before_msg_drain
126
+ { |async_handle|
127
+ log ( debug, #fmt ( "global_loop before_msg_drain: async_handle %?" ,
128
+ async_handle) ) ;
129
+ let weak_exit_po = * weak_exit_po_ptr;
130
+ if ( comm:: peek ( weak_exit_po) ) {
131
+ // if this is true, immediately bail and return false, causing
132
+ // the libuv loop to start tearing down
133
+ log ( debug, "got weak_exit meg inside libuv loop" ) ;
134
+ comm:: recv ( weak_exit_po) ;
135
+ false
136
+ }
137
+ // if no weak_exit_po msg is received, then we'll let the
138
+ // loop continue
139
+ else {
140
+ true
141
+ }
142
+ } ,
143
+ // before_tear_down
144
+ { |async_handle|
145
+ log ( debug, #fmt ( "global_loop before_tear_down: async_handle %?" ,
146
+ async_handle) ) ;
147
+ set_global_async_handle ( async_handle,
148
+ 0 as * ll:: uv_async_t ) ;
149
+ } ) ;
150
+ // supposed to return a bool to indicate to the enclosing loop whether
151
+ // it should continue or not..
152
+ ret true;
153
+ }
154
+
155
+ unsafe fn get_global_async_handle_native_representation ( )
156
+ -> * libc:: uintptr_t {
157
+ ret rustrt:: rust_uv_get_kernel_global_async_handle ( ) ;
158
+ }
159
+
160
+ unsafe fn get_global_async_handle ( ) -> * ll:: uv_async_t {
161
+ ret ( * get_global_async_handle_native_representation ( ) ) as * ll:: uv_async_t ;
162
+ }
163
+
164
+ unsafe fn set_global_async_handle ( old : * ll:: uv_async_t ,
165
+ new_ptr : * ll:: uv_async_t ) {
166
+ rustrt:: rust_compare_and_swap_ptr (
167
+ get_global_async_handle_native_representation ( ) ,
168
+ old as libc:: uintptr_t ,
169
+ new_ptr as libc:: uintptr_t ) ;
170
+ }
171
+
172
+ #[ cfg( test) ]
173
+ mod test {
174
+ crust fn simple_timer_close_cb ( timer_ptr : * ll:: uv_timer_t ) unsafe {
175
+ let exit_ch_ptr = ll:: get_data_for_uv_handle (
176
+ timer_ptr as * libc:: c_void ) as * comm:: chan < bool > ;
177
+ let exit_ch = * exit_ch_ptr;
178
+ comm:: send ( exit_ch, true ) ;
179
+ log ( debug, #fmt ( "EXIT_CH_PTR simple_timer_close_cb exit_ch_ptr: %?" ,
180
+ exit_ch_ptr) ) ;
181
+ }
182
+ crust fn simple_timer_cb ( timer_ptr : * ll:: uv_timer_t ,
183
+ status : libc:: c_int ) unsafe {
184
+ log ( debug, "in simple timer cb" ) ;
185
+ ll:: timer_stop ( timer_ptr) ;
186
+ let hl_loop = get_gl ( ) ;
187
+ hl:: interact ( hl_loop) { |loop_ptr|
188
+ log ( debug, "closing timer" ) ;
189
+ //ll::close(timer_ptr as *libc::c_void, simple_timer_close_cb);
190
+ hl:: unref_handle ( hl_loop, timer_ptr, simple_timer_close_cb) ;
191
+ log ( debug, "about to deref exit_ch_ptr" ) ;
192
+ log ( debug, "after msg sent on deref'd exit_ch" ) ;
193
+ } ;
194
+ log ( debug, "exiting simple timer cb" ) ;
195
+ }
196
+
197
+ fn impl_uv_hl_simple_timer ( hl_loop : hl:: high_level_loop ) unsafe {
198
+ let exit_po = comm:: port :: < bool > ( ) ;
199
+ let exit_ch = comm:: chan ( exit_po) ;
200
+ let exit_ch_ptr = ptr:: addr_of ( exit_ch) ;
201
+ log ( debug, #fmt ( "EXIT_CH_PTR newly created exit_ch_ptr: %?" ,
202
+ exit_ch_ptr) ) ;
203
+ let timer_handle = ll:: timer_t ( ) ;
204
+ let timer_ptr = ptr:: addr_of ( timer_handle) ;
205
+ hl:: interact ( hl_loop) { |loop_ptr|
206
+ log ( debug, "user code inside interact loop!!!" ) ;
207
+ let init_status = ll:: timer_init ( loop_ptr, timer_ptr) ;
208
+ if ( init_status == 0i32 ) {
209
+ hl:: ref_handle ( hl_loop, timer_ptr) ;
210
+ ll:: set_data_for_uv_handle (
211
+ timer_ptr as * libc:: c_void ,
212
+ exit_ch_ptr as * libc:: c_void ) ;
213
+ let start_status = ll:: timer_start ( timer_ptr, simple_timer_cb,
214
+ 1 u, 0 u) ;
215
+ if ( start_status == 0i32 ) {
216
+ }
217
+ else {
218
+ fail "failure on ll::timer_start()" ;
219
+ }
220
+ }
221
+ else {
222
+ fail "failure on ll::timer_init()" ;
223
+ }
224
+ } ;
225
+ comm:: recv( exit_po) ;
226
+ log( debug, "global_loop timer test: msg recv on exit_po, done.." ) ;
227
+ }
228
+ #[ test]
229
+ #[ ignore( cfg( target_os = "freebsd" ) ) ]
230
+ fn test_uv_global_loop_high_level_global_timer ( ) unsafe {
231
+ let hl_loop = get_gl ( ) ;
232
+ task:: spawn_sched ( task:: manual_threads ( 1 u) , { ||
233
+ impl_uv_hl_simple_timer ( hl_loop) ;
234
+ } ) ;
235
+ impl_uv_hl_simple_timer ( hl_loop) ;
236
+ }
237
+ }
0 commit comments