diff options
author | Rickard Green <[email protected]> | 2014-08-07 22:02:25 +0200 |
---|---|---|
committer | Rickard Green <[email protected]> | 2014-08-07 23:45:59 +0200 |
commit | 18c03b13d1a53282e69160fa71a7195a11e84392 (patch) | |
tree | f2d65d851546d4d2af7a65f9dcd33eb0a5cb5933 | |
parent | 1cbd897c43f0dab1275392a3736e4629c1f80243 (diff) | |
download | otp-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.c | 20 |
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); |