Skip to content

Commit cdee6d5

Browse files
committed
Enhance socket binding check, print bound coroutine call stack in case of failure
1 parent 3cbc2ad commit cdee6d5

File tree

5 files changed

+80
-24
lines changed

5 files changed

+80
-24
lines changed

‎ext-src/php_swoole.cc‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,14 @@ static void fatal_error(int code, const char *format, ...) {
508508

509509
zend::print_error(exception, E_ERROR);
510510

511+
if (code == SW_ERROR_CO_HAS_BEEN_BOUND) {
512+
fprintf(stderr, "\n [Coroutine-%ld] Stack trace:"
513+
"\n -------------------------------------------------------------------"
514+
"\n",
515+
Coroutine::get_socket_bound_cid());
516+
sw_php_print_backtrace(Coroutine::get_socket_bound_cid());
517+
}
518+
511519
#ifdef SW_THREAD
512520
if (!tsrm_is_main_thread()) {
513521
php_swoole_thread_bailout();
@@ -1145,7 +1153,7 @@ PHP_MINFO_FUNCTION(swoole) {
11451153
php_info_print_table_row(2, "execinfo", "enabled");
11461154
#endif
11471155
#ifdef HAVE_SSH2LIB
1148-
php_swoole_ssh2_minfo();
1156+
php_swoole_ssh2_minfo();
11491157
#endif
11501158
php_info_print_table_end();
11511159

‎include/swoole_coroutine.h‎

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class Coroutine {
144144
static void set_on_close(SwapCallback func);
145145
static void bailout(const BailoutCallback &func);
146146

147-
static inline long create(const CoroutineFunc &fn, void *args = nullptr) {
147+
static long create(const CoroutineFunc &fn, void *args = nullptr) {
148148
#ifdef SW_USE_THREAD_CONTEXT
149149
try {
150150
return (new Coroutine(fn, args))->run();
@@ -161,61 +161,65 @@ class Coroutine {
161161
static void activate();
162162
static void deactivate();
163163

164-
static inline Coroutine *get_current() {
164+
static Coroutine *get_current() {
165165
return current;
166166
}
167167

168-
static inline Coroutine *get_current_safe() {
168+
static Coroutine *get_current_safe() {
169169
if (sw_unlikely(!current)) {
170170
swoole_fatal_error(SW_ERROR_CO_OUT_OF_COROUTINE, "API must be called in the coroutine");
171171
}
172172
return current;
173173
}
174174

175-
static inline void *get_current_task() {
175+
static void *get_current_task() {
176176
return sw_likely(current) ? current->get_task() : nullptr;
177177
}
178178

179-
static inline long get_current_cid() {
179+
static long get_current_cid() {
180180
return sw_likely(current) ? current->get_cid() : -1;
181181
}
182182

183-
static inline Coroutine *get_by_cid(long cid) {
183+
static Coroutine *get_by_cid(long cid) {
184184
auto i = coroutines.find(cid);
185185
return sw_likely(i != coroutines.end()) ? i->second : nullptr;
186186
}
187187

188-
static inline void *get_task_by_cid(long cid) {
188+
static void *get_task_by_cid(long cid) {
189189
Coroutine *co = get_by_cid(cid);
190190
return sw_likely(co) ? co->get_task() : nullptr;
191191
}
192192

193-
static inline size_t get_stack_size() {
193+
static size_t get_stack_size() {
194194
return stack_size;
195195
}
196196

197-
static inline void set_stack_size(size_t size) {
197+
static void set_stack_size(size_t size) {
198198
stack_size = SW_MEM_ALIGNED_SIZE_EX(SW_MAX(MIN_STACK_SIZE, SW_MIN(size, MAX_STACK_SIZE)), STACK_ALIGNED_SIZE);
199199
}
200200

201-
static inline long get_last_cid() {
201+
static long get_last_cid() {
202202
return last_cid;
203203
}
204204

205-
static inline size_t count() {
205+
static long get_socket_bound_cid() {
206+
return socket_bound_cid;
207+
}
208+
209+
static size_t count() {
206210
return coroutines.size();
207211
}
208212

209-
static inline uint64_t get_peak_num() {
213+
static uint64_t get_peak_num() {
210214
return peak_num;
211215
}
212216

213-
static inline long get_elapsed(long cid) {
217+
static long get_elapsed(long cid) {
214218
Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid);
215219
return sw_likely(co) ? Timer::get_absolute_msec() - co->get_init_msec() : -1;
216220
}
217221

218-
static inline long get_execute_time(long cid) {
222+
static long get_execute_time(long cid) {
219223
Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid);
220224
return sw_likely(co) ? co->get_execute_usec() : -1;
221225
}
@@ -224,10 +228,12 @@ class Coroutine {
224228
static void calc_execute_usec(Coroutine *yield_coroutine, Coroutine *resume_coroutine);
225229
#endif
226230
static void print_list();
231+
static void print_socket_bound_error(int sock_fd, const char *event_str, long bound_cid);
227232

228233
protected:
229234
static SW_THREAD_LOCAL Coroutine *current;
230235
static SW_THREAD_LOCAL long last_cid;
236+
static SW_THREAD_LOCAL long socket_bound_cid;
231237
static SW_THREAD_LOCAL uint64_t peak_num;
232238
static SW_THREAD_LOCAL size_t stack_size;
233239
static SW_THREAD_LOCAL SwapCallback on_yield; /* before yield */

‎include/swoole_coroutine_socket.h‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,9 @@ class Socket {
305305
const char *get_event_str(EventType event) const;
306306

307307
void check_bound_co(const EventType event) const {
308-
long cid = get_bound_cid(event);
309-
if (sw_unlikely(cid)) {
310-
swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND,
311-
"Socket#%d has already been bound to another coroutine#%ld, "
312-
"%s of the same socket in coroutine#%ld at the same time is not allowed",
313-
sock_fd,
314-
cid,
315-
get_event_str(event),
316-
Coroutine::get_current_cid());
308+
auto bound_cid = get_bound_cid(event);
309+
if (sw_unlikely(bound_cid)) {
310+
Coroutine::print_socket_bound_error(sock_fd, get_event_str(event), bound_cid);
317311
}
318312
}
319313

‎src/coroutine/base.cc‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace swoole {
2121

2222
SW_THREAD_LOCAL Coroutine *Coroutine::current = nullptr;
2323
SW_THREAD_LOCAL long Coroutine::last_cid = 0;
24+
SW_THREAD_LOCAL long Coroutine::socket_bound_cid = 0;
2425
SW_THREAD_LOCAL std::unordered_map<long, Coroutine *> Coroutine::coroutines;
2526
SW_THREAD_LOCAL uint64_t Coroutine::peak_num = 0;
2627
SW_THREAD_LOCAL bool Coroutine::activated = false;
@@ -221,6 +222,17 @@ void Coroutine::print_list() {
221222
}
222223
}
223224

225+
void Coroutine::print_socket_bound_error(int sock_fd, const char *event_str, long bound_cid) {
226+
socket_bound_cid = bound_cid;
227+
swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND,
228+
"Socket#%d has already been bound to another coroutine#%ld, "
229+
"%s of the same socket in coroutine#%ld at the same time is not allowed",
230+
sock_fd,
231+
socket_bound_cid,
232+
event_str,
233+
get_current_cid());
234+
}
235+
224236
void Coroutine::set_on_yield(const SwapCallback func) {
225237
on_yield = func;
226238
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
swoole_socket_coro: bound error
3+
--SKIPIF--
4+
<?php require __DIR__ . '/../include/skipif.inc'; ?>
5+
--FILE--
6+
<?php
7+
require __DIR__ . '/../include/bootstrap.php';
8+
$port = get_one_free_port();
9+
go(function () use ($port) {
10+
$server = new Co\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
11+
Assert::assert($server->bind('127.0.0.1', $port));
12+
Assert::assert($server->listen());
13+
});
14+
go(function () use ($port) {
15+
$cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
16+
$ret = $cli->connect('127.0.0.1', $port);
17+
Assert::true($ret);
18+
go(function () use ($cli) {
19+
$cli->recv();
20+
});
21+
$cli->recv();
22+
});
23+
Swoole\Event::wait();
24+
?>
25+
--EXPECTF--
26+
Fatal error: Uncaught Swoole\Error: Socket#%d has already been bound to another coroutine#%d, reading of the same socket in coroutine#%d at the same time is not allowed in %s:%d
27+
Stack trace:
28+
#0 %s(%d): Swoole\Coroutine\Client->recv()
29+
#1 [internal function]: {closure:%s:%d}()
30+
#2 {main}
31+
thrown in %s on line %d
32+
33+
[Coroutine-3] Stack trace:
34+
-------------------------------------------------------------------
35+
#0 %s(%d): Swoole\Coroutine\Client->recv()
36+
#1 [internal function]: {closure:{%s:%d}:%d}()

0 commit comments

Comments
 (0)