aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRickard Green <[email protected]>2014-08-07 22:02:25 +0200
committerRickard Green <[email protected]>2014-08-07 23:45:59 +0200
commit18c03b13d1a53282e69160fa71a7195a11e84392 (patch)
treef2d65d851546d4d2af7a65f9dcd33eb0a5cb5933
parent1cbd897c43f0dab1275392a3736e4629c1f80243 (diff)
downloadotp-18c03b13d1a53282e69160fa71a7195a11e84392.tar.gz
otp-18c03b13d1a53282e69160fa71a7195a11e84392.tar.bz2
otp-18c03b13d1a53282e69160fa71a7195a11e84392.zip
Fix emigrate bug in erts_port_task_schedule()
While current run-queue lock is unlocked in the call to erts_check_emigration_need() from erts_port_task_schedule() the port can be migrated to another run-queue by another thread. The code in erts_port_task_schedule() needs to check if this has occurred when returning from erts_check_emigration_need(), and if so respect the migration decision. When this was not done, the thread calling erts_port_task_schedule() held the wrong run-queue lock which caused invalid updates of the port task queue. This bug was automatically fixed by the rewrites in the branch rickard/r16b/port-optimizations-fixes/OTP-10336 (commit 56cef897ca3ad2377e34a6ea5800a54a28cbeb6e) introduced in erts-5.10 and do not effect erts versions after that.
-rw-r--r--erts/emulator/beam/erl_port_task.c20
1 files changed, 17 insertions, 3 deletions
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 3dc7c14faf..11027dda7a 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -576,8 +576,20 @@ erts_port_task_schedule(Eterm id,
if (!pp->sched.taskq && !pp->sched.exe_taskq) {
#ifdef ERTS_SMP
ErtsRunQueue *xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
- if (xrunq) {
- /* Port emigrated ... */
+ ERTS_SMP_LC_ASSERT(runq != xrunq);
+ if (runq != (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue)) {
+ /*
+ * Someone else migrated this port while we had the run-queue
+ * lock on runq unlocked in erts_check_emigration_need(). Respect
+ * that migration decision...
+ */
+ erts_smp_runq_unlock(runq);
+ if (xrunq)
+ erts_smp_runq_unlock(xrunq);
+ runq = erts_port_runq(pp);
+ }
+ else if (xrunq) {
+ /* Emigrate port... */
erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
erts_smp_runq_unlock(runq);
runq = xrunq;
@@ -929,6 +941,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
#ifdef ERTS_SMP
xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL);
+ ERTS_SMP_LC_ASSERT(runq != xrunq);
+ ERTS_SMP_LC_ASSERT(runq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
if (!xrunq) {
#endif
enqueue_port(runq, pp);
@@ -938,7 +952,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
#ifdef ERTS_SMP
}
else {
- /* Port emigrated ... */
+ /* Emigrate port... */
erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
enqueue_port(xrunq, pp);
ASSERT(pp->sched.exe_taskq);