|
|
1 | #include "xthread.h" |
|
|
2 | |
|
|
3 | #include <errno.h> |
|
|
4 | |
1 | #include "EXTERN.h" |
5 | #include "EXTERN.h" |
2 | #include "perl.h" |
6 | #include "perl.h" |
3 | #include "XSUB.h" |
7 | #include "XSUB.h" |
4 | |
8 | |
5 | #include <stddef.h> |
9 | #include <stddef.h> |
6 | |
10 | |
7 | #include "EVAPI.h" |
11 | #include "EVAPI.h" |
8 | #include "xthread.h" |
|
|
9 | |
12 | |
10 | /* our userdata */ |
13 | /* our userdata */ |
11 | typedef struct { |
14 | typedef struct { |
12 | mutex_t lock; /* global loop lock */ |
15 | xmutex_t lock; /* global loop lock */ |
13 | void (*signal_func) (void *signal_arg, int value); |
16 | void (*signal_func) (void *signal_arg, int value); |
14 | void *signal_arg; |
17 | void *signal_arg; |
15 | ev_async async_w; |
18 | ev_async async_w; |
16 | thread_t tid; |
19 | xthread_t tid; |
|
|
20 | unsigned int max_loops; |
|
|
21 | unsigned int count; |
17 | |
22 | |
18 | cond_t invoke_cv; |
23 | xcond_t invoke_cv; |
19 | mutex_t invoke_mutex; |
|
|
20 | |
24 | |
21 | SV *interrupt; |
25 | SV *interrupt; |
|
|
26 | #if defined(_WIN32) && defined(USE_ITHREADS) |
|
|
27 | void *thx; |
|
|
28 | #endif |
22 | } udat; |
29 | } udat; |
23 | |
30 | |
|
|
31 | static void loop_set_cb (EV_P); |
|
|
32 | |
|
|
33 | static void |
|
|
34 | fg_invoke_pending (EV_P) |
|
|
35 | { |
|
|
36 | udat *u = ev_userdata (EV_A); |
|
|
37 | |
|
|
38 | u->count = ev_pending_count (EV_A); |
|
|
39 | |
|
|
40 | if (u->count) |
|
|
41 | ev_invoke_pending (EV_A); |
|
|
42 | } |
|
|
43 | |
24 | static void |
44 | static void |
25 | c_func (pTHX_ void *loop_, int value) |
45 | c_func (pTHX_ void *loop_, int value) |
26 | { |
46 | { |
27 | struct ev_loop *loop = (struct ev_loop *)loop_; |
47 | struct ev_loop *loop = (struct ev_loop *)loop_; |
28 | udat *u = ev_userdata (EV_A); |
48 | udat *u = ev_userdata (EV_A); |
|
|
49 | int i; |
|
|
50 | |
29 | X_LOCK (u->lock); |
51 | X_LOCK (u->lock); |
30 | ev_invoke_pending (loop); |
52 | ev_invoke_pending (EV_A); |
|
|
53 | |
|
|
54 | /* do any additional foreground loop runs */ |
|
|
55 | for (i = u->max_loops; i--; ) |
|
|
56 | { |
|
|
57 | /* this is a bit tricky, but we can manage... */ |
|
|
58 | u->count = 0; |
|
|
59 | |
|
|
60 | ev_set_invoke_pending_cb (EV_A, fg_invoke_pending); |
|
|
61 | ev_set_loop_release_cb (EV_A, 0, 0); |
|
|
62 | ev_run (EV_A, EVRUN_NOWAIT); |
|
|
63 | loop_set_cb (EV_A); |
|
|
64 | |
|
|
65 | if (!u->count) |
|
|
66 | break; |
|
|
67 | } |
|
|
68 | |
|
|
69 | X_COND_SIGNAL (u->invoke_cv); |
31 | X_UNLOCK (u->lock); |
70 | X_UNLOCK (u->lock); |
32 | X_COND_SIGNAL (u->invoke_cv); |
|
|
33 | } |
71 | } |
34 | |
72 | |
35 | static void |
73 | static void |
36 | async_cb (EV_P_ ev_async *w, int revents) |
74 | async_cb (EV_P_ ev_async *w, int revents) |
37 | { |
75 | { |
… | |
… | |
54 | |
92 | |
55 | static void |
93 | static void |
56 | l_invoke (EV_P) |
94 | l_invoke (EV_P) |
57 | { |
95 | { |
58 | udat *u = ev_userdata (EV_A); |
96 | udat *u = ev_userdata (EV_A); |
59 | X_UNLOCK (u->lock); |
97 | |
|
|
98 | while (ev_pending_count (EV_A)) |
|
|
99 | { |
60 | u->signal_func (u->signal_arg, 1); |
100 | u->signal_func (u->signal_arg, 1); |
61 | X_COND_WAIT (u->invoke_cv, u->invoke_mutex); |
101 | X_COND_WAIT (u->invoke_cv, u->lock); |
62 | X_LOCK (u->lock); |
102 | } |
|
|
103 | } |
|
|
104 | |
|
|
105 | static void |
|
|
106 | loop_set_cb (EV_P) |
|
|
107 | { |
|
|
108 | ev_set_invoke_pending_cb (EV_A, l_invoke); |
|
|
109 | ev_set_loop_release_cb (EV_A, l_release, l_acquire); |
63 | } |
110 | } |
64 | |
111 | |
65 | X_THREAD_PROC(l_run) |
112 | X_THREAD_PROC(l_run) |
66 | { |
113 | { |
67 | struct ev_loop *loop = (struct ev_loop *)thr_arg; |
114 | struct ev_loop *loop = (struct ev_loop *)thr_arg; |
|
|
115 | #if defined(_WIN32) && defined(USE_ITHREADS) |
68 | udat *u = ev_userdata (loop); |
116 | udat *u = ev_userdata (EV_A); |
69 | |
117 | |
70 | X_LOCK (u->invoke_mutex); |
118 | /* just setting the same context pointer as the other thread is */ |
71 | X_LOCK (u->lock); |
119 | /* probably fatal, yet, I have no clue what makes libev crash (malloc?) */ |
72 | ev_ref (loop); /* really? */ |
120 | /* as visual c also crashes when it tries to debug the crash */ |
73 | for (;;) /* really? */ |
121 | /* the loser platform is indeed a crashy OS */ |
74 | ev_loop (loop, 0); |
122 | PERL_SET_CONTEXT (u->thx); |
75 | X_UNLOCK (u->lock); |
123 | #endif |
|
|
124 | |
|
|
125 | l_acquire (EV_A); |
|
|
126 | |
|
|
127 | /* yeah */ |
|
|
128 | pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0); |
|
|
129 | |
|
|
130 | ev_ref (EV_A); |
|
|
131 | ev_run (EV_A, 0); |
|
|
132 | ev_unref (EV_A); |
|
|
133 | |
|
|
134 | l_release (EV_A); |
|
|
135 | |
|
|
136 | return 0; |
76 | } |
137 | } |
77 | |
138 | |
78 | static void |
139 | static void |
79 | scope_lock_cb (pTHX_ void *loop_) |
140 | scope_lock_cb (pTHX_ void *loop_) |
80 | { |
141 | { |
81 | struct ev_loop *loop = (struct ev_loop *)SvIVX ((SV *)loop_); |
142 | struct ev_loop *loop = (struct ev_loop *)SvIVX ((SV *)loop_); |
82 | udat *u = ev_userdata (loop); |
143 | udat *u = ev_userdata (EV_A); |
83 | |
144 | |
84 | X_UNLOCK (u->lock); |
145 | X_UNLOCK (u->lock); |
85 | SvREFCNT_dec ((SV *)loop_); |
146 | SvREFCNT_dec ((SV *)loop_); |
86 | } |
147 | } |
87 | |
148 | |
… | |
… | |
90 | PROTOTYPES: ENABLE |
151 | PROTOTYPES: ENABLE |
91 | |
152 | |
92 | BOOT: |
153 | BOOT: |
93 | { |
154 | { |
94 | I_EV_API ("EV::Loop::Async"); |
155 | I_EV_API ("EV::Loop::Async"); |
|
|
156 | CvNODEBUG_on (get_cv ("EV::Loop::Async::scope_lock", 0)); /* otherwise calling scope can be the debugger */ |
95 | } |
157 | } |
96 | |
158 | |
97 | void |
159 | void |
98 | _c_func (SV *loop) |
160 | _c_func (SV *loop) |
99 | PPCODE: |
161 | PPCODE: |
… | |
… | |
101 | PUSHs (sv_2mortal (newSViv (PTR2IV (c_func)))); |
163 | PUSHs (sv_2mortal (newSViv (PTR2IV (c_func)))); |
102 | PUSHs (sv_2mortal (newSViv (SvIVX (SvRV (loop))))); |
164 | PUSHs (sv_2mortal (newSViv (SvIVX (SvRV (loop))))); |
103 | |
165 | |
104 | void |
166 | void |
105 | _attach (SV *loop_, SV *interrupt, IV sig_func, void *sig_arg) |
167 | _attach (SV *loop_, SV *interrupt, IV sig_func, void *sig_arg) |
|
|
168 | PROTOTYPE: @ |
106 | CODE: |
169 | CODE: |
107 | { |
170 | { |
108 | pthread_mutexattr_t ma; |
171 | pthread_mutexattr_t ma; |
109 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
172 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
110 | udat *u; |
173 | udat *u; |
111 | |
174 | |
112 | Newz (0, u, 1, udat); |
175 | Newz (0, u, 1, udat); |
113 | u->interrupt = newSVsv (interrupt); |
176 | u->interrupt = newSVsv (interrupt); |
114 | u->signal_func = (void (*)(void *, int))sig_func; |
177 | u->signal_func = (void (*)(void *, int))sig_func; |
115 | u->signal_arg = sig_arg; |
178 | u->signal_arg = sig_arg; |
|
|
179 | #if defined(_WIN32) && defined(USE_ITHREADS) |
|
|
180 | u->thx = PERL_GET_CONTEXT; |
|
|
181 | #endif |
116 | |
182 | |
117 | ev_async_init (&u->async_w, async_cb); |
183 | ev_async_init (&u->async_w, async_cb); |
118 | ev_async_start (loop, &u->async_w); |
184 | ev_async_start (EV_A, &u->async_w); |
119 | |
185 | |
120 | pthread_mutexattr_init (&ma); |
186 | pthread_mutexattr_init (&ma); |
|
|
187 | #ifdef PTHREAD_MUTEX_RECURSIVE |
121 | pthread_mutexattr_settype (&ma, PTHREAD_MUTEX_RECURSIVE); |
188 | pthread_mutexattr_settype (&ma, PTHREAD_MUTEX_RECURSIVE); |
|
|
189 | #else |
|
|
190 | pthread_mutexattr_settype (&ma, PTHREAD_MUTEX_RECURSIVE_NP); |
|
|
191 | #endif |
122 | pthread_mutex_init (&u->lock, &ma); |
192 | pthread_mutex_init (&u->lock, &ma); |
123 | pthread_mutex_init (&u->invoke_mutex, &ma); |
|
|
124 | pthread_mutexattr_destroy (&ma); |
193 | pthread_mutexattr_destroy (&ma); |
125 | |
194 | |
126 | pthread_cond_init (&u->invoke_cv, 0); |
195 | pthread_cond_init (&u->invoke_cv, 0); |
127 | |
196 | |
128 | ev_set_userdata (loop, u); |
197 | ev_set_userdata (EV_A, u); |
129 | ev_set_invoke_pending_cb (loop, l_invoke); |
198 | loop_set_cb (EV_A); |
130 | ev_set_loop_release_cb (loop, l_release, l_acquire); |
|
|
131 | |
199 | |
132 | thread_create (&u->tid, l_run, (void *)loop); |
200 | thread_create (&u->tid, l_run, loop); |
133 | } |
201 | } |
134 | |
202 | |
135 | SV * |
203 | SV * |
136 | interrupt (SV *loop_) |
204 | interrupt (SV *loop_) |
137 | CODE: |
205 | CODE: |
138 | { |
206 | { |
139 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
207 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
140 | udat *u = ev_userdata (loop); |
208 | udat *u = ev_userdata (EV_A); |
141 | |
209 | |
142 | RETVAL = newSVsv (u->interrupt); |
210 | RETVAL = newSVsv (u->interrupt); |
143 | } |
211 | } |
144 | OUTPUT: |
212 | OUTPUT: |
145 | RETVAL |
213 | RETVAL |
|
|
214 | |
|
|
215 | void |
|
|
216 | set_max_foreground_loops (SV *loop_, UV max_loops) |
|
|
217 | CODE: |
|
|
218 | { |
|
|
219 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
|
|
220 | udat *u = ev_userdata (EV_A); |
|
|
221 | |
|
|
222 | u->max_loops = max_loops; |
|
|
223 | } |
146 | |
224 | |
147 | void |
225 | void |
148 | lock (SV *loop_) |
226 | lock (SV *loop_) |
149 | ALIAS: |
227 | ALIAS: |
150 | lock = 0 |
228 | lock = 0 |
151 | unlock = 1 |
229 | unlock = 1 |
152 | poke = 2 |
230 | notify = 2 |
153 | CODE: |
231 | CODE: |
154 | { |
232 | { |
155 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
233 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
156 | udat *u = ev_userdata (loop); |
234 | udat *u = ev_userdata (EV_A); |
157 | |
235 | |
158 | switch (ix) |
236 | switch (ix) |
159 | { |
237 | { |
160 | case 0: X_LOCK (u->lock); break; |
238 | case 0: X_LOCK (u->lock); break; |
161 | case 1: X_UNLOCK (u->lock); break; |
239 | case 1: X_UNLOCK (u->lock); break; |
162 | case 2: ev_async_send (loop, &u->async_w); break; |
240 | case 2: ev_async_send (EV_A, &u->async_w); break; |
163 | } |
241 | } |
164 | } |
242 | } |
165 | |
243 | |
166 | void |
244 | void |
167 | scope_lock (SV *loop_) |
245 | scope_lock (SV *loop_) |
168 | CODE: |
246 | CODE: |
169 | { |
247 | { |
170 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
248 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
171 | udat *u = ev_userdata (loop); |
249 | udat *u = ev_userdata (EV_A); |
172 | |
250 | |
173 | X_LOCK (u->lock); |
251 | X_LOCK (u->lock); |
174 | |
252 | |
175 | LEAVE; /* unfortunately, perl sandwiches XS calls into ENTER/LEAVE */ |
253 | LEAVE; /* unfortunately, perl sandwiches XS calls into ENTER/LEAVE */ |
176 | SAVEDESTRUCTOR_X (scope_lock_cb, (void *)SvREFCNT_inc (SvRV (loop_))); |
254 | SAVEDESTRUCTOR_X (scope_lock_cb, (void *)SvREFCNT_inc (SvRV (loop_))); |
… | |
… | |
180 | void |
258 | void |
181 | DESTROY (SV *loop_) |
259 | DESTROY (SV *loop_) |
182 | CODE: |
260 | CODE: |
183 | { |
261 | { |
184 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
262 | struct ev_loop *loop = (struct ev_loop *)SvIVX (SvRV (loop_)); |
185 | udat *u = ev_userdata (loop); |
263 | udat *u = ev_userdata (EV_A); |
186 | |
264 | |
187 | if (u) |
265 | if (u) |
188 | { |
266 | { |
189 | X_LOCK (u->lock); |
267 | X_LOCK (u->lock); |
190 | ev_async_send (loop, &u->async_w); |
268 | ev_async_stop (EV_A, &u->async_w); |
|
|
269 | /* now thread is around blocking call, or in pthread_cond_wait */ |
191 | pthread_cancel (u->tid); |
270 | pthread_cancel (u->tid); |
192 | ev_async_stop (loop, &u->async_w); |
271 | X_UNLOCK (u->lock); |
193 | pthread_mutex_destroy (&u->lock); |
272 | pthread_mutex_destroy (&u->lock); |
194 | pthread_cond_destroy (&u->invoke_cv); |
273 | pthread_cond_destroy (&u->invoke_cv); |
195 | pthread_mutex_destroy (&u->invoke_mutex); |
274 | SvREFCNT_dec (u->interrupt); |
196 | Safefree (u); |
275 | Safefree (u); |
197 | SvREFCNT_dec (u->interrupt); |
|
|
198 | } |
276 | } |
199 | } |
277 | } |
200 | |
278 | |
201 | |
279 | |
202 | |
280 | |