Skip to content

Commit ef9a619

Browse files
committed
php#9: * Fix event callback disposal and improve error handling in TrueAsync API
* Fixes for zend_async_resume_when + Add zend_async_waker_is_event_exists
1 parent f8c81f6 commit ef9a619

File tree

2 files changed

+47
-12
lines changed

2 files changed

+47
-12
lines changed

Zend/zend_async_API.c

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ ZEND_API zend_async_event_callback_t * zend_async_event_callback_new(zend_async_
406406
return event_callback;
407407
}
408408

409-
static void coroutine_event_callback_dispose(zend_async_event_callback_t *callback, zend_async_event_t * event);
409+
void coroutine_event_callback_dispose(zend_async_event_callback_t *callback, zend_async_event_t * event);
410410

411411
ZEND_API zend_coroutine_event_callback_t * zend_async_coroutine_callback_new(
412412
zend_coroutine_t * coroutine, zend_async_event_callback_fn callback, size_t size
@@ -552,14 +552,18 @@ static void coroutine_event_callback_dispose(zend_async_event_callback_t *callba
552552

553553
callback->ref_count = 0;
554554

555-
zend_async_waker_t * waker = ((zend_coroutine_event_callback_t *) callback)->coroutine->waker;
555+
const zend_coroutine_t * coroutine = ((zend_coroutine_event_callback_t *) callback)->coroutine;
556556

557-
if (event != NULL && waker != NULL) {
558-
// remove the event from the waker
559-
zend_hash_index_del(&waker->events, (zend_ulong)event);
557+
if (EXPECTED(coroutine != NULL)) {
558+
zend_async_waker_t * waker = coroutine->waker;
560559

561-
if (waker->triggered_events != NULL) {
562-
zend_hash_index_del(waker->triggered_events, (zend_ulong)event);
560+
if (event != NULL && waker != NULL) {
561+
// remove the event from the waker
562+
zend_hash_index_del(&waker->events, (zend_ulong)event);
563+
564+
if (waker->triggered_events != NULL) {
565+
zend_hash_index_del(waker->triggered_events, (zend_ulong)event);
566+
}
563567
}
564568
}
565569

@@ -582,6 +586,15 @@ ZEND_API void zend_async_waker_add_triggered_event(zend_coroutine_t *coroutine,
582586
}
583587
}
584588

589+
ZEND_API bool zend_async_waker_is_event_exists(zend_coroutine_t *coroutine, zend_async_event_t *event)
590+
{
591+
if (UNEXPECTED(coroutine->waker == NULL)) {
592+
return false;
593+
}
594+
595+
return zend_hash_index_find(&coroutine->waker->events, (zend_ulong)event) != NULL;
596+
}
597+
585598
ZEND_API void zend_async_resume_when(
586599
zend_coroutine_t *coroutine,
587600
zend_async_event_t *event,
@@ -590,6 +603,8 @@ ZEND_API void zend_async_resume_when(
590603
zend_coroutine_event_callback_t *event_callback
591604
)
592605
{
606+
bool locally_allocated_callback = false;
607+
593608
if (UNEXPECTED(ZEND_ASYNC_EVENT_IS_CLOSED(event))) {
594609
zend_throw_error(NULL, "The event cannot be used after it has been terminated");
595610
return;
@@ -610,6 +625,7 @@ ZEND_API void zend_async_resume_when(
610625
event_callback->base.ref_count = 1;
611626
event_callback->base.callback = callback;
612627
event_callback->base.dispose = coroutine_event_callback_dispose;
628+
locally_allocated_callback = true;
613629
}
614630

615631
// Set up the default dispose function if not set
@@ -627,6 +643,10 @@ ZEND_API void zend_async_resume_when(
627643
event->add_callback(event, &event_callback->base);
628644

629645
if (UNEXPECTED(EG(exception) != NULL)) {
646+
if (locally_allocated_callback) {
647+
event_callback->base.dispose(&event_callback->base, event);
648+
}
649+
630650
if (trans_event) {
631651
event->dispose(event);
632652
}
@@ -640,7 +660,18 @@ ZEND_API void zend_async_resume_when(
640660
trigger->callback = &event_callback->base;
641661

642662
if (UNEXPECTED(zend_hash_index_add_ptr(&coroutine->waker->events, (zend_ulong)event, trigger) == NULL)) {
643-
zend_throw_error(NULL, "Failed to add event to the waker");
663+
efree(trigger);
664+
665+
if (locally_allocated_callback) {
666+
event_callback->base.dispose(&event_callback->base, event);
667+
}
668+
669+
if (trans_event) {
670+
event->dispose(event);
671+
}
672+
673+
zend_throw_error(NULL, "Failed to add event to the waker: maybe event already exists");
674+
644675
return;
645676
}
646677
}

Zend/zend_async_API.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#include "zend_fibers.h"
2020
#include "zend_globals.h"
2121

22-
#define ZEND_ASYNC_API "TrueAsync API v0.2.0"
22+
#define ZEND_ASYNC_API "TrueAsync API v0.3.0"
2323
#define ZEND_ASYNC_API_VERSION_MAJOR 0
2424
#define ZEND_ASYNC_API_VERSION_MINOR 2
2525
#define ZEND_ASYNC_API_VERSION_PATCH 0
@@ -386,10 +386,12 @@ struct _zend_async_event_callback_s {
386386
}
387387

388388
#define ZEND_ASYNC_EVENT_CALLBACK_RELEASE(callback) \
389-
if (callback != NULL && callback->ref_count > 1) { \
390-
callback->ref_count--; \
389+
if ((callback) != NULL && (callback)->ref_count > 1) { \
390+
(callback)->ref_count--; \
391+
} else if((callback)->dispose != NULL) { \
392+
(callback)->dispose((callback), NULL); \
391393
} else { \
392-
coroutine_event_callback_dispose(callback, NULL); \
394+
coroutine_event_callback_dispose((callback), NULL); \
393395
}
394396

395397
struct _zend_coroutine_event_callback_s {
@@ -1287,6 +1289,7 @@ ZEND_API zend_async_event_callback_t * zend_async_event_callback_new(zend_async_
12871289
ZEND_API zend_coroutine_event_callback_t * zend_async_coroutine_callback_new(
12881290
zend_coroutine_t * coroutine, zend_async_event_callback_fn callback, size_t size
12891291
);
1292+
ZEND_API void coroutine_event_callback_dispose(zend_async_event_callback_t *callback, zend_async_event_t * event);
12901293

12911294
/* Waker API */
12921295
ZEND_API zend_async_waker_t *zend_async_waker_new(zend_coroutine_t *coroutine);
@@ -1298,6 +1301,7 @@ ZEND_API bool zend_async_waker_apply_error(
12981301
);
12991302
ZEND_API void zend_async_waker_destroy(zend_coroutine_t *coroutine);
13001303
ZEND_API void zend_async_waker_add_triggered_event(zend_coroutine_t *coroutine, zend_async_event_t *event);
1304+
ZEND_API bool zend_async_waker_is_event_exists(zend_coroutine_t *coroutine, zend_async_event_t *event);
13011305

13021306
#define ZEND_ASYNC_WAKER_APPLY_ERROR(waker, error, transfer) zend_async_waker_apply_error((waker), (error), (transfer), true, false)
13031307
#define ZEND_ASYNC_WAKER_APPEND_ERROR(waker, error, transfer) zend_async_waker_apply_error((waker), (error), (transfer), false, false)

0 commit comments

Comments
 (0)