From 12e45b843432de4c58e6dc3662d4478a4f0ed014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Grubbstr=C3=B6m=20=28Grubba=29?= <grubba@grubba.org> Date: Mon, 26 Oct 2020 11:52:21 +0100 Subject: [PATCH] Concurrent.Promise: Fixed race-condition in finalise(). The API for finalise() raced with on_{success,failure}(), as new callbacks could get registered after finalise() was called, but before the state was updated. Fixes #10055. --- lib/modules/Concurrent.pmod | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/modules/Concurrent.pmod b/lib/modules/Concurrent.pmod index 6304cbb11b..c5fb0362c1 100644 --- a/lib/modules/Concurrent.pmod +++ b/lib/modules/Concurrent.pmod @@ -232,6 +232,7 @@ class Future //! @[on_failure()], @[query_success_callbacks()] this_program on_success(function(mixed, mixed ... : void) cb, mixed ... extra) { + Thread.MutexKey key = mux->lock(); switch (state) { case STATE_FULFILLED: call_callback(cb, result, @extra); @@ -274,6 +275,7 @@ class Future //! @[on_success()], @[query_failure_callbacks()] this_program on_failure(function(mixed, mixed ... : void) cb, mixed ... extra) { + Thread.MutexKey key = mux->lock(); switch (state) { case STATE_REJECTED: state = STATE_REJECTION_REPORTED; @@ -941,14 +943,19 @@ class Promise return Future::this; } - protected this_program finalise(State newstate, mixed value, int try, - array(array(function(mixed, mixed ...: void)|array(mixed))) cbs) + protected this_program finalise(State newstate, mixed value, int try) { Thread.MutexKey key = mux->lock(); if (state <= STATE_PENDING) { state = newstate; result = value; + array(array(function(mixed, mixed ...: void)|array(mixed))) cbs; + if (state == STATE_FULFILLED) { + cbs = success_cbs; + } else { + cbs = failure_cbs; + } key = 0; cond->broadcast(); if (sizeof(cbs)) @@ -989,7 +996,7 @@ class Promise //! @[try_success()], @[try_failure()], @[failure()], @[on_success()] this_program success(mixed value, void|int try) { - return finalise(STATE_FULFILLED, value, try, success_cbs); + return finalise(STATE_FULFILLED, value, try); } //! Fulfill the @[Future] if it hasn't been fulfilled or failed already. @@ -1027,7 +1034,7 @@ class Promise this_program failure(mixed value, void|int try) { return - finalise(STATE_REJECTED, value, try, failure_cbs); + finalise(STATE_REJECTED, value, try); } //! Maybe reject the @[Future] value. -- GitLab